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;

德尔福代码

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 常量以及一个哈希类,也许我应该使用这些类,但我不知道如何使用。

4

2 回答 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;
于 2011-06-22T00:51:47.213 回答
0

首先,是什么让您认为您没有使用 SHA-1 ?我问是因为 CAPICOM 的签名功能仅适用于 SHA-1 签名。

其次,你怎么知道你得到了不同的结果?您是否尝试验证答案?如果是,使用什么?

第三,关于 CAPICOM,您必须了解一些事情:“content”属性是一个宽字符串。这有各种含义,包括所有内容都将被填充到 16 位的事实。如果您的输入数据大小不同,您将得到不同的结果。

基于java代码,我应该以二进制格式获取证书信息,对其应用sha1并将其转换为十六进制吗?

不,您获得了一个 ICertificate 对象(或者,更有可能是 ICertificate2)实例的接口,而您只是直接使用它。如果您有证书的 B64 编码版本,则可以创建一个新的 ICertificate 实例,然后调用 ICertificate.Import 方法。证书本身的哈希仅由签名机构用于签署该特定证书。

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

这是正确的顺序和java代码所做的一样吗?

是和不是。Java 代码以明确的细节完成了所有必要的步骤,这是您对 CAPICOM 没有(实际上也不能)做的事情。不过,它应该会产生兼容的结果。

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

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

HashedData 类用于(惊喜)散列数据。它与 Signeddata 具有相同的限制,即它仅适用于宽字符串,因此与其他框架的兼容性充其量是狡猾的。

最后说明:Windows 通过 CAPI 函数组提供对更全面的加密函数的访问。CAPICOM 只是该库的一个接口,它(主要)以脚本语言(网页上的 JavaScript、VB 等)使用。您应该帮自己一个忙并尝试使用它而不是 CAPICOM,因为您很有可能会遇到使用 CAPICOM 无法正确完成的事情。在那个阶段,您将不得不使用 CAPI(或其他库)为您的所有应用程序重写部分内容。因此,如果您没有使用 CAPICOM 的要求,请立即节省时间并跳过它。

于 2011-07-01T13:14:32.553 回答