我有一个使用证书签名字符串的java应用程序。它使用SHA1加密字符串。我正在尝试将代码转换为Delphi 2010,但我不知道如何让它以与java应用程序相同的方式运行(使用sha1)。到目前为止,我发现了这个:
Delphi 7 access Windows X509 Certificate Store
它确实有效,但它不使用sha1,当我运行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常量以及一个哈希类,也许我应该使用这些类,但我不知道如何。
答案 0 :(得分:3)
我们在一些与我们的Java Tomcat App接口的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 :(得分: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 dev发现将哈希值传递回调用者的方式。
我可以在capicom tlb中看到一些SHA1常量以及一个哈希类,也许我应该使用这些类,但我不知道如何。
HashedData类用于(惊讶)哈希数据。它与Signeddata具有相同的限制,即它只适用于宽带,因此与其他框架的兼容性充其量是狡猾的。
最后注意事项:Windows通过CAPI功能组提供对更全面的加密功能的访问。 CAPICOM只是该库的接口,主要用于脚本语言(网页上的JavaScript,VB等)。你应该帮个忙,尝试使用它而不是CAPICOM,因为很有可能你会遇到使用CAPICOM无法正常使用的东西。在那个阶段,您将不得不使用CAPI(或其他库)为您的所有应用程序重写部分。因此,如果您没有要求使用它,请立即节省时间并跳过CAPICOM。