供应链金融项目连接航信、联易融平台使用了RAS进行加解密验签处理,
以下是航信请求流程
加密:
对明文plainText进行加密,使用对方机构提供res公钥rsaPublicKey,下面是加密过程:
1、随机生成一个128-bit AES秘钥 aesKey
2、使用aesKey对plainText进行加密得到密文encryptedText(AES加解密使用:AES/CBC/PKCS5Padding)
3、将生成的aesKey用BASE64进行加密获得base64AesKey
4、使用RSA公钥rsaPublicKey对base64AesKey进行RSA加密后再用BASE64进行加密获得密文encryptedAesKey
5、获得encryptedAesKey和encryptedText
签名:
使用密文encryptedText、时间戳timestamp、appKey、appSecret、uri进行签名,下面是签名过程:
1、将参数进行重组:appSecret+”app_key”+appKey+”param”+encryptedText+”timestamp”+timestamp+”v2.0″+”uri”+uri+appSecret获得signParam
2、使用HmacSHA256算法已appSecret为key对signParam计算得到签名signature
请求:
使用HTTP/HTTPS协议,使用POST方式,请求header添加alg(固定填“HmacSHA256”)、signature(加密时生成的signature)、aesKey(加密时生成的encryptedAesKey)、version(固定填“2.0”)、appKey(appKey)、timestamp(签名时生成的时间戳timestamp),MediaType使用”application/json; charset=utf-8″
验签:
和签名保持一致,参数从http返回的response中获取,其中encryptedText在response的body中,timestamp和aesKey在header中,获取到参数后按照签名中的方式计算得到signature与response返回header里的signature一致为验签成功,不一致为验签失败
解密:
对密文encryptedText进行解密,使用己方RSA私钥rsaPrivateKey和response返回header里的aesKey,下面是解密过程:
1、使用rsaPrivateKey对aesKey进行RSA解密获得base64AesKey
2、对base64AesKey使用BASE64解密,获得明文aesKey
3、使用明文aesKey对密文encryptedText进行AES解密得到明文plainText(AES加解密使用:AES/CBC/PKCS5Padding)
其中参数结果iv_3000,如图
1.创建*.PSE文件证书
这步得找basis,SAP服务器上生成一对我方的公私钥,
大概的操作如下,具体的可找BASIS操作生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/tmp/rsa > openssl req -x509 -sha256 -key icbcrsa.key -out icbc.cer -days 3650 -subj '/CN=icbc' /tmp/rsa > openssl pkcs12 -export -inkey icbcrsa.key -in icbc.cer -out icbc.pf x -nodes Enter Export Password: Verifying - Enter Export Password: /tmp/rsa > setenv SECUDIR $PWD /tmp/rsa > sapgenpse import_p12 -x "" -p icbc.pse icbc.pfx Found key 'INDEX=0,SIG=YES,ENC=YES,MD5-FINGERPRINT=3B9E 77DD E5E4 3371 19FA 2FCF D1CA 512F,KEYID=3D1E2BC7E33500A3D2A098B63FE9962A6B63318C' !!! WARNING: For security reasons it is recommended to use a PIN/passphrase !!! WARNING: which is at least 8 characters long and contains characters in !!! WARNING: upper and lower case, numbers and non-alphanumeric symbols. PSE "/tmp/rsa/icbc.pse" was written |
2.加密及签名
在发送文件时,使用对方公钥进行加密,并签名,逻辑可以开头说明,具体项目上的使用依据项目需求修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
FUNCTION zfm_tr_gyljy_hx_encrypt. *"---------------------------------------------------------------------- *"*"本地接口: *" IMPORTING *" REFERENCE(IV_TEXT) TYPE STRING *" REFERENCE(IV_URI) TYPE STRING *" REFERENCE(IV_3000) TYPE ZTRGYLT3000 *" EXPORTING *" REFERENCE(EV_TEXT) TYPE STRING *" REFERENCE(EV_KEY) TYPE STRING *" REFERENCE(EV_TIMESTAMPL) TYPE STRING *" REFERENCE(EV_SIGNATURE) TYPE STRING *"---------------------------------------------------------------------- DATA: lv_input_x TYPE xstring, lv_input_xs TYPE xstring, lv_hxpub TYPE string, lv_iv_xstr TYPE xstring, lv_iv_str TYPE string, lv_key_xstr TYPE xstring, lv_key_str TYPE string, lv_en_xstr TYPE xstring, lv_en_str TYPE string, * lv_data TYPE string, lv_64key TYPE xstring, lv_str TYPE string, lv_output TYPE xstring, lv_crc TYPE ssfparms-ssfcrc, * lv_enkey64 TYPE string, lv_timestampl TYPE timestampl,"长时间格式的时间戳 lv_str_timestampl TYPE char22, lv_sign TYPE string, lv_signed_data TYPE xstring, lv_signed_datastr TYPE string, lt_recipient_list TYPE STANDARD TABLE OF ssfinfo, ls_recipient_list LIKE LINE OF lt_recipient_list, lv_len TYPE i, lv_shift TYPE string. DATA: lo_digest TYPE REF TO cx_abap_message_digest, lv_error_text TYPE string. DATA lv_appsecret TYPE string. lv_input_x = cl_bcs_convert=>string_to_xstring( iv_text )."把需加密字符串转换为十六进制 。 *lv_hxpub = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAis2Bsy0Mc+yK5yXA4DwDiOwMu7fbt9tKxOcoyt/QP910H363Agm4+' && * '+I4txvohAxzl0KJk6OVRHUkfQu4Mvcq8hxcbJ8xEJwObEzpL6BCpmeb4QAsmnbzqAPqbA6lZN3c+ICqP0ynLbnuzaFWVy/Gi3' && * 'TRcXs1ZmQHcAmzf/nwCmorQ8bcUIgTkmMMNb3QNeBxuJKNTeeiJSm8NlXV6icAOVcXzqDPlLUrD249eq84hNCHghLezFt9vet' && * 'CNNrOuIYDwu87XW/H+picSMYiMeohWCuUPR4sqmiMdGNEkTFwqtMLZNKhZcJDEpn5NZw5R1bhFpNfpjO2K855rz3B6I0mZwIDAQAB'. lv_hxpub = iv_3000-pubkey."对方公钥字符串 CONDENSE lv_hxpub NO-GAPS. " 偏移量 cbc模式使用 *lv_iv_str = '1234567890123456'. lv_iv_str = iv_3000-iv."加密偏移量:1234567890123456 lv_iv_xstr = cl_bcs_convert=>string_to_xstring( iv_string = lv_iv_str iv_codepage = '4110' )."密偏移量转换为16进行 lv_key_xstr = cl_sec_sxml_writer=>generate_key( algorithm = cl_sec_sxml_writer=>co_aes128_algorithm )."随机生成一个128-bit AES秘钥 aesKey cl_sec_sxml_writer=>pkcs7_padding( EXPORTING blocksize = 16 CHANGING data = lv_input_x"十六进制字符串(JSON) )." TRY. cl_sec_sxml_writer=>encrypt_iv( EXPORTING plaintext = lv_input_x"需加密的十六进制字符串(JSON) key = lv_key_xstr"AES秘钥 iv = lv_iv_xstr"偏移量 algorithm = cl_sec_sxml_writer=>co_aes128_algorithm_pem IMPORTING ciphertext = lv_en_xstr )."AES加解 CATCH cx_root. ENDTRY. * 移除偏移量 lv_en_str = lv_en_xstr. IF strlen( lv_en_str ) > 32. lv_len = strlen( lv_en_str ). lv_len = lv_len - 32. lv_shift = lv_en_str. lv_en_str = lv_shift+0(lv_len). lv_en_xstr = lv_en_str. ENDIF. CALL FUNCTION 'SCMS_BASE64_ENCODE_STR' EXPORTING input = lv_key_xstr"AES秘钥 IMPORTING output = lv_key_str."AES秘钥 lv_key_xstr = cl_bcs_convert=>string_to_xstring( lv_key_str ). CALL FUNCTION 'SCMS_BASE64_ENCODE_STR' EXPORTING input = lv_en_xstr"AES加解文本 IMPORTING output = ev_text."AES加解文本 CALL FUNCTION 'SCMS_BASE64_DECODE_STR' EXPORTING input = lv_hxpub"对方公钥 * UNESCAPE = 'X' IMPORTING output = lv_64key""对方公钥BASE64 EXCEPTIONS failed = 1 OTHERS = 2. * 模数 参考加密概述 lv_str = lv_64key. DATA(length) = strlen( lv_str ). IF length > 514." 2048位密钥 DATA(pos) = length - 10 - 514. lv_str = lv_str+pos(514). ELSEIF length > 256." 1024位密钥 pos = length - 10 - 256. lv_str = lv_str+pos(256). ENDIF. " 函数使用密钥格式 lv_str = 'R:m=' && lv_str && ':e=010001:'. lv_64key = cl_bcs_convert=>string_to_xstring( lv_str ). ls_recipient_list-id = '<implicit>'." *ls_recipient_list-password = '123'." APPEND ls_recipient_list TO lt_recipient_list. CALL FUNCTION 'SSFW_KRN_ENVELOPE'"RSA加密 EXPORTING str_format = 'PKCS1-V1.5' str_pab = '<no_certificate_check>' str_chainfmt = 'KEYVALUE' ostr_chain_data = lv_64key"需加密文本BASE64 ostr_input_data = lv_key_xstr"AES秘钥 IMPORTING ostr_enveloped_data = lv_output crc = lv_crc TABLES recipient_list = lt_recipient_list EXCEPTIONS OTHERS = 1. CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'"BASE64 EXPORTING input = lv_output IMPORTING output = ev_key. GET TIME STAMP FIELD lv_timestampl. lv_str_timestampl = lv_timestampl. cl_pco_utility=>convert_abap_timestamp_to_java( EXPORTING iv_date = CONV #( lv_str_timestampl(8) ) iv_time = CONV #( lv_str_timestampl+8(6) ) iv_msec = CONV #( lv_str_timestampl+14(3) ) IMPORTING ev_timestamp = ev_timestampl ). *CONCATENATE '0437rbto3yzhvjpcirm03kc8japp_keyzf6fivlu7sd6zu5aigqmparam' ev_text 'timestamp' ev_timestampl * 'v2.0uri' iv_uri '0437rbto3yzhvjpcirm03kc8j' INTO lv_sign. CONCATENATE iv_3000-appsecret 'app_key' iv_3000-appkey 'param' ev_text 'timestamp' ev_timestampl 'v' iv_3000-version 'uri' iv_uri iv_3000-appsecret INTO lv_sign. lv_input_xs = cl_bcs_convert=>string_to_xstring( lv_sign ). lv_appsecret = iv_3000-appsecret. "签名 TRY. *create the signature with the key and the request string cl_abap_hmac=>calculate_hmac_for_char( EXPORTING if_algorithm = 'SHA256' "Hash Algorithm if_key = cl_abap_hmac=>string_to_xstring( lv_appsecret ) "HMAC Key if_data = lv_sign IMPORTING ef_hmacxstring = lv_signed_data ). CATCH cx_abap_message_digest. ENDTRY. IF lv_signed_data IS NOT INITIAL. lv_signed_datastr = lv_signed_data. TRANSLATE lv_signed_datastr TO LOWER CASE. ev_signature = lv_signed_datastr. ENDIF. ENDFUNCTION. |
3.解密及验签,
在收到对方发来报文时,需要使用自己的私钥解密对方报文,并做验签名处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
FUNCTION zfm_tr_gyljy_hx_decrypt. *"---------------------------------------------------------------------- *"*"本地接口: *" IMPORTING *" REFERENCE(IV_TEXT) TYPE STRING *" REFERENCE(IV_KEY) TYPE STRING *" REFERENCE(IV_3000) TYPE ZTRGYLT3000 *" EXPORTING *" REFERENCE(EV_CODE) TYPE STRING *" REFERENCE(EV_MSG) TYPE STRING *" REFERENCE(EV_TEXT) TYPE STRING *"---------------------------------------------------------------------- DATA: lv_output TYPE xstring, lv_output_x TYPE xstring, lv_enkey TYPE xstring, lv_crc TYPE ssfparms-ssfcrc, lv_outputstr TYPE string, lv_outputen64 TYPE xstring, lv_de_xstr TYPE xstring, lv_datain TYPE string, lt_recipient_list TYPE STANDARD TABLE OF ssfinfo, ls_recipient_list LIKE LINE OF lt_recipient_list, lv_path TYPE ssfparms-pab. DATA: BEGIN OF ls_returnkl, code TYPE string, success TYPE string, data TYPE string, msg TYPE string, traceid TYPE string, bizcode TYPE string, bizmsg TYPE string, END OF ls_returnkl. DATA lt_returnkl LIKE TABLE OF ls_returnkl. CALL FUNCTION 'SCMS_BASE64_DECODE_STR' EXPORTING input = iv_text * UNESCAPE = 'X' IMPORTING output = lv_output EXCEPTIONS failed = 1 OTHERS = 2. CALL FUNCTION 'SCMS_BASE64_DECODE_STR' EXPORTING input = iv_key * UNESCAPE = 'X' IMPORTING output = lv_enkey EXCEPTIONS failed = 1 OTHERS = 2. " 解密 * ls_recipient_list-id = 'CN=XYL'." 一般为证书域名 ls_recipient_list-id = iv_3000-pseid." 一般为证书域名 CALL 'C_SAPGPARAM' ID 'NAME' FIELD 'SECUDIR' ID 'VALUE' FIELD lv_path."#EC CI_CCALL * CONCATENATE lv_path '/SAPSSLXYL.pse' INTO lv_path. CONCATENATE lv_path iv_3000-psefilename INTO lv_path. ls_recipient_list-profile = lv_path. APPEND ls_recipient_list TO lt_recipient_list. CALL FUNCTION 'SSFW_KRN_DEVELOPE' EXPORTING str_format = 'PKCS1-V1.5' * * str_pab = '<no_certificate_check>' * * str_chainfmt = 'KEYVALUE' ostr_enveloped_data = lv_enkey * b_outdec = 'X' * OSTR_CRYPTO_PARAM_IN = lv_input_xa IMPORTING ostr_output_data = lv_output_x crc = lv_crc TABLES recipient = lt_recipient_list EXCEPTIONS ssf_krn_error = 1 ssf_krn_noop = 2 ssf_krn_nomemory = 3 ssf_krn_opinv = 4 ssf_krn_nossflib = 5 ssf_krn_recipient_error = 6 ssf_krn_input_data_error = 7 ssf_krn_invalid_par = 8 ssf_krn_invalid_parlen = 9 ssf_fb_input_parameter_error = 10 OTHERS = 11. IF lv_crc = 0. lv_outputstr = cl_bcs_convert=>xstring_to_string( iv_xstr = lv_output_x iv_cp = '4110' ). CALL FUNCTION 'SCMS_BASE64_DECODE_STR' EXPORTING input = lv_outputstr * UNESCAPE = 'X' IMPORTING output = lv_outputen64 EXCEPTIONS failed = 1 OTHERS = 2. TRY. cl_sec_sxml_writer=>decrypt( EXPORTING ciphertext = lv_output key = lv_outputen64 algorithm = cl_sec_sxml_writer=>co_aes128_algorithm_pem IMPORTING plaintext = lv_de_xstr ). CATCH cx_root. ENDTRY. IF lv_de_xstr IS NOT INITIAL. lv_datain = cl_bcs_convert=>xstring_to_string( iv_xstr = lv_de_xstr iv_cp = '4110' ). ev_text = lv_datain. /ui2/cl_json=>deserialize( EXPORTING json = lv_datain CHANGING data = ls_returnkl ). ev_code = ls_returnkl-code. ev_msg = ls_returnkl-msg. ENDIF. ENDIF. ENDFUNCTION. |