You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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抛出的异常,无法定位具体错误原因(如密钥长度错误、密文损坏等)。

修复方案

  1. 生成合规的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位)
    
  2. 用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.
      
  3. 统一字符串与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.
    
  4. 添加完整的异常捕获
    在加密和解密过程中捕获异常,快速定位问题:

    " 解密时添加异常处理
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 07:45:56