1

Code in Android:

public static String encryptRSA(String text) throws Exception {

    byte[] encoded = Base64.decode(RSA_PUBLIC_KEY, Base64.NO_WRAP);

    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, pubKey);

    byte[] encrypted = cipher.doFinal(text.getBytes());

    return Base64.encodeToString(encrypted, Base64.NO_WRAP);
}

Code in Java:

public static String encryptRSA(String text) throws Exception {

    byte[] encoded = Base64.getDecoder().decode(RSA_PUBLIC_KEY);

    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, pubKey);

    byte[] encrypted = cipher.doFinal(text.getBytes());

    return Base64.getEncoder().encodeToString(encrypted);
}

It is working in Java and I can decrypt via private key, but the same function in Android doesnt work. I also noticed the output is always the same for the same string, where it is random in Java:

Java output (works to decrypt):

try {
        System.out.println(encryptRSA("lol"));
        System.out.println(encryptRSA("lol"));
        System.out.println(encryptRSA("lol"));
    } catch (Exception ex) {
        Logger.getLogger(MainGUI.class.getName()).log(Level.SEVERE, null, ex);
    }

run:

G/tAnJiWA1htmdK93T+7PYUGQ6yCdpcjpTq6rZJt4JgnzGNp1Y5wtqV5sObrFMKUcslZK1OscNTTeo8eA9cS7msA9L+9/0+leqOW/A/cPSki+UtOy5u45ZtaLVv3IrPamktEq8ilHsh5JtrU7sZS0MGDqQdGJjwEJCcCv13leCpVoMsC6NcvZmf3MkJlvN4Xqa4FLOBguIX+k0b8QgNEvNNAH4v5PFfkjN3Ybec0DHXB+In6fybPM35oposuGjpq55VftIBSFvm0zHoHfeYrUVWPqTGbXjF8xA8YaKcYgHPMMrMW9SSV4vwkr4KXHkjHwQBCmzQ5WZSBu4qvwtl9AA== VKygaIxVz4QY5/JEGAjOyFuHLZaLOYeGEhzfvNDtNNplZ3Q/2c3rcfYdt8P9ykDF8k5e7KU09lDcB2y1RKOYEpXkiJPLQTRSz4HFKOj+6zhhRaVpXkBDZQEkJ4nLktnfeEwwsmfAwk8iZ8gY0hNfTRnHsqFxD5uf6sI4yT9Tj/1BXIcuO1r+/wgnuBQPqw8HyiZBuGuIEOyMpR6HakTo8BMCSQh9KNK+WBIxlssljHGQU4N0WyKKmI5WgvS6BImBjVdct75NDEBW7RlwHQpEvObIdaeJVjEfekvh04TvMip+Tp3HYmZ3SwdFPIaGhbLWq2y/i3mZ5huQWwkcBKPLfQ== DuMWeZW8SGRU1IXKfsxAMzrKzpisu1MPXp8DNwPq6EhxbVoUlco7nJqwe+yrne0Ba+aDXuJatYLBu7/DqaAhef9XsFZwXfNgQARywMEuQS130m4Ld6hWJ0onugHtkf6u37vTtFXvVdJOx2GG5eWQKbIbrTZkVhvf145s5yGzZtvX0xmzBx1lH/E0Y1v0EaSeW8nOcrf1Hi0cqKFl/cYZdLbaRrCv6hxy58t8FiVg0vuFPrZiIESRG7u9KYC7rahtwoaxascNgCSL99I/IszQEpcllfjDqFuMJwD4dVb3zxsMYX6JHqxnPOM1K5gHlNmzzWabTfwBORZhiH3wqVECKw==

Android output (doesnt work to decrypt):

System.out.println(this.encryptRSA("lol"));
        System.out.println(this.encryptRSA("lol"));
        System.out.println(this.encryptRSA("lol"));

I/System.out:     tB4qJuqwamQADNzDhZwaIBMFWYfVjGoCz1AYk9YudRUtWAtDeTlB17VAjUZSxedlP66M/G6SJ84Zl3fEvLb2JJbRUOSeEKIl8DzDPD+O9Jtd7DNzn0IDPsUt1mcjV6ixccFLdnHOpUYShHPl39zodPIRAAh83lcQYKs8iQwWL98helUQ8GghWSpcp2QappQwYFYbhHWbvUYXTjTIy/LdvHjAXcK/s5UMNZkUA5BWCCKkAx98K86JTIUcVJF2rCBdw5Sf6bWEuVk5QXIx5Ipy9YO7QAIJd80lFgr6Q3CC4FnKVaxLlCCefFzTIiBPw+FWCYo+mrDvBGV4/YqLgD11zg==

I/System.out: tB4qJuqwamQADNzDhZwaIBMFWYfVjGoCz1AYk9YudRUtWAtDeTlB17VAjUZSxedlP66M/G6SJ84Zl3fEvLb2JJbRUOSeEKIl8DzDPD+O9Jtd7DNzn0IDPsUt1mcjV6ixccFLdnHOpUYShHPl39zodPIRAAh83lcQYKs8iQwWL98helUQ8GghWSpcp2QappQwYFYbhHWbvUYXTjTIy/LdvHjAXcK/s5UMNZkUA5BWCCKkAx98K86JTIUcVJF2rCBdw5Sf6bWEuVk5QXIx5Ipy9YO7QAIJd80lFgr6Q3CC4FnKVaxLlCCefFzTIiBPw+FWCYo+mrDvBGV4/YqLgD11zg==

