I've generated this test public key using 1024 RSA and then encoded it to DER and Base64 in another coding platform. I copied the key into a string in Android/Eclipse and I am trying to turn it into a public key using KeyFactory. It just keeps giving me an InvalidKeySpecException no matter what I try. Any advice at all would be appreciated.
private void prepKeys() {
String AppKeyPub = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5" +
"5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ" +
"OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// create the key factory
try {
KeyFactory kFactory = KeyFactory.getInstance("RSA");
// decode base64 of your key
byte yourKey[] = Base64.decode(AppKeyPub,0);
// generate the public key
X509EncodedKeySpec spec = new X509EncodedKeySpec(yourKey);
PublicKey publicKey = (PublicKey) kFactory.generatePublic(spec);
System.out.println("Public Key: " + publicKey);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The key you have is in PKCS#1 format instead of SubjectPublicKeyInfo structure that Java accepts. PKCS#1 is the encoding of the RSA parameters only and lacks things such as an algorithm identifier. SubjectPublicKeyInfo uses PKCS#1 internally - for RSA public keys anyway.
As the PKCS#1 public key is at the end of the SubjectPublicKeyInfo structure it is possible to simply prefix the bytes so that they become an RSA SubjectPublicKeyInfo. That solution is easier to perform without additional libraries such as Bouncy Castle. So if you need to go without an external library then you may have a look at my answer here.
Alternatively a simple BER decoder could be written to decode the structure into the two BigInteger values. The structure itself is not that complicated but the BER/DER length encoding takes some getting used to.
However, you can also use Bouncy Castle (lightweight API) to solve your issues:
String publicKeyB64 = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5"
+ "5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ"
+ "OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// ok, you may need to use the Base64 decoder of bouncy or Android instead
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey generatedPublic = kf.generatePublic(keySpec);
System.out.printf("Modulus: %X%n", modulus);
System.out.printf("Public exponent: %d ... 17? Why?%n", publicExponent); // 17? OK.
System.out.printf("See, Java class result: %s, is RSAPublicKey: %b%n", generatedPublic.getClass().getName(), generatedPublic instanceof RSAPublicKey);
As you can see it actually only requires a single class as interface, although that is of course backed up with the entire ASN.1/BER decoder functionality within Bouncy Castle.
Note that it may be required to change the Base 64 decoder to the Android specific one (android.util.Base64
). This code was tested on an equivalent Java runtime.
For those who dont want to use Bouncy Castle
public class RSAKeySeperation {
public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException {
String publicKeyB64 = "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBV8xakN/wOsB6qHpyMigk/5PrSxxd6tKTJsyMIq5f9npzZue0mI4H2o8toYImtRk6VHhcldo0t7UwsQXmFMk7D"
+ "i3C53Xwfk7yEFSkXGpdtp/7fbqNnjVoJl/EPcgoDsTPrHYF/HgtmbhzuYvYeY1zpV0d2uYpFxAuqkE9FreuuH0iI8xODFe5NzRevXH116elwdCGINeAecHKgiWe"
+ "bGpRPml0lagrfi0qoQvNScmi/WIN2nFcI3sQFCq3HNYDBKDhO0AEKPB2FjvoEheJJwTs5URCYsJglYyxEUon3w6KuhVa+hzYJUAgNTCsrAhQCUlX4+5LOGlwI5gonm1DYvJJZAgMBAAEB";
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey generatePublic = (RSAPublicKey) kf.generatePublic(spec);
BigInteger modulus = generatePublic.getModulus();
System.out.println(modulus);
BigInteger exponent = generatePublic.getPublicExponent();
System.out.println(exponent);
}
}