我有一个java应用程序,它使用证书对字符串进行签名。它使用SHA1对字符串进行加密。我正试图将代码翻译成Delphi2010,但我不知道如何让它像java应用程序一样工作(使用sha1)。到目前为止,我发现了这个:
Delphi 7访问Windows X509证书存储
它确实有效,但它不使用sha1,当我运行java应用程序时,我会得到不同的结果。
Java代码
char[] pass = (char[]) null;
PrivateKey key = (PrivateKey) getKeyStore().getKey(alias, pass);
Certificate[] chain = getKeyStore().getCertificateChain(alias);
CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC");
X509Certificate cert = (X509Certificate) chain[0];
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);
gen.addCertificatesAndCRLs(certsAndCRLs);
CMSProcessable data = new CMSProcessableByteArray(conteudoParaAssinar);
CMSSignedData signed = gen.generate(data, true, "SunMSCAPI");
byte[] envHex = signed.getEncoded();
CertInfo certInfo = new CertInfo();
certInfo.Hash = new BigInteger(envHex).toString(16);
return certInfo;
Delphi代码
var
lSigner: TSigner;
lSignedData: TSignedData;
fs: TFileStream;
qt: integer;
ch: PChar;
msg : WideString;
content : string;
cert: TCertificate;
begin
cert := Self.GetCert;
content := 'test';
lSigner := TSigner.Create(self);
lSigner.Certificate := cert.DefaultInterface;
lSignedData := TSignedData.Create(self);
lSignedData.content := content;
msg := lSignedData.Sign(lSigner.DefaultInterface, false, CAPICOM_ENCODE_BASE64);
lSignedData.Free;
lSigner.Free;
编辑
基于java代码,我应该获得二进制格式的证书信息,在上面应用sha1,然后他们将其转换为十六进制吗?这是正确的顺序,也是java代码所做的事情吗?我可以在capicom tlb中看到一些SHA1常量以及一个哈希类,也许我应该使用这些类,但我不知道如何使用。
我们在一些与Java Tomcat应用程序接口的delphi应用程序中使用DCPCrypt,并能够获得与SHA-256兼容的哈希。我怀疑SHA1也很容易。
这里有一个的例子
function Sha256FileStreamHash(fs : TFileStream): String;
var
Hash: TDCP_sha256;
Digest: array[0..31] of byte; // RipeMD-160 produces a 160bit digest (20bytes)
i: integer;
s: string;
begin
if fs <> nil then
begin
fs.Seek(0, soFromBeginning);
Hash:= TDCP_sha256.Create(nil); // create the hash
try
Hash.Init; // initialize it
Hash.UpdateStream(fs,fs.Size); // hash the stream contents
Hash.Final(Digest); // produce the digest
s:= '';
for i:= 0 to 31 do
s:= s + IntToHex(Digest[i],2);
Result:= s; // display the digest
finally
Hash.Free;
end;
end;
end;
首先,是什么让你认为你没有使用SHA-1?我这么问是因为CAPICOM的签名功能只适用于SHA-1签名。
第二,你怎么知道你得到了不同的结果?你试过验证答案了吗?如果是,使用什么?
第三,关于CAPICOM,您必须了解一些内容:"content"属性是一个宽字符串。这有各种含义,包括所有内容都将被填充到16位。如果你的输入数据大小不同,你会得到不同的结果。
基于java代码,我应该获得二进制格式的证书信息,在上面应用sha1,然后他们将其转换为十六进制吗?
没有。您可以获得一个到ICertificate对象实例(或者更可能是ICertificate2)的接口,然后直接使用它。如果您有B64编码版本的证书,您可以创建一个新的ICertificate实例,然后调用ICertificte。导入方法。证书本身的哈希仅由签名机构用于对特定证书进行签名。
哈希算法实际上在数据签名过程中使用:库读取数据,创建该数据的哈希(在CAPICOM的情况下使用SHA-1),然后对该哈希值进行数字签名。这种减少是必要的,因为对整个数据块进行签名太慢了,而且这样,如果你使用硬件加密系统,你只需要携带哈希。
这是正确的顺序,也是java代码所做的事情吗?
是和否。Java代码以明确的细节完成了所有必要的步骤,这是CAPICOM所没有的(实际上也不能)。不过,它应该会产生兼容的结果。
它还有一个与签名本身无关的附加步骤:我不确定它做了什么,因为它似乎创建了一个伪证书信息数据,存储了签名CMS消息的SHA-1哈希值,并返回结果实例。我想这是Java开发人员找到的将散列值传递回调用方的一种方式。
我可以在capicom tlb中看到一些SHA1常量以及一个哈希类,也许我应该使用这些类,但我不知道如何使用。
HashedData类用于(意外)散列数据。它与Signeddata有着相同的局限性,即它只适用于宽字符串,因此与其他框架的兼容性充其量是不可靠的。
最后一点:Windows通过CAPI函数组提供了对更全面的加密函数的访问。CAPICOM只是该库的一个接口,主要用于脚本语言(网页上的JavaScript、VB等)。你应该帮自己一个忙,试着用它代替CAPICOM,因为你很有可能会遇到一些你根本无法正确使用CAPICOM的事情。在那个阶段,您将不得不使用CAPI(或另一个库)重写所有应用程序的一部分。因此,现在节省时间,如果您没有使用CAPICOM的要求,请跳过它。