No luck with RSA and existing cert

I want to encrypt data in my software, data which will be sent to me by the user, in such a way that only I can decrypt it. This seems to call for asymmetric encryption (only the public key would be embedded in the software), so I am trying to use RSA.

Specifically I am trying to encrypt and decrypt data using the key pairs found in a cert that we bought from a cert authority. The cert says that key is a "Sun RSA public key, 1024 bits". In the following test, I encrypt using the cert's public key and decrypt using the same, for want of a method to return the private key but the results are the same if I initialize the cipher for decryption with the cert itself (which presumably contains the private key).

Key key = cert.getPublicKey();

Cipher cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.ENCRYPT_MODE, key);

byte[] enc = cipher.doFinal(test.getBytes());

cipher.init(Cipher.DECRYPT_MODE, key);

byte[] dec = cipher.doFinal(enc);

but at the decyrption stage I get the following error:

Exception in thread"main" javax.crypto.BadPaddingException: Data must start with zero.

which I don't know what to make of. It seems to me that I am following the (rather scant) instructions to the letter. If I specify "RSA/ECB/NoPadding" as the transformation I don't get the above error but the roundtrip fails to recreate the original string.

Furthermore, as I said before, I wanted to use public key encryption because I must include the encryption key in the software and I do not want it to be sufficient to decrypt the cipher. I was hoping that with RSA you'd encrypt using the public key but that you'd need either the secret key or the whole cert to decrypt. However the Javadocs do not say so explicitely and I am left unsure as to how this works exactly. Can anyone shed some light?

[1988 byte] By [xolotla] at [2007-10-3 4:19:17]
# 1

I agree, the documentation is inadequate. Have you also looked at the JCE reference (http://java.sun.com/j2se/1.5.0/docs/guide/security/jce/JCERefGuide.html)? This expands a lot on the javadocs for the classes. It might also help to learn more about cryptography; one book that others recommend is "Practical Cryptography" by Ferguson and Schneier.

I think the one key misunderstanding you have is what is in a certificate. A certificate contains only the public key, some information about the identity of the owner of the private key, and a digital signature over this public key and identifying information. The private key is not in the certificate! Nor should it be. If it were, it would no longer be private and the security of the system would fall apart.

The location of the private key depends entirely on the application that created the key pair. java's keytool, for example, stores the private key in a password protected file.

The error you are seeing makes sense once you understand that , for an RSA cipher, the type of key, public or private, as well as the mode Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE, determine the interpretation of the subsequent update or doFinal method calls.

Thus in your example, your first call to cipher.doFinal gives the RSA encryption of the data, which is what you wanted. Your second, however, attempts to decrypt this encrypted data with the public key, which makes no sense in this context. It checks to see if the result is has the proper padding, which it does not. If you tell it to assume no padding, you won't get an exception but the result still won't make any sense. You need to init the cipher with the private key for the second part.

ghstarka at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 2
Was 'the original string' constructed from binary data by any chance?You can't make a round-trip between binary data and a String. Use byte[].
ejpa at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 3

Thanks a lot for the detailed answer. In the meantime yes I did read the JCE reference and it makes more sense now. In the meantime I tried to do this with self-generated key pairs and it worked, so it was clear that it was something to do with the cert or how I used it. I did not realize it does not contain the private key.

Incidentally the KeyStore Javadocs look buggy to me: in the preamble you can read about ks.getEntry("privateKeyAlias", password) but the getEntry method takes a KeyStore.ProtectionParameter, not a char[]: they probably meant to write getKey. And I don't see any way to generate a KeyStore.ProtectionParameter anyhow.

The API is confusing, too. For the alias that seems to correspond to our own cert, isCertificateEntry return false, which according to the docs implies that it was set with a call to setEntry but isKeyEntry returns true, which implies that is was set with a call to setKeyEntry and not setEntry: both cannot be true!

Finally all 3 aliases in the store seem to correspond to certs (getCertificate succeeds):one is for our own cert, the other two for the two certs which are part of its chain (and which themselves have no chain). In all cases getKey either throws (our own cert) or returns null. So where is that private key? It's possible that the key pwd we were given is just wrong but it is not clear from the API whether the same alias can refer to both a cert and a key or entry or only to one of those. In other words am I missing an alias or is it a pwd problem?

xolotla at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 4

> Was 'the original string' constructed from binary data by any chance?

No, the other way round. It was a test string.

> You can't make a round-trip between binary data and a String. Use byte[].

And why not, if you are careful to specify the same encoding? Shouldn't UTTF-8 and UTF-16 do the trick?

xolotla at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 5

Hmm, I guess the docs are wrong. The KeyStore.PasswordProtection class implements KeyStore.ProtectionParameter. So, the javadocs could be corrected by changing the line to

KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("privateKeyAlias",

new KeyStore.PasswordProtection(password));

Yes, it is confusing. Any of the 3 types of entries can be set with the setEntry method. Alternatively, you can set a SecretKeyEntry or PrivateKeyEntry with setKeyEntry, or a TrustedCertificateEntry with setCertificateEntry. A curious fact is that PrivateKeyEntries must also be accompanied by a certificate; keytool will just generate a sel-signed certificate that is little more than a placeholder.

ghstarka at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 6
In other words the same alias should allow me to retrieve both the private key and the cert? The semantics of setKeyEntry suggests as much.
xolotla at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 7
The business about Strings is probably irrelevant now but you can only convert binary data to a String and back if it is UTF data to begin with. If it's just binary, e.g. from an encryption or key generation, the round-trip won't work.
ejpa at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 8
I see. So I would need to use, e.g., base64 encoding/decoding?
xolotla at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 9
Yep
ejpa at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 10
> In other words the same alias should allow me to> retrieve both the private key and the cert? The> semantics of setKeyEntry suggests as much.Yes, that is my understanding.
ghstarka at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 11
Then I must somehow have gotten hold of an incorrect pwd. I will follow up with the vendor.
xolotla at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...
# 12

Not necessarily.

Do you know how your key-pair was generated before you got the certificate from the CA vendor? Did you use the JCE Keystore, Mozilla keystore, OpenSSL, Windows CAPI keystore, etc? Whatever crypto library you used to generate the RSA key-pair, there is a key-store associated with it.That keystore has the Private Key in it. You can have the certificate in a text file anywhere on your file-system, read it in and perform the encryption part of the routine, but in order to decrypt, you must open your Keystore and then attempt to read the Private Key for decryption.

If you don't specify a keystore in your JCE code, by default it assumes the JKS (Java Key Store) in your home directory; and if that keystore was not used to generate the key-pair that corresponds to the certificate you received from the CA, then your code will never work.

Before you call the vendor, find the keystore that was used to generate the key-pair and then try opening that keystore with your code for the decryption.

As an aside, if you're looking for complete examples of working RSA encryption and decryption using multiple types of keystores (JKS, NSS, smartcards, etc.), you will find the source at www.strongkey.org.

arshad.noora at 2007-7-14 22:21:05 > top of Java-index,Security,Cryptography...