如何使用Windows Crypto API为XMLDSIG创建rsa-sha1签名
假设一个签名为信息的XMLDSig文档,如下所示:如何使用Windows Crypto API为XMLDSIG创建rsa-sha1签名,xml,cryptoapi,xml-signature,Xml,Cryptoapi,Xml Signature,假设一个签名为信息的XMLDSig文档,如下所示: <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod> <SignatureMethod Algorithm="http:
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#object">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>OPnpF/ZNLDxJ/I+1F3iHhlmSwgo=</DigestValue>
</Reference>
</SignedInfo>
要在Windows CryptoAPI中生成SignatureValue,我运行以下命令:
var hProv: HCRYPTPROV;
hHash: HCRYPTHASH;
hKey: HCRYPTKEY;
s: string;
B, bSign: TBytes;
bPrefix, bPubKeyModulus, bExp: TBytes;
iFFLen, iPos, iSize, iHashSize: Cardinal;
PubKey: TRsaPubKey;
begin
s := '<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">' + #$0A +
' <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>' + #$0A +
' <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>' + #$0A +
' <Reference URI="#object">' + #$0A +
' <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>' + #$0A +
' <DigestValue>OPnpF/ZNLDxJ/I+1F3iHhlmSwgo=</DigestValue>' + #$0A +
' </Reference>' + #$0A +
'</SignedInfo>';
B := TEncoding.ANSI.GetBytes(s);
{get context for crypt default provider}
Win32Check(CryptAcquireContext(hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT));
try
Win32Check(CryptGenKey(hProv, AT_SIGNATURE, RSA1024BIT_KEY or CRYPT_EXPORTABLE or ALG_SID_RSA_PKCS, hKey));
{create hash-object (SHA algorithm)}
Win32Check(CryptCreateHash(hProv, CALG_SHA1, 0, 0, hHash));
Win32Check(CryptHashData(hHash, @B[0], Length(B), 0));
// Obtain hash size
iSize := SizeOf(iSize);
SetLength(B, iSize);
Win32Check(CryptGetHashParam(hHash, HP_HASHSIZE, @B[0], iSize, 0));
Move(B[0], iSize, iSize);
// obtain hash value
SetLength(B, iSize);
Win32Check(CryptGetHashParam(hHash, HP_HASHVAL, @B[0], iSize, 0));
Print('Hex: ' + ToHex(B));
Print('Base64: ' + ToBase64(B));
// Signature of Hash
Win32Check(CryptSignHash(hHash, AT_SIGNATURE, nil, 0, nil, iSize));
SetLength(bSign, iSize);
Win32Check(CryptSignHash(hHash, AT_SIGNATURE, nil, 0, @bSign[0], iSize));
Print;
Print('Signature: ');
Print(ToBase64(Reverse(bSign)));
// Public key
Win32Check(CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, nil, iSize));
SetLength(B, iSize);
Win32Check(CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, @B[0], iSize));
Print;
Print('Public Key: ');
Print(ToHexASCII(B));
Move(B[SizeOf(TPublicKeyStruc)], PubKey, SizeOf(PubKey));
// Public key: Get Modulus
SetLength(bPubKeyModulus, PubKey.bitlen div 8);
Move(B[SizeOf(TPublicKeyStruc) + SizeOf(TRsaPubKey)], bPubKeyModulus[0], Length(bPubKeyModulus));
Print;
Print('Modulus: ');
Print(ToHex(bPubKeyModulus));
Print;
Print(ToBase64(bPubKeyModulus));
// Public key: Get Exponent
Print;
Print('Exponent: ');
Print(IntToStr(PubKey.pubexp));
SetLength(bExp, SizeOf(PubKey.pubexp));
Move(PubKey.pubexp, bExp[0], Length(bExp));
while bExp[Length(bExp) - 1] = 0 do
SetLength(bExp, Length(bExp) - 1);
Print('Base64: ' + ToBase64(bExp));
Print;
if CryptVerifySignature(hHash, @bSign[0], Length(bSign), hKey, nil, 0) then
Print('signature verified')
else
RaiseLastOSError;
Win32Check(CryptDestroyKey(hKey));
{destroy hash-object}
Win32Check(CryptDestroyHash(hHash));
finally
{release the context for crypt default provider}
Win32Check(CryptReleaseContext(hProv, 0));
end;
end;
var-hProv:HCRYPTPROV;
hHash:HCRYPTHASH;
hKey:HCRYPTKEY;
s:字符串;
B、 B符号:t字节;
bPrefix,bpubkey模数,bExp:TBytes;
iFFLen、IPO、iSize、iHashSize:红衣主教;
PubKey:TRsaPubKey;
开始
s:=''+#$0A+
''+#$0A+
''+#$0A+
''+#$0A+
''+#$0A+
“OPnpF/ZNLDxJ/I+1F3iHhlmSwgo=”+#$0A+
''+#$0A+
'';
B:=TEncoding.ANSI.GetBytes;
{获取crypt默认提供程序的上下文}
Win32Check(CryptAcquireContext(hProv、nil、nil、PROV_RSA_FULL、CRYPT_VERIFYCONTEXT));
尝试
Win32Check(CryptGenKey(hProv、AT_签名、RSA1024BIT_密钥或CRYPT_可导出或ALG_SID_RSA_PKCS,hKey));
{创建哈希对象(SHA算法)}
Win32Check(CryptCreateHash(hProv,CALG_SHA1,0,0,hHash));
Win32Check(CryptHashData(hHash,@B[0],长度(B),0));
//获取散列大小
iSize:=SizeOf(iSize);
设定长度(B,iSize);
Win32Check(CryptGetHashParam(hHash,HP_HASHSIZE,@B[0],iSize,0));
移动(B[0],iSize,iSize);
//获取散列值
设定长度(B,iSize);
Win32Check(CryptGetHashParam(hHash,HP_HASHVAL,@B[0],iSize,0));
打印('Hex:'+ToHex(B));
打印('Base64:'+ToBase64(B));
//散列签名
Win32Check(CryptSignHash(hHash,AT_签名,nil,0,nil,iSize));
设置长度(bSign,iSize);
Win32Check(CryptSignHash(hHash,AT_签名,nil,0,@bSign[0],iSize));
印刷品;
打印(签名:);
印刷品(ToBase64(反面(bSign));
//公钥
Win32Check(CryptExportKey(hKey,0,PUBLICKEYBLOB,0,nil,iSize));
设定长度(B,iSize);
Win32Check(CryptExportKey(hKey,0,PUBLICKEYBLOB,0,@B[0],iSize));
印刷品;
打印('公钥:');
印刷品(ToHexASCII(B));
Move(B[SizeOf(TPublicKeyStruc)],PubKey,SizeOf(PubKey));
//公钥:获取模数
设置长度(bpubkey模数,PubKey.bitlen第8部分);
Move(B[SizeOf(TPublicKeyStruc)+SizeOf(TRsaPubKey)],bpubkeymodular[0],Length(bpubkeymodular));
印刷品;
打印(‘模数:’);
打印(ToHex(bpubkeymodules));
印刷品;
打印(ToBase64(BPUBKEYMODULES));
//公钥:获取指数
印刷品;
打印('指数:');
打印(IntToStr(PubKey.xp));
SetLength(bExp,SizeOf(PubKey.xp));
移动(PubKey.xp,bExp[0],长度(bExp));
而bExp[Length(bExp)-1]=0 do
设置长度(bExp,长度(bExp)-1);
打印('Base64:'+ToBase64(bExp));
印刷品;
如果CryptVerifySignature(hHash,@bSign[0],Length(bSign),hKey,nil,0),那么
打印('签名已验证')
其他的
赖斯·塞罗;
Win32Check(加密密钥(hKey));
{销毁哈希对象}
Win32Check(加密破坏散列(hHash));
最后
{释放crypt默认提供程序的上下文}
Win32Check(CryptReleaseContext(hProv,0));
结束;
结束;
我能够生成一个签名并使用CryptoAPI验证它,我不确定我是否按照XMLDSig规范执行
为了进一步验证我的操作是否正确,我尝试在线验证程序:
我替换以下XMLDSig文档的模数和签名值,并使用在线验证器进行验证:
<?xml version="1.0" encoding="UTF-8"?>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#object">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>OPnpF/ZNLDxJ/I+1F3iHhlmSwgo=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>nihUFQg4mDhLgecvhIcKb9Gz8VRTOlw+adiZOBBXgK4JodEe5aFfCqm8WcRIT8GLLXSk8PsUP4//SsKqUBQkpotcAqQAhtz2v9kCWdoUDnAOtFZkd/CnsZ1sge0ndha40wWDV+nOWyJxkYgicvB8POYtSmldLLepPGMz+J7/Uws=</SignatureValue>
<KeyInfo>
<KeyValue>
<RSAKeyValue><Modulus>4IlzOY3Y9fXoh3Y5f06wBbtTg94Pt6vcfcd1KQ0FLm0S36aGJtTSb6pYKfyX7PqCUQ8wgL6xUJ5GRPEsu9gyz8ZobwfZsGCsvu40CWoT9fcFBZPfXro1Vtlh/xl/yYHm+Gzqh0Bw76xtLHSfLfpVOrmZdwKmSFKMTvNXOFd0V18=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
</KeyValue>
</KeyInfo>
<Object xmlns="http://www.w3.org/2000/09/xmldsig#" Id="object">some text
with spaces and CR-LF.</Object>
</Signature>
OPnpF/ZNLDxJ/I+1F3iHhlmSwgo=
nihUFQg4mDhLgecvhIcKb9Gz8VRTOlw+ADIZOBXGK4JODEE5AFFCQM8WCRIT8GLLXSK8PSUP4//SSKQUBQKPOTCAQAQHTZ2V9KCWDOUDNAOTFZKD/CNSZ1SGEE0NDHA40WWDV+现在的JXKYGICVB8POYTSMLEPGMZ+J7/Uws=
4 ILZOY3Y9FXOH3Y5F06WBBTTG94PT6VCFCD1KQ0FLM0S36AGJTSB6PYKFYX7PQCUQ8WGL6XUJ5GRPESU9GYZ8ZOBWFZSGCSvU40CWOT9FCFBZPFXRO1VTLH/xl/yYHm+GZQH0BW76XTLHSFLFPVORMZDWKMSFKMTVNxOf0V18=AQAB
一些文本
使用空格和CR-LF。
显然它失败了
我认为我没有正确生成签名值。如果您确实需要用本机代码而不是.NET验证XMLDSig文档,我建议您使用。请参阅验证签名和创建签名。我终于成功了。最后是endian问题。Microsoft CryptoAPI以小端位字符串表示模和签名值。而XMLDSIG规范以大端位字符串形式出现 从CryptoAPI函数中获取模数和签名值后,只需通过颠倒字节顺序将其转换为Big-Endian位字符串:
function Reverse(A: TBytes): TBytes;
var B: Byte;
i: integer;
begin
SetLength(Result, Length(A));
i := Length(A) - 1;
for B in A do begin
Result[i] := B;
Dec(i);
end;
end;
Print(ToBase64(reverse(bSign)));
Print(ToBase64(reverse(bPubKeyModulus)));
您好,您能在这里提供带有winapi头的完整源代码(delphi)吗?我错过了一些函数的声明:(谢谢!!
function Reverse(A: TBytes): TBytes;
var B: Byte;
i: integer;
begin
SetLength(Result, Length(A));
i := Length(A) - 1;
for B in A do begin
Result[i] := B;
Dec(i);
end;
end;
Print(ToBase64(reverse(bSign)));
Print(ToBase64(reverse(bPubKeyModulus)));