31

I'm looking for a Java sample how to do RSA Encryption with a given public key (I have it in base64 format, seems it is 1024 bit length).

Below is my code, but I have InvalidKeySpec exception.

String publicKey = "AJOnAeTfeU4K+do5QdBM2BQUhfrRI2rYf/Gk4a3jZJB2ewekgq2VgLNislBdql/glA39w0NjXZyTg0mW917JdUlHqKoQ9765pJc4aTjvX+3IxdFhteyO2jE3vKX1GgA3i3n6+sMBAJiT3ax57i68mbT+KAeP1AX9199aj2W4JZeP"; KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] res = new Base64Encoder().decode(publicKey.getBytes()); X509EncodedKeySpec KeySpec = new X509EncodedKeySpec(res); RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(KeySpec); // here the exception occurs.. Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] cipherData = cipher.doFinal(input.getBytes()); return cipherData; 

Please give me the sample,

1
  • How was the public key created? Commented Jan 9, 2015 at 7:37

4 Answers 4

44

Here's how I manage to encrypt a string with only a RSA public key.

First save the public key in PEM-format to the filename pubkey.pem

-----BEGIN PUBLIC KEY----- AJOnAeTfeU4K+do5QdBM2BQUhfrRI2rYf/Gk4... -----END PUBLIC KEY----- 

Find the public RSA key modulus

$ openssl rsa -pubin -in pubkey.pem -modulus -noout Modulus=F56D... 

Find the public RSA key Exponent

$ openssl rsa -pubin -in pubkey.pem -text -noout ... Exponent: 65537 (0x10001) 

Then insert them into the following code.

BigInteger modulus = new BigInteger("F56D...", 16); BigInteger pubExp = new BigInteger("010001", 16); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, pubExp); RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec); Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] cipherData = cipher.doFinal(text.getBytes()); 
Sign up to request clarification or add additional context in comments.

5 Comments

Question: if I have the modulus and exponent, the public key I generate every time should be the same, right?
@jonney - Yes, but they were generated from the public key, which is, well, public. It's supposed to be public so everyone can create the ciphered data and only the holder of the private key can read it.
Good answer for those (like me) who only work based on generated files. Thanks for this.
Hi, text.getBytes() is the file store the string, right? @nevcx
@Tiana987642 - Yes, the data is encrypted and stored in the variable cipherData. docs.oracle.com/javase/7/docs/api/javax/crypto/…
16

Your "key" is not a valid public key. It is a Base64 string which, when decoded, yields a sequence of 129 bytes, the first being 0x00, followed by 0x93. This is not a valid format for a RSA public key, but it suspiciously looks like the big-endian signed encoding of a 1024-bit integer (i.e. the kind of encoding returned by BigInteger.toByteArray() and used in ASN.1 "INTEGER" values). A RSA public key nominally consists of two integers, one being the modulus and the other the public exponent. A typical RSA modulus has length 1024 bits, so chances are that you have here the modulus. You still need the public exponent to complete the key.

X509EncodedKeySpec expects the DER encoding of an ASN.1 structure which identifies the algorithm as being RSA, and contains a nested encoded structure which itself contains the two integers for the RSA public key. Assembling such a structure by hand could prove difficult (it is doable, but requires some in-depth understanding of ASN.1). A simpler method would be to use RSAPublicKeySpec:

String modulusBase64 = "..."; // your Base64 string here BigInteger modulus = new BigInteger(1, new Base64Encoder.decode(modulusBase64.getBytes("UTF-8"))); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKeySpec ks = new RSAPublicKeySpec(modulus, pubExp); RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(KeySpec); 

In the above, "pubExp" should be a BigInteger containing the public exponent, which you do not give. 3 and 65537 are the traditional values for the public exponent, but others are possible, and you do not give enough information to discriminate between public exponents (i.e. your code will appear to work even if you do not use the right one). Basically, you only have half of the public key; you should ask whoever gave you that half to send you the other half as well.

Note: String.getBytes() uses the platform default encoding, which is not always the same. If you insist on converting a string to a sequence of bytes, you should use an explicit charset name, such as "UTF-8", otherwise you may run into trouble if your code ever runs on, say, a Russian or Chinese system. Also, I do not know from where your Base64Encoder class comes from (it is not part of standard Java API) but chances are that it could also work directly over a String, or a StringReader, making the conversion step unnecessary.

2 Comments

how do u get the modulus and pubExp from a ssh key?
I am using java 7 and I can't find out Base64Encoder class, where can I get this dependency ?
0

Is your public key really X509 encoded? If not, then a unencoded Keyspec should help.

Comments

0

Your public key does not look like a Base64 encoded 1024 bit value. A 1024 bit value would require 172 characters (the last one being a filler =) and here we have 175 chars.

For a test, replace the last four characters in your String with a single = and test if this eliminates the exception. This won't solve the problem but may point in correct direction.

2 Comments

I did a little mistake in the code passed here (now it's corrected). The public key really is exactly 172 characters.
@david.papirov - but the last char still isn't '=' - a 172 char Base64 with no fill byte makes 172/(4/3)*8 =1032 bits.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.