2017-12-18 331 views
0

我想要做的是使用AES加密字符串,使用RSA加密AES密鑰getEncoded()的值,然後解密該AES getEncoded()值,以便獲得我的原始串。公鑰從用戶證書中加載,私鑰從文件中加載。代碼如下。從RSA編碼的AES密鑰生成AES密鑰

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

,我不斷收到的錯誤是:

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()值,只需使用AES沒有RSA能夠正常工作。同樣與RSA合作,如果我只用一個公鑰加密一些字符串,並用私有密鑰解密它就行。我的問題是:「如何使用RSA正確加密和解​​密secretKey.getEncoded()值,以便我可以正確加密和解​​密myString?」。

+0

[使用RSA公共密鑰加密AES密鑰]的可能副本(https://stackoverflow.com/questions/9658921/encrypting-aes-key-with-rsa-public-key) – vinS

+1

@vinS它不是重複的,因爲這個問題的答案是使用填充,我已經使用但沒有效果。 –

回答

1
new String(secretKey.getEncoded()) 

這不會工作,因爲AES密鑰包含隨機字節,並不是每個字節都是字符代表。 Java中標準字符串轉換的問題是它會丟棄未知字符和字節,而不是在編碼/解碼期間生成異常。

RSA對字節進行操作,不應將密鑰轉換爲字符串,然後再轉換回字節,因爲轉換可能是有損的(例如,丟失32字節中的4個)。

另外 - 也許更好 - 你可能想嘗試密碼的包裝模式。這應該與那裏的一些硬件解決方案兼容。在這種情況下,你甚至不需要撥打getEncoded


OAEP加密和認證的加密模式,例如GCM應在PKCS#1填充(默認爲Sun提供者)和CBC模式的加密是優選的。

+0

永遠不要使用'new String'或'getBytes'而不指定字符集(來自'StandardCharsets')。 –

+0

除非您需要與其他未明確定義字符集的平臺特定代碼進行通信,當然。 –