ABAP RSA方式调用银行API
一、RSA简介
RSA是非对称加密的一种。
对称加密算法在加密和解密时使用的是同一个秘钥;
非对称加密算法需要一对密钥来加密解密,这两个密钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。一个加密,一个解密,由双方各保留一个进行操作。这样,在不传输秘钥的情况下,双方可以针对信息进行加解密,避免了直接传输秘钥可能导致的安全问题。
二、RSA加解密与签名验签
一般来说,有两种场景:加密解密和签名验签
加密解密:使用公钥加密,私钥解密
签名验签:使用私钥加密(签名),公钥解密(验签)
例如:B生成一对密钥,向A提供公钥,B保留私钥
加密:防止信息泄露
当A系统传输数据给B系统,担心报文明文传输会导致信息泄露,则可使用公钥对报文进行加密,以防传输过程中信息泄露。B接收加密报文,通过私钥进行解密
签名:防止信息篡改
当B接收后返回消息给A,担心返回消息被修改,A接收到错误消息,则可使用秘钥进行签名,并将签名和返回报文一起发送给A。A通过公钥进行验签,发现签名解析后和返回报文数据一致,证明报文没有被中间拦截篡改
三、代码实现
1、证书加密解密与签名验签
使用秘钥生成PSE证书方式进行加密解密与签名验签
生成一对密钥后,将私钥文件RSA.key上传到SAP服务器,前后需要加标注,否则不能正常使用
通过命令生成PSE文件,此操作需要BASIS协助
/tmp/rsa > openssl req -x509 -sha256 -key rsa.key -out user1.cer -days 3650 -subj '/CN=user1' /tmp/rsa > openssl pkcs12 -export -inkey rsa.key -in user1.cer -out user1.pfx -nodes Enter Export Password: Verifying - Enter Export Password: /tmp/rsa > setenv SECUDIR $PWD /tmp/rsa > sapgenpse import_p12 -x "" -p user1.pse user1.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/user1.pse" was written
证书加密解密代码:
DATA: lv_output TYPE xstring, lv_input_x TYPE xstring, lv_input TYPE string, lt_recipient_list TYPE STANDARD TABLE OF ssfinfo, ls_recipient_list LIKE LINE OF lt_recipient_list, lv_crc TYPE ssfparms-ssfcrc. DATA: lf_output TYPE xstring. DATA: lf_plain_input_x TYPE xstring. DATA: lf_plain_input TYPE string. DATA: msg_file_name TYPE string. DATA: it_recipient_list TYPE STANDARD TABLE OF ssfinfo, wa_recipient_list LIKE LINE OF it_recipient_list. "原文 lf_plain_input = '一段需要处理的报文'. "转为xstring CALLFUNCTION'SCMS_STRING_TO_XSTRING' EXPORTING text =lf_plain_input IMPORTING buffer =lf_plain_input_x. "证书域名 wa_recipient_list-id ='CN=user1'. APPENDwa_recipient_listTOit_recipient_list. "加密函数 CALLFUNCTION'SSFW_KRN_ENVELOPE' EXPORTING str_format ='PKCS1-V1.5' str_pab ='/tmp/rsa/user1.pse'"服务器中证书路径 ostr_input_data =lf_plain_input_x IMPORTING ostr_enveloped_data =lf_output TABLES recipient_list =it_recipient_list EXCEPTIONS OTHERS =1. "展示加密后信息 PERFORMshow_dataUSINGlf_output. SKIP. CLEARlv_input_x. CLEARlt_recipient_list. ls_recipient_list-id ='CN=user1'."一般为证书域名 ls_recipient_list-profile ='/tmp/rsa/user1.pse'. APPENDls_recipient_listTOlt_recipient_list. "解密 CALLFUNCTION'SSFW_KRN_DEVELOPE' EXPORTING str_format ='PKCS1-V1.5' ostr_enveloped_data =lf_output *b_outdec ='' IMPORTING ostr_output_data =lv_input_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. "根据UTF-8格式转为string类型数据 lv_input =cl_bcs_convert=>xstring_to_string(iv_xstr =lv_input_xiv_cp ='4110'). WRITE:/lv_input. FORM show_data USING data_x TYPE xstring. DATA:data_lenTYPEi, i1TYPEiVALUE0, i2TYPEiVALUE32. data_len =xstrlen(data_x). WHILE(i1<data_len). i2 =nmin(val1 =i2 val2 =data_len). WRITE:/data_x+i1(i2). i1 =i1+32. ENDWHILE. ENDFORM.
证书签名验签代码:
DATA: lv_output TYPE xstring, lv_input_x TYPE xstring, lv_input TYPE string, lt_recipient_list TYPE STANDARD TABLE OF ssfinfo, ls_recipient_list LIKE LINE OF lt_recipient_list, lv_crc TYPE ssfparms-ssfcrc. DATA: lf_input_data TYPE xstring, lf_signed_data TYPE xstring, ls_signer TYPE ssfinfo, "ssfinfoext is tested by CL_SMIME lt_signer TYPE TABLE OF ssfinfo, "ssfinfoext is tested by CL_SMIME lf_crc TYPE ssfreturn, lf_signer_name TYPE string, lf_chain_dataTYPExstring, lf_output_dataTYPExstring, lf_inputv_dataTYPExstring, ls_certificateTYPEssfcertlin, lt_certificatesTYPETABLEOFssfcertlin, lf_subject(256)TYPEc. "原文 lv_input ='一段需要处理的报文'. CALLFUNCTION'SCMS_STRING_TO_XSTRING' EXPORTING text =lv_input IMPORTING buffer =lv_input_x. "签名 ls_signer-id ='CN=user1'."一般为证书域名 ls_signer-profile ='/tmp/rsa/user1.pse'. APPENDls_signerTOlt_signer. CALLFUNCTION'SSFW_KRN_SIGN' EXPORTING ssftoolkit ='SAPSECULIB' str_format ='PKCS1-V1.5' b_inc_certs ='' b_detached ='X' b_inenc ='' str_hashalg ='SHA256'"'SHA1' str_chainfmt ='' ostr_input_data =lv_input_x IMPORTING ostr_signed_data =lf_signed_data str_signer_name =lf_signer_name *ostr_signer_keyid = ostr_chain_data =lf_chain_data crc =lf_crc TABLES signer =lt_signer EXCEPTIONS ssf_krn_error =1 ssf_krn_noop =2 ssf_krn_nomemory =3 ssf_krn_opinv =4 ssf_krn_nossflib =5 ssf_krn_signer_list_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. PERFORMshow_dataUSINGlf_signed_data. SKIP. lf_inputv_data =lv_input_x. "验签 CLEARlt_signer. *verifysignature CALLFUNCTION'SSFW_KRN_VERIFY' EXPORTING ssftoolkit ='SAPSECULIB' str_format ='PKCS1-V1.5' b_inc_certs ='' b_inenc ='' b_outdec ='' str_pab ='/tmp/rsa/user1.pse' *str_pab_password = ostr_signed_data =lf_signed_data ostr_input_data =lf_inputv_data str_hashalg ='SHA256'"'SHA1' str_chainfmt ='' *ostr_chain_data ='' str_signer_name =lf_signer_name *ostr_signer_keyid = crl_profile ='' IMPORTING ostr_output_data =lf_output_data crc =lf_crc TABLES signer_result_list =lt_signer certificatelist =lt_certificates EXCEPTIONS ssf_krn_error =1 ssf_krn_noop =2 ssf_krn_nomemory =3 ssf_krn_opinv =4 ssf_krn_nossflib =5 ssf_krn_input_data_error =6 ssf_krn_invalid_par =7 ssf_krn_invalid_parlen =8 ssf_fb_input_parameter_error =9 OTHERS =10. IFsy-subrc<>0. *Implementsuitableerrorhandlinghere WRITE:/sy-subrc. ENDIF. lv_input =cl_bcs_convert=>xstring_to_string(iv_xstr =lf_output_dataiv_cp ='4110'). WRITE:/lv_input. FORM show_data USING data_x TYPE xstring. DATA:data_lenTYPEi ,i1TYPEiVALUE0 ,i2TYPEiVALUE32. data_len =xstrlen(data_x). WHILE(i1<data_len). i2 =nmin(val1 =i2 val2 =data_len). WRITE:/data_x+i1(i2). i1 =i1+32. ENDWHILE. ENDFORM.
2、公钥验签
当对方系统收到数据返回时,需要对返回的签名使用对方系统提供的公钥进行验签,证明对方返回的数据没有被篡改
DATA:lv_input_x TYPE xstring, lv_data TYPE string, lv_xdata TYPE xstring, lv_input TYPE string, lf_signed_data TYPE xstring, lf_signer_name TYPE string, lf_chain_data TYPE xstring, lf_output_data TYPE xstring, ls_signer TYPE ssfinfo, "ssfinfoext is tested by CL_SMIME lt_signer TYPE TABLE OF ssfinfo, "ssfinfoext is tested by CL_SMIME lt_certificates TYPE TABLE OF ssfcertlin, lf_crc TYPE ssfreturn. DATA: lv_base64 TYPE string, lv_pubkey TYPE xstring, lv_pubbase64 TYPE string, lv_str TYPE string.
"签名模板 lv_data = 'json字符串签名模板'. lv_xdata = cl_abap_codepage=>convert_to( source = lv_data codepage = 'UTF-8' ). "签名 lv_input = 'g0r+iYM0FYAHworrKa+XzL/4FO/ci+S0HZr1pJFAf1/Dtkr+pnpFUWM' && 'zf3oHBeJDL3rcCgCqzUOYhpss3MKtOAR/rttdu8a5FQ1G456uYF7D5r'. CALL FUNCTION 'SCMS_BASE64_DECODE_STR' EXPORTING input = lv_input * UNESCAPE = 'X' IMPORTING output = lv_input_x EXCEPTIONS failed = 1 OTHERS = 2. "公钥 lv_pubbase64 = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwFgHD4kzEVPdOj0' && 'YCX+zp5r6R52MY0O7BsTCLT7aHaxsANsvI9ABGx3OaTVlPB59M6GPb'. CALL FUNCTION 'SCMS_BASE64_DECODE_STR' EXPORTING input = lv_pubbase64 * UNESCAPE = 'X' IMPORTING output = lv_pubkey EXCEPTIONS failed = 1 OTHERS = 2. " 模数 lv_str = lv_pubkey. 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_pubkey = cl_bcs_convert=>string_to_xstring( lv_str ). CALL FUNCTION 'SSFW_KRN_VERIFY' EXPORTING ssftoolkit = 'SAPSECULIB' str_format = 'PKCS1-V1.5' b_inc_certs = '' b_inenc = '' b_outdec = '' str_pab = '' * str_pab_password = ostr_signed_data = lv_input_x ostr_input_data = lv_xdata str_hashalg = 'SHA1' str_chainfmt = 'KEYVALUE' ostr_chain_data = lv_pubkey * ostr_signer_keyid = crl_profile = '' IMPORTING ostr_output_data = lf_output_data crc = lf_crc TABLES signer_result_list = lt_signer certificatelist = lt_certificates EXCEPTIONS ssf_krn_error = 1 ssf_krn_noop = 2 ssf_krn_nomemory = 3 ssf_krn_opinv = 4 ssf_krn_nossflib = 5 ssf_krn_input_data_error = 6 ssf_krn_invalid_par = 7 ssf_krn_invalid_parlen = 8 ssf_fb_input_parameter_error = 9 OTHERS = 10. IF sy-subrc <> 0. * Implement suitable error handling here WRITE:/ sy-subrc. ENDIF. WRITE:/ lf_crc. lv_input = cl_bcs_convert=>xstring_to_string( iv_xstr = lf_output_data iv_cp = '4110' ). WRITE:/ lv_input.
四、简述调用API过程
使用HTTP方式调用银行API
"创建HTTP对象 CALL METHOD cl_http_client=>create_by_url EXPORTING url = ls_ghinfo-zurl IMPORTING client = lo_http_client. lo_http_client->propertytype_redirect = lo_http_client->co_disabled . CALL METHOD lo_http_client->request->set_header_field EXPORTING name = '~request_method' value = 'POST'. " CALL METHOD lo_http_client->request->set_header_field EXPORTING name = '~request_protocol' value = 'HTTP/1.1'. CALL METHOD lo_http_client->request->set_header_field EXPORTING name = 'Content-Type' value = 'application/x-www-form-urlencoded; charset=UTF-8'.
根据上述签名之后,得到签名字符串拼接到发送报文中
发送数据并获得返回消息即可
lv_len = strlen( gv_json_send ). CALL METHOD lo_http_client->request->set_cdata EXPORTING data = gv_json_send offset = 0 length = lv_len. CALL METHOD lo_http_client->send EXCEPTIONS http_communication_failure = 1 http_invalid_state = 2. CALL METHOD lo_http_client->receive EXCEPTIONS http_communication_failure = 1 http_invalid_state = 2 http_processing_failed = 3. IF sy-subrc <> 0. DATA:l_sysubrc TYPE sysubrc, l_error_text TYPE string. l_sysubrc = sy-subrc. CALL METHOD lo_http_client->get_last_error IMPORTING code = l_sysubrc message = l_error_text. ENDIF. CALL METHOD lo_http_client->response->get_status IMPORTING code = http_status_code reason = status_text. e_message = '调用状态:' && http_status_code && ',状态文本:' && status_text. CALL METHOD lo_http_client->response->get_cdata RECEIVING data = w_result .
调用时报错
SSL handshake with xxx.com.cn:443 failed: SSSLERR_PEER_CERT_UNTRUSTED (-102) The peer's X.509 Certificate (chain) is untrusted SapSSLSessionStartNB()==SSSLERR_PEER_CERT_UNTRUSTED
这是因为调用HTTPS地址,没有将网页证书导入到SAP系统。可以在浏览器中下载对应地址的证书,导入SAP系统即可
五、致谢
这是一篇站在前人肩膀上完成的博客,各位前辈的资料,给予了本人极大的帮助,尤其感谢CSDN博主??Fireworks_me??对我疑惑的耐心解答,非常感谢!
参考文档:
??https://blogs.sap.com/2021/04/13/rsa-encryption-in-abap/ ?
??https://blog.csdn.net/xiefireworks/article/details/124809161?spm=1001.2014.3001.5501 ??
??https://blog.csdn.net/somehow1002/article/details/117092352 ???
???https://blog.51cto.com/wushank/1915795???