Skip to content

Commit

Permalink
Added support for encryption algorithms for symmetric keys (#17209)
Browse files Browse the repository at this point in the history
* Added support for encryption AES encryption algorithms.

* Added CryptographyOptions and ensured the initialization vector is populated before attempting to perform any local cryptography operations on symmetric keys.

* Added APIs that accept CryptographyOptions to CryptographyClient.

* Fixed Javadoc issues.

* Fixed checkstyle issues. Added samples.

* Added checkstyle exceptions.

* Fixed test and spotbugs issues.

* Applied PR feedback and added local tests.

* Made the EncryptOptions and DecryptOptions constructor package-private, as well as their children's, and made them have factory methods for creating the former to help with discoverability.

* Fixed build issues.

* Changed EncryptOptions and DecryptOptions to use a factory model.

* Added iv, additionalAuthenticatedDate and authenticationTag to EncryptResult.

* Made `plainText` and `cipherText` all lowercase.
  • Loading branch information
vcolin7 authored Nov 13, 2020
1 parent 6955838 commit b637d40
Show file tree
Hide file tree
Showing 50 changed files with 2,297 additions and 403 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@
<!-- InvalidKeyException is not a runtime exception, issue link: https://github.com/Azure/azure-sdk-for-java/issues/5178 -->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.keys.cryptography.AesCbc.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.keys.cryptography.AesCbcPad.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
files="com.azure.security.keyvault.keys.cryptography.AesGcm.java"/>

<!-- suppress the runtime exception in the KeyVaultClient class-->
<suppress checks="com.azure.tools.checkstyle.checks.ThrowFromClientLogger"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2390,7 +2390,7 @@
<Method name="getUserName" />
<Bug pattern="NM_CONFUSING" />
</Match>

<!-- Disabling false positives in azure-core -->
<!-- This Issue has been resolved as per spotbugs's recommended solution but the static checker still flags it, its a known issue with this rule. -->
<Match>
Expand Down Expand Up @@ -2421,4 +2421,13 @@
<Method name="~(get|post)" />
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
</Match>

<!-- Conflicting APIs have already been GA'd with incorrect capitalization -->
<Match>
<Or>
<Class name="com.azure.security.keyvault.keys.cryptography.DecryptOptions" />
<Class name="com.azure.security.keyvault.keys.cryptography.EncryptOptions" />
</Or>
<Bug pattern="NM_CONFUSING" />
</Match>
</FindBugsFilter>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

class Aes128CbcPad extends AesCbcPad {
private static final int KEY_SIZE = 128;
public static final String ALGORITHM_NAME = "A128CBCPAD";

Aes128CbcPad() {
super(ALGORITHM_NAME, KEY_SIZE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

class Aes128Gcm extends AesGcm {
private static final int KEY_SIZE = 128;
public static final String ALGORITHM_NAME = "A128GCM";

Aes128Gcm() {
super(ALGORITHM_NAME, KEY_SIZE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import java.security.Provider;
import java.util.Arrays;

class AesKw128 extends AesKw {
class Aes128Kw extends AesKw {

public static final String ALGORITHM_NAME = "A128KW";

static final int KEY_SIZE_IN_BYTES = 128 >> 3;

AesKw128() {
Aes128Kw() {
super(ALGORITHM_NAME);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

class Aes192CbcPad extends AesCbcPad {
private static final int KEY_SIZE = 192;
public static final String ALGORITHM_NAME = "A192CBCPAD";

Aes192CbcPad() {
super(ALGORITHM_NAME, KEY_SIZE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

class Aes192Gcm extends AesGcm {
private static final int KEY_SIZE = 192;
public static final String ALGORITHM_NAME = "A192GCM";

Aes192Gcm() {
super(ALGORITHM_NAME, KEY_SIZE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import java.security.Provider;
import java.util.Arrays;

class AesKw192 extends AesKw {
class Aes192Kw extends AesKw {

public static final String ALGORITHM_NAME = "A192KW";

static final int KEY_SIZE_IN_BYTES = 192 >> 3;

AesKw192() {
Aes192Kw() {
super(ALGORITHM_NAME);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

class Aes256CbcPad extends AesCbcPad {
private static final int KEY_SIZE = 256;
public static final String ALGORITHM_NAME = "A256CBCPAD";

Aes256CbcPad() {
super(ALGORITHM_NAME, KEY_SIZE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.keys.cryptography;

class Aes256Gcm extends AesGcm {
private static final int KEY_SIZE = 256;
public static final String ALGORITHM_NAME = "A256GCM";

Aes256Gcm() {
super(ALGORITHM_NAME, KEY_SIZE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import java.security.Provider;
import java.util.Arrays;

class AesKw256 extends AesKw {
class Aes256Kw extends AesKw {

public static final String ALGORITHM_NAME = "A256KW";

static final int KEY_SIZE_IN_BYTES = 256 >> 3;

AesKw256() {
Aes256Kw() {
super(ALGORITHM_NAME);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,30 @@
import java.util.Arrays;

abstract class AesCbc extends SymmetricEncryptionAlgorithm {

final int keySizeInBytes;
final int keySize;
static class AesCbcDecryptor implements ICryptoTransform {

protected AesCbc(String name, int size) {
super(name);

keySize = size;
keySizeInBytes = size >> 3;
}

static class AesCbcEncryptor implements ICryptoTransform {
private final Cipher cipher;

AesCbcDecryptor(byte[] key, byte[] iv, Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException {
AesCbcEncryptor(byte[] key, byte[] iv, Provider provider) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {

// Create the cipher using the Provider if specified
if (provider == null) {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} else {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", provider);
cipher = Cipher.getInstance("AES/CBC/NoPadding", provider);
}

cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
}

@Override
Expand All @@ -43,22 +48,20 @@ public byte[] doFinal(byte[] plaintext) throws IllegalBlockSizeException, BadPad
}
}

static class AesCbcEncryptor implements ICryptoTransform {

static class AesCbcDecryptor implements ICryptoTransform {
private final Cipher cipher;

AesCbcEncryptor(byte[] key, byte[] iv, Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException {
AesCbcDecryptor(byte[] key, byte[] iv, Provider provider) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {

// Create the cipher using the Provider if specified
if (provider == null) {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} else {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", provider);
cipher = Cipher.getInstance("AES/CBC/NoPadding", provider);
}

cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
}

@Override
Expand All @@ -67,56 +70,45 @@ public byte[] doFinal(byte[] plaintext) throws IllegalBlockSizeException, BadPad
}
}

protected AesCbc(String name, int size) {
super(name);
keySize = size;
keySizeInBytes = size >> 3;
}

@Override
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] authenticationData)
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {

if (key == null || key.length < keySizeInBytes) {
throw new InvalidKeyException("key must be at least " + keySize + " bits in length");
}

return new AesCbcEncryptor(Arrays.copyOfRange(key, 0, keySizeInBytes), iv, null);
return createEncryptor(key, iv, additionalAuthenticatedData, null, null);
}

@Override
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] authenticationData, Provider provider)
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag, Provider provider)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {

if (key == null || key.length < keySizeInBytes) {
throw new InvalidKeyException("key must be at least " + keySize + " bits in length");
throw new InvalidKeyException("Key must be at least " + keySize + " bits in length.");
}

return new AesCbcEncryptor(Arrays.copyOfRange(key, 0, keySizeInBytes), iv, provider);
}

@Override
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] authenticationData, byte[] authenticationTag)
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {

if (key == null || key.length < keySizeInBytes) {
throw new InvalidKeyException("key must be at least " + keySize + " bits in length");
}

return new AesCbcDecryptor(Arrays.copyOfRange(key, 0, keySizeInBytes), iv, null);
return createDecryptor(key, iv, additionalAuthenticatedData, authenticationTag, null);
}

@Override
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] authenticationData, byte[] authenticationTag,
Provider provider)
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag, Provider provider)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {

if (key == null || key.length < keySizeInBytes) {
throw new InvalidKeyException("key must be at least " + keySize + " bits in length");
throw new InvalidKeyException("Key must be at least " + keySize + " bits in length.");
}

return new AesCbcDecryptor(Arrays.copyOfRange(key, 0, keySizeInBytes), iv, provider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,17 @@ protected AesCbcHmacSha2(String name) {
}

@Override
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] authenticationData, byte[] authenticationTag)
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
return createDecryptor(key, iv, authenticationData, authenticationTag, null);

return createDecryptor(key, iv, additionalAuthenticatedData, authenticationTag, null);
}

@Override
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] authenticationData, byte[] authenticationTag,
Provider provider)
public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag, Provider provider)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
if (key == null) {
Expand All @@ -207,7 +209,7 @@ public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] authentica
throw logger.logExceptionAsWarning(new IllegalArgumentException("No initialization vector"));
}

if (authenticationData == null) {
if (additionalAuthenticatedData == null) {
throw logger.logExceptionAsWarning(new IllegalArgumentException("No authentication data"));
}

Expand All @@ -216,18 +218,21 @@ public ICryptoTransform createDecryptor(byte[] key, byte[] iv, byte[] authentica
}

// Create the Decryptor
return new AesCbcHmacSha2Decryptor(getName(), key, iv, authenticationData, authenticationTag, provider);
return new AesCbcHmacSha2Decryptor(getName(), key, iv, additionalAuthenticatedData, authenticationTag, provider);
}

@Override
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] authenticationData)
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
return createEncryptor(key, iv, authenticationData, null);

return createEncryptor(key, iv, additionalAuthenticatedData, null, null);
}

@Override
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] authenticationData, Provider provider)
public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] additionalAuthenticatedData,
byte[] authenticationTag, Provider provider)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {

Expand All @@ -239,11 +244,11 @@ public ICryptoTransform createEncryptor(byte[] key, byte[] iv, byte[] authentica
throw logger.logExceptionAsError(new IllegalArgumentException("No initialization vector"));
}

if (authenticationData == null) {
if (additionalAuthenticatedData == null) {
throw logger.logExceptionAsError(new IllegalArgumentException("No authentication data"));
}

// Create the Encryptor
return new AesCbcHmacSha2Encryptor(getName(), key, iv, authenticationData, provider);
return new AesCbcHmacSha2Encryptor(getName(), key, iv, additionalAuthenticatedData, provider);
}
}
Loading

0 comments on commit b637d40

Please sign in to comment.