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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从RSA编码的AES密钥生成AES密钥相关的知识,希望对你有一定的参考价值。

我想要做的是使用AES加密字符串,使用RSA加密AES密钥getEncoded()值,然后解密该AES getEncoded()值,以便获取我的原始字符串。公钥是从用户证书加载的,私钥是从文件加载的。代码如下。

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("
", "");

    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));
}

我一直得到的错误是:

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)

如果我不加密和解密secretKey.getEncoded()值,只使用没有RSA的AES它可以正常工作。如果我只使用公钥加密一些字符串,并使用私有密码解密它也可以使用RSA。我的问题是:“我如何使用RSA正确加密和解​​密secretKey.getEncoded()值,以便我可以正确加密和解​​密myString?”。

答案
new String(secretKey.getEncoded())

这不起作用,因为AES密钥包含随机字节,并不是每个字节都是字符代表。 Java中标准字符串转换的问题在于它丢弃了未知字符和字节,而不是在编码/解码期间生成异常。

RSA对字节进行操作,您不应将密钥转换为字符串,然后再将其转换回字节,因为转换可能是有损的(例如,丢弃32个字节中的4个)。

或者 - 甚至可能更好 - 你可能想尝试密码的包装模式。这应该与那里的一些硬件解决方案兼容。在这种情况下,你甚至不必打电话给getEncoded


OAEP加密和经过身份验证的加密模式(如GCM)应优先于PKCS#1填充(Sun提供商的默认设置)和CBC模式加密。

以上是关于从RSA编码的AES密钥生成AES密钥的主要内容,如果未能解决你的问题,请参考以下文章

加密解密

Android Okhttp/Retrofit网络请求加解密实现方案

Android接口安全 - RSA+AES混合加密方案

java的aes加密成多少位数

[转] AES,SHA1,DES,RSA,MD5区别

尝试使用 RSA 解密时密钥不存在