8

Background:

I have created an applet to extract public key of a certificate extracted from a smart card. This public key is then stored in database. The private key of certificate is used to sign data and the public key is then used to verify the signature. Code for extracting public key from certificate:

private byte[] getPublicKey(KeyStore paramKeyStore)
    throws GeneralSecurityException {
  Enumeration localEnumeration = paramKeyStore.aliases();

  if (localEnumeration.hasMoreElements()) {
    String element = (String) localEnumeration.nextElement();
    Certificate[] arrayOfCertificate =
        paramKeyStore.getCertificateChain(element);
    byte[] publicKeyByteArray =
        arrayOfCertificate[0].getPublicKey().getEncoded();

    return publicKeyByteArray;
  }
  throw new KeyStoreException("The keystore is empty!");
}

This publicKeyByteArray is then storeed in database as BLOB after converting to string using bytes2String method:

private static String bytes2String(byte[] bytes) {
  StringBuilder string = new StringBuilder();
  for (byte b : bytes) {
    String hexString = Integer.toHexString(0x00FF & b);
    string.append(hexString.length() == 1 ? "0" + hexString : hexString);
  }
  return string.toString();
}

The content of the BLOB(key) saved in database is:

30820122300d06092a864886f70d01010105000382010f003082010a02820101009bd307e4fc38adae43b93ba1152a4d6dbf82689336bb4e3af5160d16bf1599fe070f7acbfefd93e866e52043de1620bd57d9a3f244fb4e6ef758d70d19e0be86e1b12595af748fbc00aad9009bd61120d3348079b00af8462de46e254f6d2b092cbc85c7f6194c6c37f8955ef7b9b8937a7e9999541dbbea8c1b2349c712565482dbd573cd9b7ec56a59e7683b4c246620cf0d8148ed38da937f1e4e930eb05d5b4c6054712928fa59870763468c07e71265525e1e40839b51c833579f5742d3c8e0588766e3ed6deef1593b10baad0a2abea34734de1505d37710e1cfaa4225b562b96a6a4e87fecb1d627d4c61916e543eba87054ee9212e8183125cdb49750203010001

After reading the stored public key byte[] from database, I try to convert it back to Public Key using following code:

Cipher rsa;
rsa = Cipher.getInstance("RSA");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pkey.getBytes());
PublicKey pk = keyFactory.generatePublic(publicKeySpec);
rsa.init(Cipher.DECRYPT_MODE, pk);
byte[] cipherDecrypt = rsa.doFinal(encryptedText.getBytes());

but it gives following error:

Caused by: java.security.InvalidKeyException: invalid key format
    at sun.security.x509.X509Key.decode(X509Key.java:387)
    at sun.security.x509.X509Key.decode(X509Key.java:403)
    at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:83)
    at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:298)
    at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:201)

Please suggest the reason and resolution for this issue.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
user3619997
  • 197
  • 2
  • 2
  • 12
  • 3
    "*An encrypted text provided by the user **is decrypted using the stored public key from database**. The resultant decrypted text is matched for authentication purposes.*" Public keys cannot decrypt, they can only encrypt. Either the user *signed* data with the private key, in which case you can use the public key to *verify* the signature. Or the user encrypted data with a public key and you'll need the private key to decrypt. I guess it's the former case? – Duncan Jones Sep 02 '14 at 12:04
  • Hi, Yes this is the 1st case you have mentioned. The private key is used to sign data and the public key is then used to verify the signature. I have updated my question accordingly. – user3619997 Sep 02 '14 at 12:17
  • You're doing this wrong. The certificate should accompany the signature, and indeed the signature should include the certificate in the data it signs. The public key can then be acquired directly from the certificate: you don't need to store it in a database at all. – user207421 Sep 02 '14 at 12:28
  • The scenario is that a user approve a task and while approving, he/she uses the smart card to sign the data. The application creates a hash of data and then signs it using the private key. The public key of smart card is also stored in the database. Later on, the encrypted text (which will somehow be feed into application through a screen) is validated against the hash stored in the database. The public key is used here to decrypt the encrypted text. There is no smart card available at this time – user3619997 Sep 02 '14 at 12:44

1 Answers1

10

You must have an error in the way you read the key back from the database. The following code works just fine for me:

String key = "3082012230..."; // full key omitted for brevity
byte[] derPublicKey = DatatypeConverter.parseHexBinary(key);

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(derPublicKey);
keyFactory.generatePublic(publicKeySpec);

I would guess, based on the use of pkey.getBytes(), that you've simply tried to get the bytes from the string rather than hex-decoding it.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254