4

I'm trying to encrypt data in android side and decrypt it in php side i'm using phpseclib in php to generate public/private keys

after i generate keys this public key i got in PHP side :

-----BEGIN RSA PUBLIC KEY-----".
            "MIGJAoGBAKks62Itns2uU/dVZJ4kCkMinHgyeh/rdMD53a4Zu2a76OIJvdSZ8q4c".
            "YTWvPj0giefVtMc7tV4c6AAw04jyIfmCTvcQUlHI+sspHxXDlQTagNoxCuA29b5L".
            "9MKO6Ok0LwF9rGgTywC1heNEulZz9ISn9FQDazJT+Bd9cnNOrJRdAgMBAAE=".
            "-----END RSA PUBLIC KEY-----

and then i encoded it to base64 and got this base64 encoded key

LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tTUlHSkFvR0JBS2tzNjJJdG5zMnVVL2RWWko0a0NrTWluSGd5ZWgvcmRNRDUzYTRadTJhNzZPSUp2ZFNaOHE0Y1lUV3ZQajBnaWVmVnRNYzd0VjRjNkFBdzA0anlJZm1DVHZjUVVsSEkrc3NwSHhYRGxRVGFnTm94Q3VBMjliNUw5TUtPNk9rMEx3RjlyR2dUeXdDMWhlTkV1bFp6OUlTbjlGUURhekpUK0JkOWNuTk9ySlJkQWdNQkFBRT0tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0t

i copied it to android side to encrypt data using it but i got InvalidKeySpecException

Android Side Code:

