导航:
论坛 -> DELPHI技术
斑竹:liumazi,sephil
作者:
2023/11/6 13:14:43
标题:
加入我的收藏
楼主:
我使用Cnpack里面的sm2签名后,在第三方网站验签不通过,怀疑是utf8编码的问题,下面是我的代码,用过的大佬帮我看一下 MSG2: String = 'biz_con_tent={"out_trade_no":"123456789","seller_name":"银盛支付服务股份有限公司行业发展部",'+ '"total_amount":"2.99","subject":"公众号","is_minipg":"1","appid":"wxf71930fbcc125f64","timeout_express":"96h",'+ '"currency":"CNY","business_code":"01000010","seller_id":"826584848160007","sub_openid":'+ '"oc0KJ5Kcr5lhbKgmkqXPENrGzsdw"}&charset=utf-8&method=ysepay.online.weixin.pay¬ify_url=http://127.0.0.1&partner_id=826584848160007&sign_type=SM×tamp=2023-11-05 05:11&version=3.0'; USER_A: String = 'BCSoftLib@1672'; procedure TestFp256SignExample; var SM2: TCnSM2; PrivateKey: TCnSM2PrivateKey; PublicKey: TCnSM2PublicKey; Sig: TCnSM2Signature; sU:UTF8String; begin SM2 := TCnSM2.Create(); // 注意这里不是标准 SM2 曲线参数,内部不能用预计算的加速算法 PrivateKey := TCnSM2PrivateKey.Create; PublicKey := TCnSM2PublicKey.Create; Sig := TCnSM2Signature.Create; // PublicKey.X.SetHex('0AE4C7798AA0F119471BEE11825BE46202BB79E2A5844495E97C04FF4DF2548A'); // PublicKey.Y.SetHex('7C0240F88F1CD4E16352A73C17B7F16F07353E53A176D684A9FE0C6BB798E857'); // PrivateKey.SetHex('128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263'); PublicKey.X.SetHex('87b15fc6c3f8d4401323fd98a97cfce2871dfa203fcd8da1b7227c4f73c85947'); PublicKey.Y.SetHex('d0c3f23325e78cc4f81414351cf1914c4c4852f9e74b51a7b86d0482d179f280'); PrivateKey.SetHex('3ce4497107cba3a6a5db4adf4b3d6a2ab74fe862100cdfc3cdd303fa2fb368e0'); FormSM2.edtSM2PrivateKey.Text:=PrivateKey.ToHex(); FormSM2.edtSM2PublicKey.Text := PublicKey.ToString; // 里头的随机数 K 要 6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F var cText:=TEncoding.UTF8.GetBytes(MSG2); if CnSM2SignData(USER_A, @cText[0], Length(cText), Sig, PrivateKey, PublicKey, SM2) then begin FormSM2.mmoSignResult.Lines.Add(Sig.R.ToHex); FormSM2.mmoSignResult.Lines.Add(Sig.S.ToHex); ShowMessage('Sig OK: ' + Sig.R.ToHex + ', ' + Sig.S.ToHex); if CnSM2VerifyData(USER_A, @cText[0], Length(cText), Sig, PublicKey, SM2) then ShowMessage('Verify OK.'); end; Sig.Free; PrivateKey.Free; PublicKey.Free; SM2.Free; end; 用的就是Cnpack里面的例子,改的这个函数TestFp256SignExample,原函数用的是Ansistring, 第三方验签的网站用的是这个网址https://const.net.cn/tool/sm2/verify/
----------------------------------------------
-
作者:
2023/11/6 16:02:18
1楼:
那个验签网站的UserID填的貌似是十六进制格式,你传的USER_A是string,先纠正这个差异?
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/6 18:11:26
2楼:
不可以,然后我试着将签名的string换成hexstring也是不可以的
----------------------------------------------
-
作者:
2023/11/7 8:07:05
3楼:
顶
----------------------------------------------
-
作者:
2023/11/7 10:30:50
4楼:
第三方验签网站你去验签时具体贴的啥内容?
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/7 18:03:43
5楼:
第三方验签网站我贴的也是转成了16进制的内容
----------------------------------------------
-
作者:
2023/11/7 18:21:04
6楼:
function strtohex(s:string):string; var I:integer; begin var c:=TEncoding.Default.GetBytes(s); result:=''; for I:=0 to Length(c)-1 do begin Result:=result+inttohex(Ord(c[I])); end; end; procedure TFormSM2.Button1Click(Sender: TObject); var SM2: TCnSM2; PrivateKey: TCnSM2PrivateKey; PublicKey: TCnSM2PublicKey; Sig: TCnSM2Signature; lvUser:string; begin lvUser:=strtohex(USER_A); self.Memo1.Lines.Add('userid='+lvUser); SM2 := TCnSM2.Create(ctSM2); // 注意这里不是标准 SM2 曲线参数,内部不能用预计算的加速算法 PrivateKey := TCnSM2PrivateKey.Create; PublicKey := TCnSM2PublicKey.Create; Sig := TCnSM2Signature.Create; PrivateKey.SetHex('F4A115840CE610EAEBE6682230D072E88AE891CA803EBB75769A9300E8062742'); PublicKey.Assign(SM2.Generator); SM2.MultiplePoint(PrivateKey, PublicKey); Self.Memo1.Lines.Add('PrivateKey='+PrivateKey.ToHex()); Self.Memo1.Lines.Add('PublicKey='+PublicKey.ToHex()); var s:=TEncoding.UTF8.GetBytes('你好,大D'); // s:=TEncoding.Default.GetBytes('你好,大D'); var ctext:=''; for var I :=0 to Length(s)-1 do begin cText:=cText+inttohex(Ord(s[I])); end; Self.Memo1.Lines.Add('Data='+cText); // 里头的随机数 K 要 6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F if CnSM2SignData(lvUser, @cText[1], Length(cText), Sig, PrivateKey, PublicKey, SM2) then begin ShowMessage('Sig OK: ' + Sig.R.ToHex + ', ' + Sig.S.ToHex); Self.Memo1.Lines.Add('----------'); self.Memo1.Lines.Add('R='+sig.R.ToHex()); self.Memo1.Lines.Add('S='+sig.S.ToHex()); if CnSM2VerifyData(USER_A, @s[0], Length(s)-1, Sig, PublicKey, SM2) then ShowMessage('Verify OK.'); end; Sig.Free; PrivateKey.Free; PublicKey.Free; SM2.Free; end;
----------------------------------------------
-
作者:
2023/11/8 16:17:53
7楼:
USER_A: AnsiString = '1234567812345678'; //java bouncycastle 默认
----------------------------------------------
-
作者:
2023/11/20 0:22:37
8楼:
public static final String SM3_SM2 = "sm3WithSM2Encryption"; 这里java里面使用的算法,对应的CNPack里面的sm2应该用哪一个呢 TCnEccCurveType = (ctCustomized, ctSM2, ctSM2Example192, ctSM2Example256, ctRfc4754ECDSAExample256, ctSecp224r1, ctSecp224k1, ctSecp256k1, ctPrime256v1, ctWapiPrime192v1, ctSM9Bn256v1);这是CNVCL里的
----------------------------------------------
-
作者:
2023/11/20 6:34:08
9楼:
楼上,直接使用CnSM2.pas里的封装好了的CnSM2EncryptData函数可以实现标准SM2加密,不需要自己指定特定的椭圆曲线。 function CnSM2EncryptData(PlainData: Pointer; DataLen: Integer; OutStream: TStream; PublicKey: TCnSM2PublicKey; SM2: TCnSM2 = nil; SequenceType: TCnSM2CryptSequenceType = cstC1C3C2; IncludePrefixByte: Boolean = True; const RandHex: string = ''): Boolean; overload; {* 用公钥对数据块进行加密,参考 GM/T0003.4-2012《SM2椭圆曲线公钥密码算法 第4部分:公钥加密算法》中的运算规则,不同于普通 ECC 与 RSA 的对齐规则 SequenceType 用来指明内部拼接采用默认国标的 C1C3C2 还是想当然的 C1C2C3 IncludePrefixByte 用来声明是否包括 C1 前导的 $04 一字节,默认包括 返回加密是否成功,加密结果写入 OutStream 中}
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/20 12:09:34
10楼:
哦,我说错了,其实我说的还是签名,不管我怎么签名,就是和其他的验签不过
----------------------------------------------
-
作者:
2023/11/21 17:06:50
11楼:
目前签名的话,用Cnapck验签是可以通过的,但是我对接的接口,SDK用的是Java的,然后我用里面的验签就不通过了。接口所提供的SDK用的是cfca-sadk包 下面是他签名的代码 public byte[] p1SignMessage(String signAlg, byte[] sourceData, PrivateKey privateKey, Session session) throws PKIException { if (LoggerManager.debugLogger.isDebugEnabled()) { StringBuffer buffer = new StringBuffer(); buffer.append("p1SignMessage>>>>>>Running"); buffer.append("\n signAlg: "); buffer.append(SADKDebugger.dump(signAlg)); buffer.append("\n sourceData: "); buffer.append(SADKDebugger.dump(sourceData)); buffer.append("\n PrivateKey: "); buffer.append(SADKDebugger.dump(privateKey)); buffer.append("\n session: "); buffer.append(SADKDebugger.dump(session)); LoggerManager.debugLogger.debug(buffer.toString()); } boolean isFailed = false; byte[] var9; try { byte[] signData = session.sign(new Mechanism(signAlg), privateKey, sourceData); byte[] binarySignValue = this.SM2RStoASN1(signData, signAlg); byte[] base64SignValue = this.outputSignResult(binarySignValue); if (LoggerManager.debugLogger.isDebugEnabled()) { LoggerManager.debugLogger.debug("p1SignMessage<<<<<<Finished"); } var9 = base64SignValue; } catch (PKIException var16) { isFailed = true; LoggerManager.exceptionLogger.error("p1SignMessage<<<<<<Failure", var16); throw var16; } catch (Throwable var17) { isFailed = true; LoggerManager.exceptionLogger.error("p1SignMessage<<<<<<Failure", var17); throw new PKIException("p1SignMessage Failure", var17); } finally { if (isFailed && LoggerManager.exceptionLogger.isErrorEnabled()) { StringBuffer buffer = new StringBuffer(); buffer.append("p1SignMessage>>>>>>Running"); buffer.append("\n signAlg: "); buffer.append(SADKDebugger.dump(signAlg)); buffer.append("\n sourceData: "); buffer.append(SADKDebugger.dump(sourceData)); buffer.append("\n PrivateKey: "); buffer.append(SADKDebugger.dump(privateKey)); buffer.append("\n session: "); buffer.append(SADKDebugger.dump(session)); LoggerManager.exceptionLogger.error(buffer.toString()); } } return var9; }
----------------------------------------------
-
作者:
2023/11/21 17:15:18
12楼:
另外,他调用这个签名之前 ,指定了算法 String signAlg = Mechanism.SM3_SM2;//SM3WithSM2 // 签名,必须指定签名算法,返回BASE64签名结果 signed = util.p1SignMessage(signAlg, sourceData, priKey, session);
----------------------------------------------
-
作者:
2023/11/21 18:34:23
13楼:
注意这一句Java代码:this.SM2RStoASN1(signData, signAlg); 从字面上看它把签名结果里的R和S俩值转换成了ASN1格式,而你的代码中是拼接的R的Hex和S的Hex,这俩应该是对不上号的。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/21 20:13:27
14楼:
是的,他最终生成的会比我用Cnapck生成的长一点点
----------------------------------------------
-
作者:
2023/11/21 20:54:19
15楼:
可以用CnVcl中签名结果类TCnEccSignature的ToASN1Hex方法将签名结果转换成ASN1格式的十六进制。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/22 0:22:22
16楼:
目前来看,最络转换是楼上所说的方法,必须把R和S合并后再转成ASNI,再base64格式化 但是他这个签名从头到尾没有看到传入userid这个参数,我从他的私钥里看到有Dint值 并且跟踪后在Sign有一段函数 public void sign(byte[] hash, BigInteger userD, SM2Result sm2Ret) { if (sm2Ret == null) { throw new SecurityException("null not allowed for sm2Ret"); } else { try { BigInteger[] signature = generateSignature(hash, userD); sm2Ret.r = signature[0]; sm2Ret.s = signature[1]; } catch (PKIException var5) { throw new SecurityException(var5.getMessage()); } } } 这里面的userD就是私钥里看到有Dint的值,所以怀疑这个Dint就是userid这个参数, 但目前有个问题,我们的参数userid是一个AnString,而java里同的是一个BigInteger 他的值是 27542118920543702403654067052892950960095467836988035615188147333450236651744 我不管是直接 userid:='27542118920543702403654067052892950960095467836988035615188147333450236651744' 然后传入,还是用其他方法,最终都验签不过
----------------------------------------------
-
作者:
2023/11/22 6:24:22
17楼:
正常写代码的话,这个大整数应该就是私钥本身。 椭圆曲线的私钥是一个大随机整数,公钥是根据这个整数算出来的椭圆曲线点坐标。 userid应该都没传,使用默认的ASCII码1234567812345678
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/23 2:15:12
18楼:
@Cnpack,java的这个加库里面,私钥有两种格式的文件,一种是sm2格式,一种是pfx格式。他打开sm2用的是 PKCS12_SM2 P12 = new PKCS12_SM2(sm2FileData); CNVCL里是否没有对这种文件的封装 我按照Java里的代码,自己读取,但是最终乱码了 var Stream := TFileStream.Create('D:\data\test_sm2.sm2', fmOpenRead or fmShareDenyWrite); Stream.Position:=0; Buf:=StreamToBytes(Stream); var ANSBuf:TBytes; Base64Decode(BytesToAnsi(BUF),ANSBuf); var s:AnsiString; s:=BytesToAnsi(ANSBuf);
----------------------------------------------
-
作者:
2023/11/23 9:03:54
19楼:
不确定扩展名是sm2的是啥格式,没听说过。 PFX格式cnvcl目前不支持。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/26 22:38:43
20楼:
我说的sm2应该是java库自己封装的,我在解释的过程过,发现有一中叫ASN1的东西。然后Delphi对于ASN1的资料很少,不知道CNPACK里面有没有对asn1转换的库,例如直接转换成string.
----------------------------------------------
-
作者:
2023/11/26 23:07:34
21楼:
lockbox2 有ASN1的相關東西。
----------------------------------------------
-
作者:
2023/11/27 10:55:52
22楼:
ASN.1是一种对多个类型数据的树形包装格式,cnvcl\Source\Crypto\CnBerUtils.pas提供了对其解析和组装的接口。 相关例子见cnvcl\Example\VCL\BerParse
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/27 12:39:41
23楼:
你这个是ber吧,好像有个der的东西,cnpack没有吧
----------------------------------------------
-
作者:
2023/11/27 15:38:44
24楼:
官方文档:DER is a subset of BER providing for exactly one way to encode an ASN.1 value. DER是BER的子集。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/28 21:53:46
24楼:
还得请教一个问题,SM4DecryptCbcBytes解密之后,得到的数据,如果后面几位是空的,他会填充16进去,我要如何计算从哪个位置开始他是空的呢。如果我用土方法进行循环,得到从哪个位置写的是16,然后判断从这里开始他就是无效的,会不会有问题,例如,有一些有效的数据,他中间有一位就是16呢
----------------------------------------------
-
作者:
2023/11/29 9:25:32
25楼:
CBC这种分块加密模式规定会补齐缺的块,而Padding(对齐)就是为了应对这种问题而发明的,典型的对齐类型有PKCS7等。 CnPemUtils.pas里有这俩函数可以用来处理,加密前先加Padding、解密后去除Padding: procedure BytesAddPKCS7Padding(var Data: TBytes; BlockSize: Integer); {* 给字节数组末尾加上 PKCS7 规定的填充“几个几”的填充数据} procedure BytesRemovePKCS7Padding(var Data: TBytes); {* 去除 PKCS7 规定的字节数组末尾填充“几个几”的填充数据} SM4的BlockSize是16。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/30 0:17:10
26楼:
我看到CNPack里需读取私钥的文件头是 -----BEGIN EC PARAMETERS----- 而我用其他库读取的pfx,他的文件头是 -----BEGIN PRIVATE KEY----- 我将这个私钥保存Pem用CNPack读取,是不可以读取的。 这两者是不是压根就不是同一种东西,也就说只有-----BEGIN EC PARAMETERS-----才是国密sm2的,而如果用的是Pfx,也就是-----BEGIN PRIVATE KEY-----,根本就不是国密sm2
----------------------------------------------
-
作者:
2023/11/30 2:52:07
27楼:
我好像明白了,pfx文件只有文件头是 -----BEGIN EC PARAMETERS-----的才是sm2的证书,如果是-----BEGIN PRIVATE KEY-----的,就是标准的RSA证书。而RSA证书Cnpack也是支持的。只需要用ICS打开pfx,然后保存成Pem就可以了。 另外这几天折腾了sm2的签名,发现节果的base64和Java的base64不同,只是输出不同而己,但是浪费了我很多时间才查清楚。我这里增加了两个函数,群主可以考虑是否加上,以后有使用者和其他语言方便对比,节省精力。 两个函数,一个是输入base64,别一个是反过来,根据base64设置Key TCnSM2Signaturehelper= class helper for TCnEccSignature public function ToAnsiBase64(FixedLen: Integer=0): string; function SetAsn1Base64(const Buf: AnsiString):boolean; end; function TCnSM2Signaturehelper.SetAsn1Base64(const Buf: AnsiString): boolean; begin var B: TBytes; Result:=false; Base64Decode(string(Buf), B); Result:=SetAsn1Hex(BytesToHex(B)); end; function TCnSM2Signaturehelper.ToAnsiBase64(FixedLen: Integer): string; begin var Writer := TCnBerWriter.Create; var Root := Writer.AddContainerNode(CN_BER_TAG_SEQUENCE); var Stream :=TMemoryStream.Create; Result:=''; try AddBigNumberToWriter(Writer,Self.R, Root); AddBigNumberToWriter(Writer,Self.S, Root); Writer.SaveToStream(Stream); Stream.Position:=0; Base64Encode(Stream.Memory,Stream.Size,Result); finally Writer.Free; Stream.Free; end; end;
----------------------------------------------
-
作者:
2023/11/30 9:13:05
28楼:
好,我看看能否集成。 不过如果集成的话,我们就不用helper了,直接在原类上增加方法了。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/
作者:
2023/11/30 10:18:59
29楼:
已更新CnECC.pas,增加这俩方法。 注意是ASN1,不是Ansi。
----------------------------------------------
欢迎使用CnPack IDE Wizards http://www.cnpack.org/