1

Small question regarding a Java code for RSA please.

I am having a very simple piece of Java code.


import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class RSA {

    public static void main(String[] args) throws Exception {
        String privateKeyString = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER+iccdhb474gKs6QE9c3JNS3BMlPTyFD2EOP3/NSrBlZtvVpKyQdHxYZ0W6a/IixWc0WjDqqcVAtrwCILmHU7Q==";
        String publicKeyString = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCyp0Sx3AgDhXYN3ecGaFYt51dnlrbgJJoRnYMh52QmDg=";

        String              secretMessage = "My TOP SECRET Message";

        byte[]              buffer1       = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec1      = new PKCS8EncodedKeySpec(buffer1);
        KeyFactory          keyFactory1   = KeyFactory.getInstance("RSA");
        PrivateKey          privateKey    = (RSAPrivateKey) keyFactory1.generatePrivate(keySpec1);

        byte[]             buffer2     = Base64.getDecoder().decode(publicKeyString);
        KeyFactory         keyFactory2 = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec2      = new PKCS8EncodedKeySpec(buffer2);
        PublicKey          publicKey   = (RSAPublicKey) keyFactory2.generatePublic(keySpec2);

        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);

        byte[] secretMessageBytes    = secretMessage.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
        String encodedMessage        = Base64.getEncoder().encodeToString(encryptedMessageBytes);

        Cipher decryptCipher = Cipher.getInstance("RSA");
        decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
        String decryptedMessage      = new String(decryptedMessageBytes, StandardCharsets.UTF_8);

        System.out.println("Step 1" + secretMessage);
        System.out.println("Step 2" + encodedMessage);
        System.out.println("Step 3" + decryptedMessage);
    }
    
}

I would have expected this to work, and be able to see some kind of gibberish for "Step 2"

But instead, I am seeing this:

Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DER input, Integer tag error
    at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:251)
    at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)

May I ask what is wrong with this piece of code please?

Thank you

7
  • Please describe more clearly what you want to achieve and reduce your example to the minimum. Commented Feb 18, 2022 at 7:19
  • 2
    Regarding the public key, X509EncodedKeySpec must be used instead of PKCS8EncodedKeySpec. Also, for RSA you have to use RSA keys and not EC keys. And, in Cipher.getInstance() the padding should be specified, e.g. RSA/ECB/PKCS1Padding, otherwise a provider dependent default will be used. With these changes, the code works. Commented Feb 18, 2022 at 7:28
  • Do yo mind pasting a sample with a fake private/public key please? :) Commented Feb 18, 2022 at 12:19
  • 1
    Sure, look at this: jdoodle.com/iembed/v0/nmU Commented Feb 18, 2022 at 13:35
  • I wish I could just accept comments :) thanks Commented Feb 18, 2022 at 14:02

1 Answer 1

1

In the code there are the following issues:

  • A private and public EC key are imported (additionally, both keys are swapped). Since RSA encryption is to be performed, RSA keys are to be used instead.
  • The public key is imported with PKCS8EncodedKeySpec. However, PKCS8EncodedKeySpec is intended for importing a private PKCS#8 key. Since a public X.509/SPKI key is to be imported, X509EncodedKeySpec has to be used instead.
  • When instantiating the Cipher object, only the algorithm is specified (RSA), but not the padding. Therefore, a provider-dependent default value is used for the padding. To avoid unintentional use of an incorrect padding and cross-platform problems, the padding should also be explicitly specified instead (e.g. RSA/ECB/PKCS1Padding).

