简体   繁体   中英

Generate AES key from RSA encoded AES key

What I am trying to do is to encrypt a string with AES, encrypt the AES key getEncoded() value with RSA, then decrypt that AES getEncoded() value so that I get my original string. The public key is loaded from the users certificate, and the private key from file. The code is given below.

public class Main {

public static void main(String[] args) throws Exception {
String myString = "My Message";
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(128);

    SecretKey secretKey = keyGenerator.generateKey();

    byte[] initializationVector = new byte[128 / 8];//16
    SecureRandom prng = new SecureRandom();
    prng.nextBytes(initializationVector);

    Cipher AESCipherForEncryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");

    AESCipherForEncryption.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initializationVector));

    byte[] byteVersionOfMyMessage = myString.getBytes();
    byte[] byteVersionOfCipherText = AESCipherForEncryption.doFinal(byteVersionOfMyMessage);
    String cipherText = new BASE64Encoder().encode(byteVersionOfCipherText);

    InputStream in1 = new FileInputStream("user.crt");
    CertificateFactory cf1 = CertificateFactory.getInstance("X509");
    Certificate c1 = cf1.generateCertificate(in1);
    X509Certificate toSendcert = (X509Certificate) c1;
    PublicKey publicKey = toSendcert.getPublicKey();
    String cipherTextRSA = encryptRSA(publicKey, new String(secretKey.getEncoded()));

    String decypheredRSA = decryptRSA(getPrivateKey("user.pk8", "RSA"), cipherTextRSA);
    System.out.println(cipherTextRSA);
    System.out.println(decypheredRSA);

    SecretKey originalKey = new SecretKeySpec(new String(decypheredRSA.getBytes("UTF-8")).getBytes(), 0, new String(decypheredRSA.getBytes("UTF-8")).getBytes().length, "AES");

    Cipher AESCipherForDecryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    AESCipherForDecryption.init(Cipher.DECRYPT_MODE, originalKey, new IvParameterSpec(initializationVector));
    byte[] byteVersionOfDecriptedText = AESCipherForDecryption.doFinal(new BASE64Decoder().decodeBuffer(cipherText));
    String decMessage = new String(byteVersionOfDecriptedText);
    System.out.println(decMessage);
}
public static String encryptRSA(PublicKey pubKey, String message) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, pubKey);
    Base64.Encoder encoder = Base64.getEncoder();
    String encryptedString = encoder.encodeToString(cipher.doFinal(message.getBytes("UTF-8")));
    return encryptedString;
}

public static PrivateKey getPrivateKey(String filename, String algorithm) throws Exception {
    File f = new File(filename);
    FileInputStream fis = new FileInputStream(f);
    DataInputStream dis = new DataInputStream(fis);
    byte[] keyBytes = new byte[(int) f.length()];
    dis.readFully(keyBytes);
    dis.close();

    String temp = new String(keyBytes);
    String privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----", "");
    privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
    privKeyPEM = privKeyPEM.replace("\n", "");

    byte[] decoded = Base64.getDecoder().decode(privKeyPEM);

    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
    KeyFactory kf = KeyFactory.getInstance(algorithm);
    return kf.generatePrivate(spec);
}

public static String decryptRSA(PrivateKey prKey, String encrypted) throws Exception {
    Base64.Decoder decoder = Base64.getDecoder();
    byte[] input = decoder.decode(encrypted);
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, prKey);

    return new String(cipher.doFinal(input));
}

The error that I keep getting is:

Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 28 bytes
    at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:509)
    at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1067)
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1038)
    at javax.crypto.Cipher.implInit(Cipher.java:805)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1396)
    at javax.crypto.Cipher.init(Cipher.java:1327)
    at com.company.Main.main(Main.java:79)

If I don't encrypt and decrypt the secretKey.getEncoded() value, and just use AES without RSA it works properly. Also working with RSA, if I just encrypt some string with a public key, and decrypt it with a private it works. My question would be: "How could I properly encrypt and decrypt the secretKey.getEncoded() value with RSA, so that I can properly encrypt and decrypt myString ?".

new String(secretKey.getEncoded())

This won't work as AES keys contain random bytes, and not every byte is a character representative. The problem with the standard string conversion in Java is that it drops unknown characters and bytes instead of generating an exception during encoding / decoding.

RSA operates on bytes, you should not turn the key into string and then back again into bytes as the transformation may be lossy (eg dropping 4 of the 32 bytes).

Alternatively - and probably even better - you may want to try the wrapping modes of cipher instead. This should be compatible with some hardware solutions out there. In that case you don't even have to call getEncoded .


OAEP encryption and authenticated encryption modes such as GCM should be preferred over PKCS#1 padding (the default for the Sun providers) and CBC mode encryption.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM