1
package demo123;

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

// Java 8 example for RSA-AES encryption/decryption.

public class AESwithRSA {

public static void main(String[] args) throws Exception {
    String plainText = "{\"scope\":\"payments\",\"x-mig-bank\": \"ICICI\",\"x-mig-channel\": \"RC\",\"tpp_redirect_uri\": \"www.tpp-app.com?query=123\",\"x-tpp-client-id\": \"Ck234567890112\",\"tpp-app-name\": \"amazon\",\"Consent Id\": \"d25a6f26-3ad5-4dd8-96fd-2582abfa3f58\",\"Type\": \"Domestic Payment\" }";

// Generate public and private keys using RSA

    Key privateKey = getPrivate("KeyPair/privateKey.jks");
    System.out.println("Private key success");
    System.out.println("Private key :" + privateKey);
    Key publicKey = getPublic("KeyPair/publicKey.");
    System.out.println("Public key success");
    System.out.println("Public key :" + publicKey);

// First create an AES Key

    String secretAESKeyString = getSecretAESKeyAsString();
    System.out.println("Secret Key "+secretAESKeyString);

// Encrypt our data with AES key

    String encryptedText = encryptTextUsingAES(plainText, secretAESKeyString);
    System.out.println("Encrypted text "+encryptedText);

// Encrypt AES Key with RSA Private Key

    byte[] encryptedAESKeyString = encryptAESKey(secretAESKeyString, publicKey);
    System.out.println("Encrypted AES key with RSA "+encryptedAESKeyString);

// First decrypt the AES Key with RSA Public key

    String decryptedAESKeyString = decryptAESKey(encryptedAESKeyString, privateKey);
    System.out.println("Decrypted AES key with RSA "+decryptedAESKeyString);

// Now decrypt data using the decrypted AES key!

    String decryptedText = decryptTextUsingAES(encryptedText, decryptedAESKeyString);

//Showing all the outputs

    System.out.println("input: " + encryptedText);
    System.out.println("AES Key: " + secretAESKeyString);
    System.out.println("decrypted: " + decryptedText);
    System.out.println("Text New:" + textNew);
    System.out.println("Encrypted New:" + encryptedTextNew);
    //System.out.println("Decrypted New:" + decryptedTextNew);

}

// Create a new AES key. Uses 128 bit (weak)

public static String getSecretAESKeyAsString() throws Exception {
    KeyGenerator generator = KeyGenerator.getInstance("AES");
    generator.init(128); // The AES key size in number of bits
    SecretKey secKey = generator.generateKey();
    String encodedKey = Base64.getEncoder().encodeToString(secKey.getEncoded());
    return encodedKey;
}

// Encrypt text using AES key

public static String encryptTextUsingAES(String plainText, String aesKeyString) throws Exception {
    byte[] decodedKey = Base64.getDecoder().decode(aesKeyString);
    SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");

    // AES defaults to AES/ECB/PKCS5Padding in Java 7
    Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    aesCipher.init(Cipher.ENCRYPT_MODE, originalKey);
    byte[] byteCipherText = aesCipher.doFinal(plainText.getBytes());
    return Base64.getEncoder().encodeToString(byteCipherText);
}

// Decrypt text using AES key

public static String decryptTextUsingAES(String encryptedText, String aesKeyString) throws Exception {

    byte[] decodedKey = Base64.getDecoder().decode(aesKeyString);
    SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");

    // AES defaults to AES/ECB/PKCS5Padding in Java 7
    Cipher aesCipher = Cipher.getInstance("AES");
    aesCipher.init(Cipher.DECRYPT_MODE, originalKey);
    byte[] bytePlainText = aesCipher.doFinal(Base64.getDecoder().decode(encryptedText));
    return new String(bytePlainText);
}

// Encrypt AES Key using RSA private key

private static byte[] encryptAESKey(String plainAESKey, Key publicKey ) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    return Base64.getEncoder().encode(cipher.doFinal(plainAESKey.getBytes()));
}

// Decrypt AES Key using RSA public key

private static String decryptAESKey(byte[] encryptedAESKey, Key privateKey) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedAESKey)));
}

//Getting public and private .jks format key

public static Key getPrivate(String filename) throws Exception {
        String password = "123456";
        FileInputStream is = new FileInputStream(filename);
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(is, password.toCharArray());
        String alias = "rib_pub_priv_ob";
        Key key = keystore.getKey(alias, password.toCharArray());        
        return key;
    }

    // https://docs.oracle.com/javase/8/docs/api/java/security/spec/X509EncodedKeySpec.html
    public static Key getPublic(String filename) throws Exception {
        String password = "123456";
        FileInputStream is = new FileInputStream(filename);
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(is, password.toCharArray());
        String alias = "tmsx_pub_priv_ob";
        Key key = keystore.getKey(alias, password.toCharArray());        
        return key;
    }

}

//Following is the output

Private key success
Private key :sun.security.rsa.RSAPrivateCrtKeyImpl@ffd2dcac
Public key success
Public key :sun.security.rsa.RSAPrivateCrtKeyImpl@fff19d78
Secret Key XsZue3ATt4OAQFP5C4sa8Q==
Encrypted text mjynBmIDhsj9L3jhFmzb4CFaJr+i5k2B1luuTdg0ls3NoAvI3wLfeU54Sxo7IDBrqH3i3F3RNM4DDPhWdbtEMNQ+27EcQOugidB2BcTFigzIImNohZOVjBi+qrPC7KWGLf9JWlJHUsoUz+oKiuAJGjitrrIMg/qQN7He87hH6hxfNZ7vceZV2N6HihYsQ4R1S6YFRUDVBwuG+IjvEyzihkw4mmlmjq4FIspXmaYuxYE/6urevUD/dY7HSLVrVRst83VRKnqDrzf32RolGsM12Ebyk0XJGGOYHV/OWfYExkaQdfUaEVMhU3h/tTmSoVJHEHTf1YdMxv5x/HZd2aXoYw==
Encrypted AES key with RSA [B@5f150435
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
    at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
    at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
    at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at demo123.AESwithRSA.decryptAESKey(AESwithRSA.java:135)
    at demo123.AESwithRSA.main(AESwithRSA.java:60)
wilx
  • 16,767
  • 5
  • 53
  • 109
bhavesh
  • 13
  • 4
  • My answer was wrong, so deleted. Regardless that I have to add a few notes to avoid some weaknesses: Do not use ECB mode, any other would do, use explicit algorithm declaration such as `AES/CBC/PKCS7Padding`, use some sort of integrity check such as HMAC or GCM mode, no need to always base64 encode – gusto2 Dec 31 '19 at 10:40

1 Answers1

2

The error is caused by loading in getPublic the private key instead of the public key. This can be easily seen in the output: For both keys, objects of the type sun.security.rsa.RSAPrivateCrtKeyImpl are displayed, whereas the public key should actually be of the type sun.security.rsa.RSAPublicKeyImpl. To load the public key in getPublic replace the line

Key key = keystore.getKey(alias, password.toCharArray());    

by

Key key = keystore.getCertificate(alias).getPublicKey(); 

See also [1][2]. With this fix the code works on my machine.

Update: In the current code, the Base64-encoded AES key (from getSecretAESKey) is encrypted with RSA and this key is then again Base64-encoded (in encryptAESKey). Such a double Base64-encoding is actually not necessary, as it would be sufficient to encrypt the raw AES key with RSA, as each Base64-encoding has a 33% overhead.

Topaco
  • 18,591
  • 2
  • 12
  • 39