Java Language
RSA-Verschlüsselung
Suche…
Ein Beispiel für ein Hybrid-Kryptosystem bestehend aus OAEP und GCM
Im folgenden Beispiel werden Daten mithilfe eines Hybrid-Kryptosystems bestehend aus AES GCM und OAEP unter Verwendung ihrer Standardparametergrößen und einer AES-Schlüsselgröße von 128 Bit verschlüsselt.
OAEP ist weniger anfällig für Padding-Orakel-Angriffe als PKCS # 1 v1.5-Padding. GCM ist auch vor Auffüllangriffen geschützt.
Die Entschlüsselung kann durchgeführt werden, indem zuerst die Länge des eingekapselten Schlüssels und dann der gekapselte Schlüssel abgerufen wird. Der gekapselte Schlüssel kann dann mit dem privaten RSA-Schlüssel entschlüsselt werden, der ein Schlüsselpaar mit dem öffentlichen Schlüssel bildet. Danach kann der AES / GCM-verschlüsselte Chiffretext zum ursprünglichen Klartext entschlüsselt werden.
Das Protokoll besteht aus:
- ein Längenfeld für den
RSAPrivateKey
Schlüssel (RSAPrivateKey
vermisst einegetKeySize()
-Methode); - der umwickelte / gekapselte Schlüssel, der die gleiche Größe wie die RSA-Schlüsselgröße in Bytes hat;
- der GCM-Chiffretext und das 128-Bit-Authentifizierungs-Tag (automatisch von Java hinzugefügt).
Anmerkungen:
- Um diesen Code korrekt zu verwenden, sollten Sie einen RSA-Schlüssel mit mindestens 2048 Bit angeben. Größer ist besser (aber langsamer, insbesondere während der Entschlüsselung).
- Um AES-256 zu verwenden, sollten Sie zunächst die unlimitierten Kryptographiedateien installieren.
- Anstatt ein eigenes Protokoll zu erstellen, sollten Sie stattdessen ein Containerformat verwenden, z. B. die Cryptographic Message Syntax (CMS / PKCS # 7) oder PGP.
Hier ist das Beispiel:
/**
* Encrypts the data using a hybrid crypto-system which uses GCM to encrypt the data and OAEP to encrypt the AES key.
* The key size of the AES encryption will be 128 bit.
* All the default parameter choices are used for OAEP and GCM.
*
* @param publicKey the RSA public key used to wrap the AES key
* @param plaintext the plaintext to be encrypted, not altered
* @return the ciphertext
* @throws InvalidKeyException if the key is not an RSA public key
* @throws NullPointerException if the plaintext is null
*/
public static byte[] encryptData(PublicKey publicKey, byte[] plaintext)
throws InvalidKeyException, NullPointerException {
// --- create the RSA OAEP cipher ---
Cipher oaep;
try {
// SHA-1 is the default and not vulnerable in this setting
// use OAEPParameterSpec to configure more than just the hash
oaep = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(
"Runtime doesn't have support for RSA cipher (mandatory algorithm for runtimes)", e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException(
"Runtime doesn't have support for OAEP padding (present in the standard Java runtime sinze XX)", e);
}
oaep.init(Cipher.WRAP_MODE, publicKey);
// --- wrap the plaintext in a buffer
// will throw NullPointerException if plaintext is null
ByteBuffer plaintextBuffer = ByteBuffer.wrap(plaintext);
// --- generate a new AES secret key ---
KeyGenerator aesKeyGenerator;
try {
aesKeyGenerator = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(
"Runtime doesn't have support for AES key generator (mandatory algorithm for runtimes)", e);
}
// for AES-192 and 256 make sure you've got the rights (install the
// Unlimited Crypto Policy files)
aesKeyGenerator.init(128);
SecretKey aesKey = aesKeyGenerator.generateKey();
// --- wrap the new AES secret key ---
byte[] wrappedKey;
try {
wrappedKey = oaep.wrap(aesKey);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException(
"AES key should always fit OAEP with normal sized RSA key", e);
}
// --- setup the AES GCM cipher mode ---
Cipher aesGCM;
try {
aesGCM = Cipher.getInstance("AES/GCM/Nopadding");
// we can get away with a zero nonce since the key is randomly generated
// 128 bits is the recommended (maximum) value for the tag size
// 12 bytes (96 bits) is the default nonce size for GCM mode encryption
GCMParameterSpec staticParameterSpec = new GCMParameterSpec(128, new byte[12]);
aesGCM.init(Cipher.ENCRYPT_MODE, aesKey, staticParameterSpec);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(
"Runtime doesn't have support for AES cipher (mandatory algorithm for runtimes)", e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException(
"Runtime doesn't have support for GCM (present in the standard Java runtime sinze XX)", e);
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(
"IvParameterSpec not accepted by this implementation of GCM", e);
}
// --- create a buffer of the right size for our own protocol ---
ByteBuffer ciphertextBuffer = ByteBuffer.allocate(
Short.BYTES
+ oaep.getOutputSize(128 / Byte.SIZE)
+ aesGCM.getOutputSize(plaintext.length));
// - element 1: make sure that we know the size of the wrapped key
ciphertextBuffer.putShort((short) wrappedKey.length);
// - element 2: put in the wrapped key
ciphertextBuffer.put(wrappedKey);
// - element 3: GCM encrypt into buffer
try {
aesGCM.doFinal(plaintextBuffer, ciphertextBuffer);
} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException("Cryptographic exception, AES/GCM encryption should not fail here", e);
}
return ciphertextBuffer.array();
}
Natürlich ist Verschlüsselung ohne Entschlüsselung nicht sehr nützlich. Beachten Sie, dass dies zu minimalen Informationen führt, wenn die Entschlüsselung fehlschlägt.
/**
* Decrypts the data using a hybrid crypto-system which uses GCM to encrypt
* the data and OAEP to encrypt the AES key. All the default parameter
* choices are used for OAEP and GCM.
*
* @param privateKey
* the RSA private key used to unwrap the AES key
* @param ciphertext
* the ciphertext to be encrypted, not altered
* @return the plaintext
* @throws InvalidKeyException
* if the key is not an RSA private key
* @throws NullPointerException
* if the ciphertext is null
* @throws IllegalArgumentException
* with the message "Invalid ciphertext" if the ciphertext is invalid (minimize information leakage)
*/
public static byte[] decryptData(PrivateKey privateKey, byte[] ciphertext)
throws InvalidKeyException, NullPointerException {
// --- create the RSA OAEP cipher ---
Cipher oaep;
try {
// SHA-1 is the default and not vulnerable in this setting
// use OAEPParameterSpec to configure more than just the hash
oaep = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(
"Runtime doesn't have support for RSA cipher (mandatory algorithm for runtimes)",
e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException(
"Runtime doesn't have support for OAEP padding (present in the standard Java runtime sinze XX)",
e);
}
oaep.init(Cipher.UNWRAP_MODE, privateKey);
// --- wrap the ciphertext in a buffer
// will throw NullPointerException if ciphertext is null
ByteBuffer ciphertextBuffer = ByteBuffer.wrap(ciphertext);
// sanity check #1
if (ciphertextBuffer.remaining() < 2) {
throw new IllegalArgumentException("Invalid ciphertext");
}
// - element 1: the length of the encapsulated key
int wrappedKeySize = ciphertextBuffer.getShort() & 0xFFFF;
// sanity check #2
if (ciphertextBuffer.remaining() < wrappedKeySize + 128 / Byte.SIZE) {
throw new IllegalArgumentException("Invalid ciphertext");
}
// --- unwrap the AES secret key ---
byte[] wrappedKey = new byte[wrappedKeySize];
// - element 2: the encapsulated key
ciphertextBuffer.get(wrappedKey);
SecretKey aesKey;
try {
aesKey = (SecretKey) oaep.unwrap(wrappedKey, "AES",
Cipher.SECRET_KEY);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(
"Runtime doesn't have support for AES cipher (mandatory algorithm for runtimes)",
e);
} catch (InvalidKeyException e) {
throw new RuntimeException(
"Invalid ciphertext");
}
// --- setup the AES GCM cipher mode ---
Cipher aesGCM;
try {
aesGCM = Cipher.getInstance("AES/GCM/Nopadding");
// we can get away with a zero nonce since the key is randomly
// generated
// 128 bits is the recommended (maximum) value for the tag size
// 12 bytes (96 bits) is the default nonce size for GCM mode
// encryption
GCMParameterSpec staticParameterSpec = new GCMParameterSpec(128,
new byte[12]);
aesGCM.init(Cipher.DECRYPT_MODE, aesKey, staticParameterSpec);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(
"Runtime doesn't have support for AES cipher (mandatory algorithm for runtimes)",
e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException(
"Runtime doesn't have support for GCM (present in the standard Java runtime sinze XX)",
e);
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException(
"IvParameterSpec not accepted by this implementation of GCM",
e);
}
// --- create a buffer of the right size for our own protocol ---
ByteBuffer plaintextBuffer = ByteBuffer.allocate(aesGCM
.getOutputSize(ciphertextBuffer.remaining()));
// - element 3: GCM ciphertext
try {
aesGCM.doFinal(ciphertextBuffer, plaintextBuffer);
} catch (ShortBufferException | IllegalBlockSizeException
| BadPaddingException e) {
throw new RuntimeException(
"Invalid ciphertext");
}
return plaintextBuffer.array();
}