The fixed code is as follows:

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class MyClass {
    public static void main(String args[]) throws Exception {
        
        // Fix 1: Use RSA keys: The private RSA key is a 2048 bit DER encoded PKCS#8 key (Base64 encoded). The public RSA key is the associated 2048 bit DER encoded X.509/SPKI key (Base64 encoded). Check the RSA keys with an ASN.1 parser, e.g. https://lapo.it/asn1js/.  
        String privateKeyString = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6cXloNrocJ8swwj8xktPmEOTfTgJT7KkUwWIGOjBB1QxApgdn5+SUHsakvEJq3Fgn+FnuuN8cfcqWrbT9jURtgNGinJNJ+StPM/PCxfhSv+XbkK11CV2EcJMyDB/8S/9u4E2ht/N1kT4OF2/mVDAq2MjjeUMq8CLmQR63ZMXB8lwmsGJEl4Rwt9WBZNcZFCfuCeBYrKRS7mtLzd4BTXEf0UuiNB/KJrz38TKSI47v/dRbB34wBNn0cuNLHb8t/eDaOvzV6J8SZgOWuL85mng6Fm77QGjUteWgJN76+YhDZgJfsRq1Q67JAy3ZXDHi5X538DcM/o+0wYEqkXxK3iIbAgMBAAECggEASlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODwczdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfyqmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBXmNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyFEM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQKBgQDd76M0Po5Lqh8ZhQ3obhFqkfO5EBXy7HUL15cw51kVtwF6Gf/J2HNHjwsg9Nb0eJETTS6bbuVd9bn884JoRS986nVTFNZ4dnjEgKjjQ8GjfzdkpbUxsRLWiIxuOQSpIUZGdMi2ctTTtspvMsDsjRRYdYIQCe/SDsdHGT3vcUCybwKBgQDXDz6iVnY84Fh5iDDVrQOR4lYoxCL/ikCDJjC6y1mjR0eVFdBPQ4j1dDSPU9lahBLby0VyagQCDp/kxQOl0z2zBLRI4I8jUtz9/9KW6ze7U7dQJ7OTfumd5I97OyQOG9XZwKUkRgfyb/PAMBSUSLgosi38f+OC3IN3qlvHFzvxFQKBgQCITpUDEmSczih5qQGIvolN1cRF5j5Ey7t7gXbnXz+Umah7kJpMIvdyfMVOAXJABgi8PQwiBLM0ySXo2LpARjXLV8ilNUggBktYDNktc8DrJMgltayaj3HNd2IglD5rjfc2cKWRgOd7/GlKcHaTEnbreYhfR2sWrWLxJOyoMfuVWwKBgFalCbMV6qU0LfEo8aPlBN8ttVDPVNpntP4h0NgxPXgPK8Pg+gA1UWSy4MouGg/hzkdHaj9ifyLlCX598a5JoT4S0x/ZeVHd/LNI8mtjcRzD6cMde7gdFbpLb5NSjIAyrsIAX4hxvpnqiOYRePkVIz0iLGziiaMbfMwlkrxvm/LRAoGBALPRbtSbE2pPgvOHKHTGPr7gKbmsWVbOcQA8rG801T38W/UPe1XtynMEjzzQ29OaVeQwvUN9+DxFXJ6Yvwj6ih4Wdq109i7Oo1fDnMczOQN9DKch2eNAHrNSOMyLDCBm++wbyHAsS2T0VO8+gzLABviZm5AFCQWfke4LZo5mOS10";
        String publicKeyString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGwIDAQAB";

        String              secretMessage = "My TOP SECRET Message";

        byte[]              buffer1       = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec1      = new PKCS8EncodedKeySpec(buffer1);
        KeyFactory          keyFactory1   = KeyFactory.getInstance("RSA");
        PrivateKey          privateKey    = (RSAPrivateKey) keyFactory1.generatePrivate(keySpec1);

        byte[]             buffer2     = Base64.getDecoder().decode(publicKeyString);
        KeyFactory         keyFactory2 = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec2      = new X509EncodedKeySpec(buffer2); // Fix 2: Apply X509EncodedKeySpec for the import of the public key
        PublicKey          publicKey   = (RSAPublicKey) keyFactory2.generatePublic(keySpec2);

        Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // Fix 3: Specify algorithm and padding when instantiating the Cipher object
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);

        byte[] secretMessageBytes    = secretMessage.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
        String encodedMessage        = Base64.getEncoder().encodeToString(encryptedMessageBytes);

        Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // Fix 3: Specify algorithm and padding when instantiating the Cipher object
        decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
        String decryptedMessage      = new String(decryptedMessageBytes, StandardCharsets.UTF_8);

        System.out.println("Step 1" + secretMessage);
        System.out.println("Step 2" + encodedMessage);
        System.out.println("Step 3" + decryptedMessage);
    }
}

With these changes, encryption and decryption work.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.