Capicom和SHA1 - 帮助将Java代码翻译成Delphi

3

我有一个使用证书对字符串进行签名的Java应用程序。它使用SHA1加密该字符串。我正在尝试将代码转换为Delphi 2010,但我不知道如何使其以与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常量以及哈希类,也许我应该使用这些类,但我不知道如何使用。


1
你在 Delphi 中使用 Base 64 编码,在 Java 中使用 Base 16,我认为这可能会有问题。 - Asad Rasheed
可能是这样,但那是另一个问题。我需要知道如何使用SHA1签署文件。 - Rafael Colucci
4
我的评论并没有恶意,如果你这样理解了,我很抱歉——我是想帮忙的。或许你应该试着更加礼貌——“根本没问你的问题”是很无礼的,因为你是在公共论坛上发布问题,因此向任何愿意花时间帮助你的人提出了问题(顺便说一句,都是免费的)。而且你的回答也没有意义——你告诉我两个常量都有值“0”,因此它们之间没有区别?我觉得这很不可能(但现在我也不会浪费时间去检查)。 - Ken White
@ken 好的,我为此感到抱歉。是的,它们之间没有任何区别。这并不奇怪,它们是用于不同地方的常量。CAPICOM_ENCODE_BINARY = $00000001; CAPICOM_HASH_ALGORITHM_SHA1 = $00000000; CAPICOM_ENCODE_BASE64 = $00000000; - Rafael Colucci
2个回答

3
我们在一些与我们的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;

1
是的,我知道这个库。我不知道的是这行代码的作用:gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);。我猜CAPCOM会自己处理sha1,但我不知道它何时处理。也许我需要有人帮助我理解Java代码。我应该以二进制格式获取证书信息,对其应用sha1,然后将其转换为十六进制吗?这是正确的顺序吗? - Rafael Colucci

0
首先,你怎么知道你没有使用SHA-1?我问这个问题是因为CAPICOM的签名功能只能用于SHA-1签名。
其次,你怎么知道你得到了不同的结果?你尝试过验证答案吗?如果是的话,你用的是什么方法?
第三,有一件事情你必须知道关于CAPICOM: "content"属性是一个widestring。这有各种含义,包括所有内容都将填充到16位。如果你的输入数据大小不同,你会得到不同的结果。
引用:
基于Java代码,我应该以二进制格式获取证书信息,对其应用sha1,然后将其转换为十六进制吗?
不需要。你可以直接获得ICertificate对象(或更可能是ICertificate2)的实例接口,并直接使用它。如果你有证书的B64编码版本,你可以创建一个新的ICertificate实例,然后调用ICertificate.Import方法。证书本身的哈希值仅由签名机构用于签署特定证书。

哈希算法实际上是在数据签名过程中使用的:库读取数据,创建该数据的哈希值(在CAPICOM的情况下使用SHA-1),然后对该哈希值进行数字签名。这种缩减是必要的,因为签署整个数据块将会太慢,并且因为这样,如果您正在使用硬件加密系统,您只需要携带哈希。

这是正确的顺序和Java代码所做的相同吗?

是和不是。Java代码以明确的细节执行所有必要的步骤,这是CAPICOM没有的(实际上也不能有)。但它应该产生兼容的结果。

它还有一个与签名本身无关的额外步骤:我不确定它是什么,因为它似乎创建了一个虚拟证书信息数据并存储了已签名CMS消息的SHA-1哈希值,然后返回结果实例。我想这是Java开发人员找到的一种将哈希值传递回调用者的方法。

我可以在capicom tlb中看到一些SHA1常量以及哈希类,也许我应该使用这些类,但我不知道如何使用。

HashedData类用于(惊喜)哈希数据。它与Signeddata具有相同的限制,即它仅适用于widestrings,因此与其他框架的兼容性最好是不确定的。

最后注意:Windows通过CAPI函数组提供了更全面的加密功能。CAPICOM只是该库的接口,主要用于脚本语言(Web页面上的JavaScript、VB等)。如果没有使用CAPICOM的要求,您应该尝试使用CAPI,因为您很可能会遇到一些无法使用CAPICOM正确完成的任务。在那个阶段,您将不得不重写部分或全部应用程序,使用CAPI(或另一个库)。因此,如果您没有使用CAPICOM的要求,请立即节省时间并跳过它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接