public static byte[] encrypt(String text) {

        byte[] encodedPublicKey= Base64.decode("LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tTUlHSkFvR0JBS2tzNjJJdG5zMnVVL2RWWko0a0NrTWluSGd5ZWgvcmRNRDUzYTRadTJhNzZPSUp2ZFNaOHE0Y1lUV3ZQajBnaWVmVnRNYzd0VjRjNkFBdzA0anlJZm1DVHZjUVVsSEkrc3NwSHhYRGxRVGFnTm94Q3VBMjliNUw5TUtPNk9rMEx3RjlyR2dUeXdDMWhlTkV1bFp6OUlTbjlGUURhekpUK0JkOWNuTk9ySlJkQWdNQkFBRT0tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0t", Base64.DEFAULT);



   PublicKey publicKey=null;
    KeyFactory keyFactory = null;
    try {
        keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
        publicKey = keyFactory.generatePublic(publicKeySpec);

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }


    byte[] cipherText = null;
    try {
        // get an RSA cipher object and print the provider
        final Cipher cipher = Cipher.getInstance("RSA");
        // encrypt the plain text using the public key
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        cipherText = cipher.doFinal(text.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cipherText;
}
neubert
  • 15,947
  • 24
  • 120
  • 212
  • 2
    According to the [docs](http://docs.oracle.com/javase/7/docs/api/java/security/spec/X509EncodedKeySpec.html), X509EncodedKeySpec expects an ASN.1 formatted key, whereas you are passing a [PEM](https://en.wikipedia.org/wiki/Privacy-enhanced_Electronic_Mail) formatted key. – adelphus Aug 15 '15 at 18:17
  • 1
    Heh, double base64 encoding. That really does not make sense. You need base64 to convert binary to text; there is little use for base64 if the input *is already text*, won't you agree? Strip off the header and footer and see if that works. – Maarten Bodewes Aug 15 '15 at 18:59
  • 1
    @MaartenBodewes: It won't work because the format is the simpler PKCS#1 public key, just the modulus and exponent. Java expects the more complicated SubjectPublicKeyInfo beast, aka an "X509EncodedKey" in Java-ese. – President James K. Polk Aug 15 '15 at 19:06
  • 1
    Probably your best bet is to export from PHPSeclib in the XML Signature format: `$rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_XML);`. Then extract the modulus and exponent out, base64 decode them and place them into an `RSAPublicKeySpec` instance. – President James K. Polk Aug 15 '15 at 19:09
  • @JamesKPolk i've did this : `$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_XML); $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_XML); extract($rsa->createKey()); ` and i got this key : ` uRe18YPb3x8Moqe6E9M2f4GTCQNm5i7lm5MrvmmiW0hjC+SG0vxXOcWpDegwQIlfWfN3SUP‌​JUxn4YeaAqX4Z2Xx2N9elgsPUM6EYYMl+dforjsBpk0uc1EvncYahpg0si0I7MnXqLD6SmVM6OX2M8XvT‌​kKbYu2GvZ6zlPrMe3B8= AQAB ` how to deal with this and use it in android ,can you provide me with code to do this ? – Khaled Awad Aug 15 '15 at 20:53
  • @JamesKPolk Used your comment to look up the right class for my answer, thanks, less debugging. Here's [the key after parsing the ASN.1 / DER](https://lapo.it/asn1js/#30818902818100A92CEB622D9ECDAE53F755649E240A43229C78327A1FEB74C0F9DDAE19BB66BBE8E209BDD499F2AE1C6135AF3E3D2089E7D5B4C73BB55E1CE80030D388F221F9824EF7105251C8FACB291F15C39504DA80DA310AE036F5BE4BF4C28EE8E9342F017DAC6813CB00B585E344BA5673F484A7F454036B3253F8177D72734EAC945D0203010001) – Maarten Bodewes Aug 15 '15 at 22:19

1 Answers1

2

The following code parses the PEM, feeds it to a PKCS#1 RSAPublicKey class parser from BouncyCastle, then constructs a JCE RSAPublicKey from the modulus and the exponent. The rest is error handling, the key itself and a main method to show it works.

For Android you may need to use Spongy Castle instead.

package nl.owlstead.stackoverflow;

import java.io.IOException;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

public final class RSAPublicKeyFromOpenSSL_PKCS1_PEM {
    private static final String PEM = "-----BEGIN RSA PUBLIC KEY-----\r\n"
            + "MIGJAoGBAKks62Itns2uU/dVZJ4kCkMinHgyeh/rdMD53a4Zu2a76OIJvdSZ8q4c\r\n"
            + "YTWvPj0giefVtMc7tV4c6AAw04jyIfmCTvcQUlHI+sspHxXDlQTagNoxCuA29b5L\r\n"
            + "9MKO6Ok0LwF9rGgTywC1heNEulZz9ISn9FQDazJT+Bd9cnNOrJRdAgMBAAE=\r\n"
            + "-----END RSA PUBLIC KEY-----\r\n";

    public static RSAPublicKey parsePEM(final String pem)
            throws IllegalArgumentException {

        // --- read PEM object
        final PemObject readPemObject;
        try (final PemReader reader = new PemReader(new StringReader(PEM))) {
            readPemObject = reader.readPemObject();
        } catch (final IOException e) {
            throw new IllegalArgumentException("Not a PEM object", e);
        }
        if (!readPemObject.getType().equalsIgnoreCase("RSA PUBLIC KEY")) {
            throw new IllegalArgumentException("Not a public key");
        }
        final byte[] pemContent = readPemObject.getContent();

        // --- create Bouncy Castle PKCS#1 public key
        final org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
        try {
            pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
                    .getInstance(pemContent);
        } catch (final Exception e) {
            throw new IllegalArgumentException(
                    "Could not parse BER PKCS#1 public key structure", e);
        }

        // --- convert to JCE RSAPublicKey
        final RSAPublicKeySpec spec = new RSAPublicKeySpec(
                pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
        final KeyFactory rsaKeyFact;
        try {
            rsaKeyFact = KeyFactory.getInstance("RSA");
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException("RSA KeyFactory should be available", e);
        }
        try {
            return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
        } catch (InvalidKeySpecException e) {
            throw new IllegalArgumentException(
                    "Invalid RSA public key, modulus and/or exponent invalid", e);
        }
    }

    public static void main(final String ... args) throws Exception {
        final RSAPublicKey publicKey = parsePEM(PEM);
        System.out.println(publicKey);
    }
}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • You could also prefix the public key with the right DER structure to create a `SubjectPublicKey` and parse that using `X509PublicKeySpec` but that only works if you know the exact key size in advance, so it's more brittle code than this. – Maarten Bodewes Aug 15 '15 at 22:14
  • Does this answer your question, Khaled? – Maarten Bodewes Aug 23 '15 at 14:26