SAP中AES128加密存储密码后解密失败求助
问题:SAP AES128加密存储后解密失效
我需要在SAP表中存储用户名与密码,要求密码以AES128加密后存入数据库,使用前解密,但解密功能失效。表中的PWD和PWDKEY列定义为RWASTRING类型。
加密存储源代码
data : passwd type string, encoded type string, decoded type string. data: lv_plaintext type xstring. data: lv_plaintext2 type xstring. data: lv_key type xstring. *----------------------------------------------------------------------* * Selection-Screen *----------------------------------------------------------------------* parameters: p_name type char10. parameters: p_usern type char10. parameters: p_pass type text10. parameters: p_descr type text120. *----------------------------------------------------------------------* * At Selection Screen Output *----------------------------------------------------------------------* at selection-screen output. loop at screen. if screen-name = 'P_PASS'. screen-invisible = 1. modify screen. endif. endloop. *----------------------------------------------------------------------* * Start of Selection *----------------------------------------------------------------------* start-of-selection. * create message data: lr_conv_sec type ref to cl_abap_conv_out_ce . data: lr_conv_key type ref to cl_abap_conv_out_ce . try. call method cl_abap_conv_out_ce=>create exporting encoding = 'DEFAULT' * endian = replacement = '#' ignore_cerr = abap_false receiving conv = lr_conv_sec . catch cx_parameter_invalid_range . catch cx_sy_codepage_converter_init . endtry. lr_conv_sec->write( p_pass ). * create key try. call method cl_abap_conv_out_ce=>create exporting encoding = 'DEFAULT' * endian = replacement = '#' ignore_cerr = abap_false receiving conv = lr_conv_key . catch cx_parameter_invalid_range . catch cx_sy_codepage_converter_init . endtry. lr_conv_key->write( p_pass ). try. call method lr_conv_sec->get_buffer receiving buffer = lv_plaintext. endtry. try. call method lr_conv_key->get_buffer receiving buffer = lv_key. endtry. data: lv_message type xstring. * encrypt using AES256 call method cl_sec_sxml_writer=>encrypt exporting plaintext = lv_plaintext key = lv_key algorithm = cl_sec_sxml_writer=>co_aes128_algorithm importing ciphertext = lv_message. data: ls_pwdstore type /vaps/pwdstore. ls_pwdstore-name = p_name. ls_pwdstore-username = p_usern. ls_pwdstore-pwd = lv_message. ls_pwdstore-pwdkey = lv_key. ls_pwdstore-description = p_descr. modify /vaps/pwdstore from ls_pwdstore. write:/ 'Name', ' : ', ls_pwdstore-name. write:/ 'Benutzername', ' : ', ls_pwdstore-username. write:/ 'Passwort', ' : ', ls_pwdstore-pwd. write:/ 'PWDKEY', ' : ', ls_pwdstore-pwdkey.
解密显示源代码
data : passwd type string, encoded type string, decoded type string. data: lv_plaintext type xstring. data: lv_plaintext2 type xstring. data: lv_key type xstring. *----------------------------------------------------------------------* * Selection-Screen *----------------------------------------------------------------------* parameters: p_name type char10. *----------------------------------------------------------------------* * At Selection Screen Output *----------------------------------------------------------------------* at selection-screen output. *----------------------------------------------------------------------* * Start of Selection *----------------------------------------------------------------------* start-of-selection. data: lv_message type xstring. data: ls_pwdstore type /vaps/pwdstore. data: lv_length type i, e_string type string, lt_binary type standard table of x255. select single * from /vaps/pwdstore into ls_pwdstore where name = p_name. lv_key = ls_pwdstore-pwdkey. lv_message = ls_pwdstore-pwd. call method cl_sec_sxml_writer=>decrypt exporting ciphertext = lv_message key = lv_key algorithm = cl_sec_sxml_writer=>co_aes128_algorithm importing plaintext = lv_plaintext2. call function 'SCMS_XSTRING_TO_BINARY' exporting buffer = lv_plaintext2 importing output_length = lv_length tables binary_tab = lt_binary. call function 'SCMS_BINARY_TO_STRING' exporting input_length = lv_length importing text_buffer = e_string tables binary_tab = lt_binary exceptions failed = 1 others = 2. if sy-subrc <> 0. * Implement suitable error handling here endif. write:/ 'Name', ' : ', p_name. write:/ 'PWD', ' : ', e_string.
问题根源分析
- AES128密钥长度不合法:AES128强制要求密钥长度为16字节(128位),当前代码直接将用户输入的密码转成xstring作为密钥,若输入密码长度不是16字节,会导致加密解密时密钥不匹配,或算法执行抛出未捕获的异常。
- RWASTRING存储的二进制损坏:RWASTRING是字符型变长字段,直接存储xstring类型的二进制数据时,会触发隐式的字符编码转换,导致密文或密钥的二进制内容被篡改。
- 编码转换不一致:加密时用
cl_abap_conv_out_ce以DEFAULT编码转换字符串到xstring,解密后却用SCMS_XSTRING_TO_BINARY+SCMS_BINARY_TO_STRING的组合转换回字符串,编码规则不统一易导致乱码或解密失败。 - 缺失异常处理:加密和解密过程中未捕获
cl_sec_sxml_writer抛出的异常,无法定位具体错误原因(如密钥长度错误、密文损坏等)。
修复方案
生成合规的AES128密钥
AES128必须使用16字节密钥,建议通过哈希算法对密钥种子处理后截取固定长度:" 结合固定种子与用户输入生成密钥种子,提升安全性 data(lv_key_seed) = 'SAP_PWD_ENC_SECRET' && p_pass. " SHA256哈希后截取前16字节作为AES128密钥 call method cl_abap_hash=>hash_sha256 exporting data = cl_abap_conv_out_ce=>create( encoding = 'UTF-8' )->write( lv_key_seed )->get_buffer( ) receiving hash = lv_key. lv_key = lv_key(16). " 截取16字节(128位)用BASE64编码存储二进制数据
避免RWASTRING直接存储二进制,先将xstring转BASE64字符串存储:- 加密存储时:
" 密文转BASE64 call method cl_http_utility=>encode_xbase64 exporting data = lv_message receiving r_string = ls_pwdstore-pwd. " 密钥转BASE64 call method cl_http_utility=>encode_xbase64 exporting data = lv_key receiving r_string = ls_pwdstore-pwdkey. - 解密读取时:
" 密文解码BASE64 call method cl_http_utility=>decode_xbase64 exporting data = ls_pwdstore-pwd receiving r_xstring = lv_message. " 密钥解码BASE64 call method cl_http_utility=>decode_xbase64 exporting data = ls_pwdstore-pwdkey receiving r_xstring = lv_key.
- 加密存储时:
统一字符串与xstring的转换逻辑
解密后用与加密时一致的编码转换方式,避免乱码:try. data(lr_conv_in) = cl_abap_conv_in_ce=>create( encoding = 'UTF-8' ). lr_conv_in->write( lv_plaintext2 ). e_string = lr_conv_in->get_buffer( ). catch cx_sy_conversion_codepage into data(lx_conv_err). write:/ '字符串转换错误:', lx_conv_err->get_text( ). endtry.添加完整的异常捕获
在加密和解密过程中捕获异常,快速定位问题:" 解密时添加异常处理 try. call method cl_sec_sxml_writer=>decrypt exporting ciphertext = lv_message key = lv_key algorithm = cl_sec_sxml_writer=>co_aes128_algorithm importing plaintext = lv_plaintext2. catch cx_sec_sxml_encrypt_error into data(lx_err). write:/ '解密失败:', lx_err->get_text( ). endtry.
内容的提问来源于stack exchange,提问作者Eminen
相关产品推荐
相关产品推荐

