Java Language
RSA-codering
Zoeken…
Een voorbeeld met een hybride cryptosysteem bestaande uit OAEP en GCM
In het volgende voorbeeld worden gegevens gecodeerd met behulp van een hybride cryptosysteem dat bestaat uit AES GCM en OAEP, met behulp van hun standaard parametergroottes en een AES-sleutelgrootte van 128 bits.
OAEP is minder kwetsbaar voor opvallende orakelaanvallen dan PKCS # 1 v1.5 padding. GCM is ook beschermd tegen opvallende orakelaanvallen.
Decodering kan worden uitgevoerd door eerst de lengte van de ingekapselde sleutel op te halen en vervolgens door de ingekapselde sleutel op te halen. De ingekapselde sleutel kan vervolgens worden gedecodeerd met behulp van de RSA private sleutel die een sleutelpaar vormt met de openbare sleutel. Daarna kan de AES / GCM-gecodeerde cijfertekst worden gedecodeerd naar de oorspronkelijke platte tekst.
Het protocol bestaat uit:
- een lengteveld voor de ingepakte sleutel (
RSAPrivateKey
mist een methodegetKeySize()
); - de ingepakte / ingekapselde sleutel, van dezelfde grootte als de RSA-sleutelgrootte in bytes;
- de GCM-cijfertekst en 128-bits authenticatietag (automatisch toegevoegd door Java).
Opmerkingen:
- Om deze code correct te gebruiken, moet u een RSA-sleutel van minimaal 2048 bits opgeven, groter is beter (maar langzamer, vooral tijdens het decoderen);
- Als u AES-256 wilt gebruiken, moet u eerst de onbeperkte cryptografiebeleidsbestanden installeren;
- In plaats daarvan kunt u in plaats daarvan een eigen indeling maken, zoals de Cryptographic Message Syntax (CMS / PKCS # 7) of PGP.
Dus hier is het voorbeeld:
/**
* 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();
}
Versleuteling is natuurlijk niet erg handig zonder ontsleuteling. Merk op dat dit minimale informatie zal retourneren als de decryptie mislukt.
/**
* 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();
}