3

I have a java application that signs a string using a certificate. It works encrypting the string it with SHA1. I am trying to translate the code to Delphi 2010, but I have no idea how to get it working the same way the java app does (using sha1). So far, I have found this:

Delphi 7 access Windows X509 Certificate Store

It does work, but it does not use sha1 and I get different results when I run the java app.

Java code

 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 Code

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;

EDIT

Based on the java code, should I get the cert info in binary format, apply sha1 on it and them convert it to hex? Is this the right order and the same thing the java code does? I can see some SHA1 constants in the capicom tlb as well as a hash class, maybe I should use those classes, but I dont know how.

Community
  • 1
  • 1
Rafael Colucci
  • 6,018
  • 4
  • 52
  • 121
  • 1
    you are encoding in base 64 in delphi and in java use base 16, I think it might be a problem. – Asad Rasheed Jun 21 '11 at 21:00
  • It may be, but thats another issue. I need to know how to sign a document using SHA1. – Rafael Colucci Jun 21 '11 at 21:02
  • 4
    There was no intent to be impolite in my comment. If you took it that way, I apologize - I was trying to be helpful. Perhaps you should try to be more polite - the "question that was not even asked to you" is rude, as you posted the question in a public forum and therefore *did* address it to anyone choosing to spend their time trying to help you (for free, BTW). And your reply makes no sense, either - you're telling me that both of the constants have the value `0` and therefore have no difference between them? I find that pretty unlikely (but not wasting my time to check now). – Ken White Jun 21 '11 at 22:57
  • @ken ok, i am sorry for that. And yes, there are no differences between them. Its not unlikely, they are constants to be used in different places. CAPICOM_ENCODE_BINARY = $00000001; CAPICOM_HASH_ALGORITHM_SHA1 = $00000000; CAPICOM_ENCODE_BASE64 = $00000000; – Rafael Colucci Jun 22 '11 at 12:08

2 Answers2

3

We use DCPCrypt in some delphi apps that interface with our Java Tomcat App and are able to get SHA-256 compatible hashes. I suspect SHA1 is also easy.

Here's an example

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;
MJB
  • 9,352
  • 6
  • 34
  • 49
  • 1
    yes i know this library. What I dont know is what this line does: `gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);`. I suppose that CAPCOM is handling the sha1 by itself and I dont know when it does. Maybe I need somebody to help me understand the java code. Should I get the cert info in binary format, apply sha1 on it and them convert it to hex? Is this the right order? – Rafael Colucci Jun 22 '11 at 12:04
0

First, what makes you think you're not using SHA-1 ? I'm asking because CAPICOM's sign function only works with SHA-1 signature.

Second, how do you know that you're getting a different result ? Have you tried to validate the answer ? If yes, using what ?

Third, there is something that you MUST know about CAPICOM: the "content" property is a widestring. This has various implication, including the fact that all content will be padded to 16-bits. If your input data is of different size, you'll get a different result.

Based on the java code, should I get the cert info in binary format, apply sha1 on it and them convert it to hex?

No. You get an interface to an instance of a ICertificate object (or, more likely, ICertificate2) and you just use that directly. If you have the B64 encoded version of the certificate, you can create a new ICertificate instance and then call the ICertificate.Import method. The hash of the certificate itself is only used by the signing authority to sign that specific cert.

The hash algorythm is actually used during the data signature process: the library reads the data, creates a hash of that data (using SHA-1 in case of CAPICOM) and then digitally sign that hash value. This reduction is necessary because signing the whole data block would be far too slow and because, that way, you only have to carry the hash if you're using a hardware crypto system.

Is this the right order and the same thing the java code does?

Yes and no. The Java code does all the necessary steps in explicit details, something you don't have (and actually cannot) do with CAPICOM. It should result in compatible result, though.

It also has an additional step not related to the signature itself: I'm not sure what it does because it seems to create a dummy certificate information data and store the SHA-1 hash value of the signed CMS message and return the resulting instance. I suppose that it's a way the Java dev has found to pass the hash value back to the caller.

I can see some SHA1 constants in the capicom tlb as well as a hash class, maybe I should use those classes, but I dont know how.

The HashedData class is used to (surprise) hash data. It has the same limitation as Signeddata i.e. it only works on widestrings so compatibility with other frameworks is dodgy at best.

Final note: Windows offers access to much more comprehensive cryptographic functions through the CAPI group of functions. CAPICOM is only an interface to that library that is used (mostly) in script language (JavaScript on web pages, VB, etc). You should do yourself a favor and try using it instead of CAPICOM because there is a good chance you'll encounter something that you simply cannot do properly using CAPICOM. At that stage, you will have to rewrite part for all of your application using CAPI (or another library). So save time now and skip CAPICOM if you don't have a requirement to use it.

Stephane
  • 3,173
  • 3
  • 29
  • 42