Problem verifying signature created with openssl generated key

[nobr]This is a long posting so please bear with me!

I am having problems verifying a signature created by an openssl generated key and certificate.

I created a private key and self signed certificate as described in http://developer.softwareag.com/tamino/documentation/ssl/ssl_support/certificates.htm

I then used the php script below to sign some text and write the signature to a file. The signature is then read back in and verified using the certificate.This works:

PHP Script

<?php

// The file the source data will be saved in

$sourceFile ="/tmp/SourceData.txt";

// Create the unencrypted text info and store it in a file

$source ="Some text to be signed\n";

file_put_contents($sourceFile, $source);

// Paths to private key and certificate generated by openssl

$privateKeyPath="/Projects/CreateKeys/server.key.unsecure";

$certPath ="/Projects/CreateKeys/server.crt";

// Read in the private key data

$fp=fopen($privateKeyPath,"r");

$priv_key_data=fread($fp,8192);

fclose($fp);

$priv_key = openssl_pkey_get_private($priv_key_data);

// Read in the source data from the file

$fp=fopen($sourceFile,"r");

$srcDataFromFile=fread($fp,8192);

fclose($fp);

// compute signature

$result = openssl_sign($srcDataFromFile, $signature, $priv_key);

if ($result == True)

{

print"<br>";

print"Signature created successfully:";

print"<br>";

// Write the signature to the output file base64 encoded

$sig = base64_encode($signature);

file_put_contents("/tmp/signature.txt", $sig);

}

else

{

print"<br>Failed to sign<br>";

}

// Now try to verify signature

$fp=fopen($certPath,"r");

$cert_data=fread($fp,8192);

fclose($fp);

$pub_key = openssl_get_publickey($cert_data);

$cert_res = openssl_x509_read($cert_data);

$keyCheck = openssl_x509_check_private_key($cert_res, $priv_key);

if ($keyCheck == True)

{

$result = openssl_verify($srcDataFromFile, $signature, $pub_key);

if ($result == True)

{

print"<br><br>";

print"Signature verified";

print"<br><br>";

}

}

openssl_free_key($pub_key);

openssl_x509_free($cert_res);

?>

Now we get to the Java code.

All I want to do here is to verify the signature created by the PHP code using the same certificate I used to verify the signature in PHP:

import java.io.BufferedInputStream;

import java.io.DataInputStream;

import java.io.FileInputStream;

import java.security.KeyFactory;

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.Signature;

import java.security.cert.Certificate;

import java.security.cert.CertificateFactory;

import java.security.cert.X509Certificate;

import java.security.spec.PKCS8EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

publicclass SignatureTest

{

publicstaticvoid main(String[] unused)throws Exception

{

// Get the public key from the certificate

Certificate cert = readCertificateFromFile();

PublicKey publicKey = ((X509Certificate)cert).getPublicKey();

byte[] sigbytes = getSignatureBytes("C:/tmp/signature.txt");

boolean result = verify("C:/tmp/sourceData.txt", publicKey,"MD5withRSA", sigbytes);

System.out.println("Signature Verification Result = " + result);

}

privatestatic Certificate readCertificateFromFile()

{

Certificate cert =null;

try

{

FileInputStream fis =new FileInputStream("C:/Projects/CreateKeys/server.pem");

BufferedInputStream bis =new BufferedInputStream(fis);

CertificateFactory cf = CertificateFactory.getInstance("X.509");

while (bis.available() > 0)

{

cert = cf.generateCertificate(bis);

System.out.println(cert.toString());

}

}

catch (Exception e)

{

System.out.println("ERROR: " + e);

e.printStackTrace();

}

return cert;

}

publicstaticbyte[] getSignatureBytes(String filename)

{

byte[] sigBytes =null;

try

{

FileInputStream in =new FileInputStream(filename);

sigBytes =newbyte[8192];

int count = in.read(sigBytes);

in.close();

}

catch (Exception e)

{

System.out.println(e.toString());

e.printStackTrace();

}

return sigBytes;

}

}

Which produces the result:

Signature Verification Result = false

Please can someone help? I have spent several days trying to sort out this problem.

Thanks in advance...[/nobr]

[8049 byte] By [rdhulla] at [2007-10-3 3:19:14]
# 1

Well you don't show the verify method so I can't comment on that but there are two issues with this code:

(a) You are always returning a signature of byte[8192] regardless of the actual length read. Without knowing what happens in verify() I would guess that it won't like junk on the end of the signature. I would read the file and write it to a ByteArrayOutputStream and then get the byte[] array from that - it will be the correct length.

(b) Get rid of the while (bis.available() > 0) test, it's just a waste of time. Are you expecting more than one certificate to be in the file?

ejpa at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...
# 2

Can you post the PEM-encoded certificate and the details of the verify() method in the Java code? Secondly, what is the signing algorithm when you call openssl_sign($srcDataFromFile, $signature, $priv_key); ? Are you certain that it is MD5withRSA as you've apparently indicated in your Java code.

arshad.noora at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...
# 3

Thank you both for responding so quickly - it is much appreciated!

The verify method is as below:

private static boolean verify(String datafile, PublicKey pubKey, String sigAlg, byte[] sigbytes) throws Exception

{

Signature sig = Signature.getInstance(sigAlg);

sig.initVerify(pubKey);

FileInputStream fis = new FileInputStream(datafile);

byte[] dataBytes = new byte[8192];

int nread = fis.read(dataBytes);

while (nread > 0)

{

sig.update(dataBytes, 0, nread);

nread = fis.read(dataBytes);

}

return sig.verify(sigbytes);

}

I have taken on board the point about the signature and have modified the method as suggested. I have also removed the while (bis.available() > 0) test which served no purpose.

After reading the PHP openssl_sign documentation again the signing algorithm used is SHA1 not MD5 so I modified the algorithm passed to the verify method to be SHA1withRSA.

Unfortunately the results are still the same. The strange thing is that the PHP code signs and verifies the data without problem. It is only when I try to verify the signed data in Java there is a problem.

Another point to note is that the self signed certificate server.crt file generated by the openssl commands was as follows:

Certificate:

Data:

Version: 1 (0x0)

Serial Number: 1 (0x1)

Signature Algorithm: md5WithRSAEncryption

Issuer: C=UK, L=town, CN=CA/emailAddress=ca@ca.com

Validity

Not Before: Aug 23 19:03:24 2006 GMT

Not After : Aug 23 19:03:24 2007 GMT

Subject: C=UK, CN=name/emailAddress=name@company.com

Subject Public Key Info:

Public Key Algorithm: rsaEncryption

RSA Public Key: (1024 bit)

Modulus (1024 bit):

00:ea:4c:e7:ce:37:03:32:46:96:76:67:88:25:06:

c4:f5:1d:34:e8:47:4c:2b:6e:fd:ea:55:f6:42:29:

11:a4:e8:da:36:fa:16:17:ce:c8:5a:2e:b4:c6:f8:

bc:f2:7b:9a:b5:84:81:a9:8e:e4:1a:4f:94:bc:69:

86:1a:e6:a7:0c:95:2c:bc:13:42:57:d2:7c:c4:bc:

3a:d4:39:1c:91:29:02:01:0e:19:64:1e:af:d7:aa:

84:a3:64:c2:9f:20:88:fd:e7:95:78:a9:64:f6:c6:

d6:27:31:c7:c0:65:3a:11:a9:71:57:63:6e:bd:42:

9a:db:e1:45:c7:3b:ca:4b:e3

Exponent: 65537 (0x10001)

Signature Algorithm: md5WithRSAEncryption

b4:0c:03:bb:27:8b:5c:dc:67:c9:05:8f:67:3c:fb:6b:12:cd:

b5:7e:12:ec:d1:cc:5b:6c:15:24:8f:e6:8e:76:5d:79:13:ba:

7a:5a:20:2a:f3:32:bd:86:c0:87:c7:e2:dd:0b:d8:90:10:8e:

c0:b7:7e:b3:77:76:d2:bf:cb:14:d5:3d:72:0e:b4:d3:0d:2b:

a6:a4:f4:3b:ec:8b:ce:43:c0:d1:7e:e5:66:65:68:bf:db:6d:

fd:5a:2f:b3:56:10:06:9e:a5:be:d1:fd:d2:c2:6b:74:c7:ab:

4b:6c:6d:d9:47:1a:66:f4:1a:5e:e2:28:16:90:69:16:e4:cc:

ae:17

--BEGIN CERTIFICATE--

MIIB7zCCAVgCAQEwDQYJKoZIhvcNAQEEBQAwQzELMAkGA1UEBhMCVUsxDTALBgNV

