简体   繁体   English

从RSA编码的AES密钥生成AES密钥

[英]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. 我要执行的操作是使用AES加密字符串,使用RSA加密AES密钥getEncoded()值,然后解密该AES getEncoded()值,以便获得原始字符串。 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. 如果我不加密和解密secretKey.getEncoded()值,而仅使用不带RSA的AES,则它将正常工作。 Also working with RSA, if I just encrypt some string with a public key, and decrypt it with a private it works. 如果我只是使用公共密钥加密某些字符串,然后使用私有密钥解密它,那么它也可以与RSA一起使用。 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 ?". 我的问题是:“如何使用RSA正确加密和解​​密secretKey.getEncoded()值,以便可以正确加密和解​​密myString ?”。

new String(secretKey.getEncoded())

This won't work as AES keys contain random bytes, and not every byte is a character representative. 由于AES密钥包含随机字节,并且并非每个字节都代表一个字符,因此这将不起作用。 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. Java中标准字符串转换的问题在于,它丢弃未知的字符和字节,而不是在编码/解码期间生成异常。

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). RSA对字节进行操作,您不应该将密钥转换为字符串,然后再转换为字节,因为转换可能会造成损失(例如,删除32个字节中的4个)。

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 . 在这种情况下,您甚至不必调用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. OAEP加密和经过身份验证的加密模式(例如GCM)应优先于PKCS#1填充(Sun提供程序的默认设置)和CBC模式加密。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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