extorn
extorn

Reputation: 685

Verify a signature in Java using OpenSSL generated key and certificate

The Java 8 code below results in the message "signature NOT signed by matching key" and I can't understand why. Any ideas why its not verifying?

My best guess is that it is something to do with the way the private key or certificate are being generated by openssl, but from my reseach it seems I'm using the correct commands. What I'm trying to do is load a private key, sign a message, then verify the signature against a public key contained in an x509 certificate.

The privatekey.pkcs8.key file was generated using MS windows based openssl using the following commands:

openssl.exe" genrsa -out privatekey.pem 1024
openssl pkcs8 -in privatekey.pem -inform PEM -topk8 -out privatekey.pkcs8.key -outform DER -nocrypt

The publickey.cer was also generated using MS windows based openssl, using the following commands:

openssl req -x509 -key privatekey.pem -days 365 -out publickey.cer -new

The Java code I've been using in an attempt to verify the signature is as follows:

public static void main(String[] args) throws Exception{ 
    new TestSigs();
}

public TestSigs {
    byte[] signature = generateSignatureForMessage("src/cryptoprivatekey.pkcs8.key", "Hello");
    verifySignature("src/crypto/publickey.cer", signature);
}

public byte[] generateSignatureForMessage(String privateKeyPath, String message) throws Exception {
    RSAPrivateKey privKey = loadPrivateRSAKeyFromFile(privateKeyPath);
    Signature s = Signature.getInstance("SHA256withRSA");
    s.initSign(privKey);
    s.update(ByteBuffer.wrap(message.getBytes()));
    byte[] signature = s.sign();
    return signature;
}

private void verifySignature(String publicKeyPath, byte[] signature) throws Exception {
    Certificate cert = loadCertificate(publicKeyPath);
    Signature s = Signature.getInstance("SHA256withRSA");
    s.initVerify(cert);
    if(s.verify(signature)) {
        System.out.println("signature signed by matching key");
    } else {
        System.out.println("signature NOT signed by matching key");
    }
}

private Certificate loadCertificate(String filename) throws FileNotFoundException, CertificateException {
    FileInputStream fis = new FileInputStream(filename);
    BufferedInputStream bis = new BufferedInputStream(fis);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    return cf.generateCertificate(bis);
}

private RSAPrivateKey loadPrivateRSAKeyFromFile(String keyPath) throws Exception {
    byte[] privKeyBytes = loadRSAKeyBytesFromFile(keyPath);
    KeyFactory kFact = KeyFactory.getInstance("RSA");
    KeySpec ks = new PKCS8EncodedKeySpec(privKeyBytes);
    return (RSAPrivateKey)kFact.generatePrivate(ks);
}

Upvotes: 3

Views: 4741

Answers (1)

GPI
GPI

Reputation: 9338

Your signature (no pun) of the verifySignature method is a tell tale that your implementation can not work. For a Signature to be checked, the processor needs to hava access to both the signed data, and the signature. Which your method does not provide :

private void verifySignature(String publicKeyPath, byte[] signature)

I suggest you modify it to be symetrical to the generateSignatureForMessage method, that is :

  1. You feed the signature object the content it needs to work (the content to be signed for signature, the signed content for signature checking)
  2. You ask the signature object to perform the work (sign, or verify)

Which add up to an implementation akin to :

private void verifySignature(String publicKeyPath, byte[] signature, byte[] signedContent) throws Exception {
    Certificate cert = loadCertificate(publicKeyPath);
    Signature s = Signature.getInstance("SHA256withRSA");
    s.initVerify(cert);
    s.update(signedContent);
    if(s.verify(signature)) {
        System.out.println("signature signed by matching key");
    } else {
        System.out.println("signature NOT signed by matching key");
    }
}

Upvotes: 3

Related Questions