BAcTBHRvd24xCzAJBgNVBAMTAkNBMRgwFgYJKoZIhvcNAQkBFgljYUBjYS5jb20w

HhcNMDYwODIzMTkwMzI0WhcNMDcwODIzMTkwMzI0WjA9MQswCQYDVQQGEwJVSzEN

MAsGA1UEAxMEbmFtZTEfMB0GCSqGSIb3DQEJARYQbmFtZUBjb21wYW55LmNvbTCB

nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6kznzjcDMkaWdmeIJQbE9R006EdM

K2796lX2QikRpOjaNvoWF87IWi60xvi88nuatYSBqY7kGk+UvGmGGuanDJUsvBNC

V9J8xLw61DkckSkCAQ4ZZB6v16qEo2TCnyCI/eeVeKlk9sbWJzHHwGU6EalxV2Nu

vUKa2+FFxzvKS+MCAwEAATANBgkqhkiG9w0BAQQFAAOBgQC0DAO7J4tc3GfJBY9n

PPtrEs21fhLs0cxbbBUkj+aOdl15E7p6WiAq8zK9hsCHx+LdC9iQEI7At36zd3bS

v8sU1T1yDrTTDSumpPQ77IvOQ8DRfuVmZWi/2239Wi+zVhAGnqW+0f3Swmt0x6tL

bG3ZRxpm9Bpe4igWkGkW5MyuFw==

--END CERTIFICATE--

Now when I tried to use this as the certificate in the Java program I got the following exception:

java.security.cert.CertificateException: Unable to initialize, java.io.IOException: insufficient data

By snipping off everything above the --BEGIN CERTIFICATE--

line the exceptions stopped but the verify method still returned false.

If it is of any use the method I used to generate the self signed certificate and private key was as follows:

In a clean directory create a new directory called newcerts

Begin by creating a certificate sign request

openssl genrsa -des3 -out server.key 1024

Create a decrypted PEM version of the RSA private key

openssl rsa -in server.key -out server.key.unsecure

Create a certificate sign request with the RSA key

openssl req -new -key server.key -out server.csr -config new_openssl.cnf

The next part relates to the certificate authority:

Create an RSA private key for the root certificate

openssl genrsa -des3 -out ca.key 1024

Create a decrypted PEM version of this private key

openssl rsa -in ca.key -out ca.key.unsecure

Create an empty file called index.txt

Create a file called serial in the current directory and enter "01" in column 1, line 1

Create the self-signed root certificate with the private key using the following info:

openssl req -new -x509 -days 365 -key ca.key -out ca.crt -config new_openssl.cnf

Sign the original certificate with the newly created self-signed root certificate

This step creates the files server.crt, server.key and server.key.unsecure

openssl ca -config new_openssl.cnf -policy policy_any -out server.crt -infiles server.csr

I have tried creating keys with PHP openssl functions, Java JCE methods, keytool etc. and they all fail for one reason or another regardless of the signing algorithm (I've tried DSA as well as RSA).

Any further help would be really appreciated.

rdhulla at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...
# 4
At the very least, you have failed to base64-decode your data. Recall that the signature you wrote out was the base64-encoded version.
ghstarka at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...
# 5
Oops - forgot about that. I now decode the signature data before calling the Signature.verify() method but unfortunately the result is still the same. Thank you for your help though, at least I am nearer to finding a solution.
rdhulla at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...
# 6

I haven't had a chance to look through your verify() code yet, but I think your problem could be your certificate:

Certificate:

Data:

Version: 1 (0x0)<--

Serial Number: 1 (0x1)

While the JCE documentation (http://java.sun.com/j2se/1.5.0/docs/guide/security/CryptoSpec.html#AppA) doesn't specify which version number of X509 certificates are supported, the current industry standard is version 3 for most uses; and it is possible that the JDK may be choking on that.

Try creating a X509v3 certificate in OpenSSL and then try your program again to see if it works; if it doesn't then I'll look a little closer at your code.

arshad.noora at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...
# 7

After my last post I tried re-generating the certificate and key still using the same method as before and now with all the code fixes it works! I can only assume that whilst examining the original keys/certs they must have become corrupted.

Thanks once again - as you have all provided useful answers I will split the duke dollars.

rdhulla at 2007-7-14 21:11:08 > top of Java-index,Security,Cryptography...