2017-12-18 15 views
0

Was ich versuche ist, eine Zeichenfolge mit AES zu verschlüsseln, den AES-Schlüssel getEncoded() -Wert mit RSA zu verschlüsseln, dann den AES getEncoded() -Wert zu entschlüsseln, damit ich mein Original bekomme Zeichenfolge. Der öffentliche Schlüssel wird aus dem Benutzerzertifikat und der private Schlüssel aus der Datei geladen. Der Code ist unten angegeben.Generieren AES-Schlüssel aus RSA-codierten AES-Schlüssel

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

Der Fehler, dass ich immer halten ist:

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) 

Wenn ich Verschlüsseln und Entschlüsseln nicht den secretKey.getEncoded() Wert, und verwenden Sie nur AES ohne RSA es richtig funktioniert. Auch wenn ich mit RSA arbeite, wenn ich nur eine Zeichenfolge mit einem öffentlichen Schlüssel verschlüssle und sie mit einem privaten entschlüssle, funktioniert es. Meine Frage wäre: "Wie kann ich den secretKey.getEncoded() Wert mit RSA richtig verschlüsseln und entschlüsseln, so dass ich myString richtig verschlüsseln und entschlüsseln kann?"

+0

Mögliches Duplikat von [AES-Schlüssel mit RSA Public Key verschlüsseln] (https://stackoverflow.com/questions/9658921/encrypting-aes-key-with-rs-a-public-key) – vinS

+1

@vinS es ist kein Duplikat , da die Antwort auf diese Frage war, Padding zu verwenden, die ich verwendet habe, aber ohne Erfolg –

Antwort

1

Dies funktioniert nicht, da AES-Schlüssel zufällige Bytes enthalten und nicht jedes Byte ein Zeichen darstellt. Das Problem mit der Standard-String-Konvertierung in Java ist, dass es unbekannte Zeichen und Bytes löscht, anstatt eine Ausnahme während der Kodierung/Dekodierung zu erzeugen.

RSA arbeitet mit Bytes. Sie sollten den Schlüssel nicht in eine Zeichenfolge und dann wieder in Byte umwandeln, da die Umwandlung verlustbehaftet sein kann (z. B. das Löschen von 4 der 32 Bytes).

Alternativ - und wahrscheinlich noch besser - möchten Sie vielleicht stattdessen die Verschlüsselungsmodi der Chiffre ausprobieren. Dies sollte mit einigen Hardware-Lösungen kompatibel sein. In diesem Fall müssen Sie nicht einmal getEncoded anrufen.


OAEP Verschlüsselung und authentifizierte Verschlüsselungsmodi wie GCM sollte über PKCS # 1 Padding (der Standard für die Sun-Provider) und CBC-Modus Verschlüsselung bevorzugt werden.

+0

Verwenden Sie nie 'neue String' oder' getBytes' ohne Angabe eines Zeichensatzes (aus 'StandardCharsets'). –

+0

Es sei denn, Sie müssen mit anderem plattformspezifischem Code kommunizieren, der natürlich nicht explizit einen Zeichensatz definiert. –