RSA Encryption with given public key (in Java)

2019-01-29 22:48发布

问题:

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:

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());


回答2:

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.



回答3:

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



回答4:

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.