I/System.out: tB4qJuqwamQADNzDhZwaIBMFWYfVjGoCz1AYk9YudRUtWAtDeTlB17VAjUZSxedlP66M/G6SJ84Zl3fEvLb2JJbRUOSeEKIl8DzDPD+O9Jtd7DNzn0IDPsUt1mcjV6ixccFLdnHOpUYShHPl39zodPIRAAh83lcQYKs8iQwWL98helUQ8GghWSpcp2QappQwYFYbhHWbvUYXTjTIy/LdvHjAXcK/s5UMNZkUA5BWCCKkAx98K86JTIUcVJF2rCBdw5Sf6bWEuVk5QXIx5Ipy9YO7QAIJd80lFgr6Q3CC4FnKVaxLlCCefFzTIiBPw+FWCYo+mrDvBGV4/YqLgD11zg==

M. H.
  • 171
  • 1
  • 14
  • You're probably using different encodings. You should always use UTF8. – SLaks Jan 29 '17 at 19:39
  • 2
    Try also to use `Cipher.getInstance("RSA/ECB/PKCS1Padding");` instead of `Cipher.getInstance("RSA");` – pedrofb Jan 29 '17 at 19:49
  • @SLaks how can an input string of "lol" have country specific symbols? – M. H. Jan 29 '17 at 19:51
  • @pedrofb that fixed it :/ thank you very much. how come it worked in Java though? – M. H. Jan 29 '17 at 20:00
  • 1
    In Android it is needed to use specific cipher initialization. Seems an inconsistency between default cipher initialization in Java and Android. See http://stackoverflow.com/questions/6069369/rsa-encryption-difference-between-java-and-android – pedrofb Jan 29 '17 at 20:15
  • @pedrofb OAEP padding would be a more secure option. – Maarten Bodewes Jan 29 '17 at 21:31
  • @MaartenBodewes how come? can you give an example of what to change? would I have to change decryption code too to have it working? Because the decryption is on a php script side, via "openssl_private_encrypt($string, $encrypted, $key);" – M. H. Jan 30 '17 at 07:10
  • @MaartenBodewes is right. In Java/Android you can use `Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");`, In PHP side, according to http://php.net/manual/es/function.openssl-private-decrypt.php you will need to use `OPENSSL_PKCS1_OAEP_PADDING` in the `padding` parameter – pedrofb Jan 30 '17 at 09:37
  • @pedrofb I looked at the API of openssl_private_decrypt, openssl_private_encrypt, openssl_public_decrypt and openssl_public_encrypt. And only openssl_private_decrypt and openssl_public_encrypt support that padding. How come? So I am not allowed to use "RSA/ECB/PKCS1Padding" for public decrypt too I guess on the Java side? Whats the four correct padding values for Java/Android Public encrypt/decrypt, and PHP private encrypt/decrypt? – M. H. Jan 30 '17 at 13:35
  • @pedrofb So it's on Java/Android (public): "RSA/ECB/OAEPWithSHA1AndMGF1Padding" (encrypt), "RSA/ECB/PKCS1Padding" (decrypt). And on the PHP side (private): "OPENSSL_PKCS1_OAEP_PADDING" (decrypt) and no argument for ecrypt. How come the private encrypt doesnt support or need it? – M. H. Jan 30 '17 at 13:41
  • Encrypt with public key and decrypt with private. Use the same padding in encryption and decryption. e.g. `RSA/ECB/OAEPWithSHA1AndMGF1Padding` java/android side and `OPENSSL_PKCS1_OAEP_PADDING` in php side. – pedrofb Jan 30 '17 at 20:32
  • @pedrofb After using your suggestion with the OAEPWithSHA1AndMGF1Padding padding, I am running into random "java.lang.RuntimeException: error:0400006d:RSA routines:OPENSSL_internal:DATA_LEN_NOT_EQUAL_TO_MOD_LEN" exceptions in the Android code when decoding. Any idea why? – M. H. Feb 02 '17 at 17:13

1 Answers1

0

Answer by @pedrofb was correct and solved the issue. Though I dont know why, it is working in Java with "RSA" too, and generating correctly encrypted outputs, though in Android, it wont. @SLaks was correct too of course you should always use utf8 when converting to bytes, but in this case, I just use the RSA as a method of signing, and the input String was always base64 already. But here's the working code both in Android and Java:

public static String encryptRSA(byte[] toEncode) throws Exception {

    byte[] encoded = Base64.decode(RSA_PUBLIC_KEY, Base64.NO_WRAP);

    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, pubKey);

    byte[] encrypted = cipher.doFinal(toEncode);

    return Base64.encodeToString(encrypted, Base64.NO_WRAP);
}
M. H.
  • 171
  • 1
  • 14