All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.azure.security.keyvault.keys.cryptography.implementation.AesKeyCryptographyClient Maven / Gradle / Ivy

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

import com.azure.core.util.Context;
import com.azure.security.keyvault.keys.cryptography.models.DecryptParameters;
import com.azure.security.keyvault.keys.cryptography.models.DecryptResult;
import com.azure.security.keyvault.keys.cryptography.models.EncryptParameters;
import com.azure.security.keyvault.keys.cryptography.models.EncryptResult;
import com.azure.security.keyvault.keys.cryptography.models.EncryptionAlgorithm;
import com.azure.security.keyvault.keys.cryptography.models.KeyWrapAlgorithm;
import com.azure.security.keyvault.keys.cryptography.models.SignResult;
import com.azure.security.keyvault.keys.cryptography.models.SignatureAlgorithm;
import com.azure.security.keyvault.keys.cryptography.models.UnwrapResult;
import com.azure.security.keyvault.keys.cryptography.models.VerifyResult;
import com.azure.security.keyvault.keys.cryptography.models.WrapResult;
import com.azure.security.keyvault.keys.models.JsonWebKey;
import com.azure.security.keyvault.keys.models.KeyOperation;
import reactor.core.publisher.Mono;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Objects;

import static com.azure.security.keyvault.keys.cryptography.implementation.CryptographyUtils.verifyKeyPermissions;

class AesKeyCryptographyClient extends LocalKeyCryptographyClient {
    private final byte[] aesKey;

    static final int AES_BLOCK_SIZE = 16;

    AesKeyCryptographyClient(JsonWebKey jsonWebKey, CryptographyClientImpl implClient) {
        super(jsonWebKey, implClient);

        aesKey = jsonWebKey.toAes().getEncoded();

        if (aesKey == null || aesKey.length == 0) {
            throw new IllegalArgumentException("The provided JSON Web Key cannot be null or empty.");
        }
    }

    @Override
    public Mono encryptAsync(EncryptionAlgorithm algorithm, byte[] plaintext, Context context) {
        Objects.requireNonNull(algorithm, "Encryption algorithm cannot be null.");
        Objects.requireNonNull(plaintext, "Plaintext cannot be null.");

        try {
            return encryptInternalAsync(algorithm, plaintext, null, null, context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public EncryptResult encrypt(EncryptionAlgorithm algorithm, byte[] plaintext, Context context) {
        Objects.requireNonNull(algorithm, "Encryption algorithm cannot be null.");
        Objects.requireNonNull(plaintext, "Plaintext cannot be null.");

        try {
            return encryptInternal(algorithm, plaintext, null, null, context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Mono encryptAsync(EncryptParameters encryptParameters, Context context) {
        Objects.requireNonNull(encryptParameters, "Encrypt parameters cannot be null.");

        try {
            return encryptInternalAsync(encryptParameters.getAlgorithm(), encryptParameters.getPlainText(),
                encryptParameters.getIv(), encryptParameters.getAdditionalAuthenticatedData(), context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public EncryptResult encrypt(EncryptParameters encryptParameters, Context context) {
        Objects.requireNonNull(encryptParameters, "Encrypt parameters cannot be null.");

        try {
            return encryptInternal(encryptParameters.getAlgorithm(), encryptParameters.getPlainText(),
                encryptParameters.getIv(), encryptParameters.getAdditionalAuthenticatedData(), context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Mono encryptInternalAsync(EncryptionAlgorithm algorithm, byte[] plaintext, byte[] iv,
                                                     byte[] additionalAuthenticatedData, Context context) throws NoSuchAlgorithmException {
        // Interpret the algorithm
        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof SymmetricEncryptionAlgorithm)) {
            if (implClient != null) {
                return implClient.encryptAsync(algorithm, plaintext, context);
            }

            throw new NoSuchAlgorithmException(algorithm.toString());
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.ENCRYPT);
        validateEncryptionAlgorithm(algorithm);

        SymmetricEncryptionAlgorithm symmetricEncryptionAlgorithm = (SymmetricEncryptionAlgorithm) baseAlgorithm;

        final byte[] finalIv;

        if (iv == null) {
            if (isAes(algorithm)) {
                try {
                    finalIv = generateIv(AES_BLOCK_SIZE);
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("Could not generate iv for this local operation.", e);
                }
            } else {
                throw new IllegalArgumentException("Encryption algorithm provided is not supported: " + algorithm);
            }
        } else {
            finalIv = iv;
        }

        return Mono.fromCallable(() -> {
            byte[] ciphertext =
                symmetricEncryptionAlgorithm.createEncryptor(aesKey, finalIv, additionalAuthenticatedData, null)
                    .doFinal(plaintext);

            return new EncryptResult(ciphertext, algorithm, jsonWebKey.getId(), finalIv, null,
                additionalAuthenticatedData);
        });
    }

    private EncryptResult encryptInternal(EncryptionAlgorithm algorithm, byte[] plaintext, byte[] iv,
                                          byte[] additionalAuthenticatedData, Context context)
        throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException,
        InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {

        // Interpret the algorithm
        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof SymmetricEncryptionAlgorithm)) {
            if (implClient != null) {
                return implClient.encrypt(algorithm, plaintext, context);
            }

            throw new NoSuchAlgorithmException(algorithm.toString());
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.ENCRYPT);
        validateEncryptionAlgorithm(algorithm);

        SymmetricEncryptionAlgorithm symmetricEncryptionAlgorithm = (SymmetricEncryptionAlgorithm) baseAlgorithm;

        if (iv == null) {
            if (isAes(algorithm)) {
                try {
                    iv = generateIv(AES_BLOCK_SIZE);
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("Could not generate iv for this local operation.", e);
                }
            } else {
                throw new IllegalArgumentException("Encryption algorithm provided is not supported: " + algorithm);
            }
        }

        byte[] ciphertext = symmetricEncryptionAlgorithm.createEncryptor(aesKey, iv, additionalAuthenticatedData, null)
            .doFinal(plaintext);

        return new EncryptResult(ciphertext, algorithm, jsonWebKey.getId(), iv, null,
            additionalAuthenticatedData);
    }

    @Override
    public Mono decryptAsync(EncryptionAlgorithm algorithm, byte[] ciphertext, Context context) {
        Objects.requireNonNull(algorithm, "Encryption algorithm cannot be null.");
        Objects.requireNonNull(ciphertext, "Ciphertext cannot be null.");

        try {
            return decryptInternalAsync(algorithm, ciphertext, null, null, null, context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public DecryptResult decrypt(EncryptionAlgorithm algorithm, byte[] ciphertext, Context context) {
        Objects.requireNonNull(algorithm, "Encryption algorithm cannot be null.");
        Objects.requireNonNull(ciphertext, "Ciphertext cannot be null.");

        try {
            return decryptInternal(algorithm, ciphertext, null, null, null, context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Mono decryptAsync(DecryptParameters decryptParameters, Context context) {
        Objects.requireNonNull(decryptParameters, "Decrypt parameters cannot be null.");

        try {
            return decryptInternalAsync(decryptParameters.getAlgorithm(), decryptParameters.getCipherText(),
                decryptParameters.getIv(), decryptParameters.getAdditionalAuthenticatedData(),
                decryptParameters.getAuthenticationTag(), context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public DecryptResult decrypt(DecryptParameters decryptParameters, Context context) {
        Objects.requireNonNull(decryptParameters, "Decrypt parameters cannot be null.");

        try {
            return decryptInternal(decryptParameters.getAlgorithm(), decryptParameters.getCipherText(),
                decryptParameters.getIv(), decryptParameters.getAdditionalAuthenticatedData(),
                decryptParameters.getAuthenticationTag(), context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Mono decryptInternalAsync(EncryptionAlgorithm algorithm, byte[] ciphertext, byte[] iv,
                                                     byte[] additionalAuthenticatedData, byte[] authenticationTag,
                                                     Context context) throws NoSuchAlgorithmException {
        // Interpret the algorithm
        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof SymmetricEncryptionAlgorithm)) {
            if (implClient != null) {
                return implClient.decryptAsync(algorithm, ciphertext, context);
            }

            throw new NoSuchAlgorithmException(algorithm.toString());
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.DECRYPT);
        validateEncryptionAlgorithm(algorithm);

        SymmetricEncryptionAlgorithm symmetricEncryptionAlgorithm = (SymmetricEncryptionAlgorithm) baseAlgorithm;

        Objects.requireNonNull(iv, "'iv' cannot be null in local decryption operations.");

        return Mono.fromCallable(() -> {
            byte[] plaintext =
                symmetricEncryptionAlgorithm.createDecryptor(aesKey, iv, additionalAuthenticatedData, authenticationTag)
                    .doFinal(ciphertext);

            return new DecryptResult(plaintext, algorithm, jsonWebKey.getId());
        });
    }

    private DecryptResult decryptInternal(EncryptionAlgorithm algorithm, byte[] ciphertext, byte[] iv,
                                          byte[] additionalAuthenticatedData, byte[] authenticationTag,
                                          Context context)
        throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException,
        InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {

        // Interpret the algorithm
        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof SymmetricEncryptionAlgorithm)) {
            if (implClient != null) {
                return implClient.decrypt(algorithm, ciphertext, context);
            }

            throw new NoSuchAlgorithmException(algorithm.toString());
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.DECRYPT);
        validateEncryptionAlgorithm(algorithm);

        SymmetricEncryptionAlgorithm symmetricEncryptionAlgorithm = (SymmetricEncryptionAlgorithm) baseAlgorithm;

        Objects.requireNonNull(iv, "'iv' cannot be null in local decryption operations.");

        byte[] plaintext =
            symmetricEncryptionAlgorithm.createDecryptor(aesKey, iv, additionalAuthenticatedData, authenticationTag)
                .doFinal(ciphertext);

        return new DecryptResult(plaintext, algorithm, jsonWebKey.getId());
    }

    private static void validateEncryptionAlgorithm(EncryptionAlgorithm algorithm) {
        if (isGcm(algorithm)) {
            throw new UnsupportedOperationException("AES-GCM is not supported for local cryptography operations.");
        }

        if (!isAes(algorithm)) {
            throw new IllegalArgumentException("Encryption algorithm provided is not supported: " + algorithm);
        }
    }

    @Override
    public Mono signAsync(SignatureAlgorithm algorithm, byte[] digest, Context context) {
        throw new UnsupportedOperationException("The sign operation not supported for OCT/symmetric keys.");
    }

    @Override
    public SignResult sign(SignatureAlgorithm algorithm, byte[] digest, Context context) {
        throw new UnsupportedOperationException("The sign operation not supported for OCT/symmetric keys.");
    }

    @Override
    public Mono verifyAsync(SignatureAlgorithm algorithm, byte[] digest, byte[] signature,
                                          Context context) {
        throw new UnsupportedOperationException("The verify operation is not supported for OCT/symmetric keys.");
    }

    public VerifyResult verify(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, Context context) {
        throw new UnsupportedOperationException("The verify operation is not supported for OCT/symmetric keys.");
    }

    @Override
    public Mono wrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] keyToWrap, Context context) {
        Objects.requireNonNull(algorithm, "Key wrap algorithm cannot be null.");
        Objects.requireNonNull(keyToWrap, "Key content to be wrapped cannot be null.");

        // Interpret the algorithm
        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof LocalKeyWrapAlgorithm)) {
            if (implClient != null) {
                return implClient.wrapKeyAsync(algorithm, keyToWrap, context);
            }

            throw new RuntimeException(new NoSuchAlgorithmException(algorithm.toString()));
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.WRAP_KEY);

        LocalKeyWrapAlgorithm localKeyWrapAlgorithm = (LocalKeyWrapAlgorithm) baseAlgorithm;

        return Mono.fromCallable(() -> {
            byte[] encrypted = localKeyWrapAlgorithm.createEncryptor(aesKey, null, null)
                .doFinal(keyToWrap);

            return new WrapResult(encrypted, algorithm, jsonWebKey.getId());
        });
    }

    @Override
    public WrapResult wrapKey(KeyWrapAlgorithm algorithm, byte[] keyToWrap, Context context) {
        Objects.requireNonNull(algorithm, "Key wrap algorithm cannot be null.");
        Objects.requireNonNull(keyToWrap, "Key content to be wrapped cannot be null.");

        // Interpret the algorithm
        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof LocalKeyWrapAlgorithm)) {
            if (implClient != null) {
                return implClient.wrapKey(algorithm, keyToWrap, context);
            }

            throw new RuntimeException(new NoSuchAlgorithmException(algorithm.toString()));
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.WRAP_KEY);

        LocalKeyWrapAlgorithm localKeyWrapAlgorithm = (LocalKeyWrapAlgorithm) baseAlgorithm;

        ICryptoTransform transform;

        try {
            transform = localKeyWrapAlgorithm.createEncryptor(aesKey, null, null);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }

        byte[] encrypted;

        try {
            encrypted = transform.doFinal(keyToWrap);
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                throw new RuntimeException(e);
            }
        }

        return new WrapResult(encrypted, algorithm, jsonWebKey.getId());
    }

    @Override
    public Mono unwrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] encryptedKey, Context context) {
        Objects.requireNonNull(algorithm, "Key wrap algorithm cannot be null.");
        Objects.requireNonNull(encryptedKey, "Encrypted key content to be unwrapped cannot be null.");

        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof LocalKeyWrapAlgorithm)) {
            if (implClient != null) {
                return implClient.unwrapKeyAsync(algorithm, encryptedKey, context);
            }

            throw new RuntimeException(new NoSuchAlgorithmException(algorithm.toString()));
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.UNWRAP_KEY);

        LocalKeyWrapAlgorithm localKeyWrapAlgorithm = (LocalKeyWrapAlgorithm) baseAlgorithm;

        return Mono.fromCallable(() -> {
            byte[] decrypted = localKeyWrapAlgorithm.createDecryptor(aesKey, null, null).doFinal(encryptedKey);

            return new UnwrapResult(decrypted, algorithm, jsonWebKey.getId());
        });
    }

    @Override
    public UnwrapResult unwrapKey(KeyWrapAlgorithm algorithm, byte[] encryptedKey, Context context) {
        Objects.requireNonNull(algorithm, "Key wrap algorithm cannot be null.");
        Objects.requireNonNull(encryptedKey, "Encrypted key content to be unwrapped cannot be null.");

        verifyKeyPermissions(jsonWebKey, KeyOperation.UNWRAP_KEY);

        Algorithm baseAlgorithm = AlgorithmResolver.DEFAULT.get(algorithm.toString());

        if (!(baseAlgorithm instanceof LocalKeyWrapAlgorithm)) {
            if (implClient != null) {
                return implClient.unwrapKey(algorithm, encryptedKey, context);
            }

            throw new RuntimeException(new NoSuchAlgorithmException(algorithm.toString()));
        }

        verifyKeyPermissions(jsonWebKey, KeyOperation.UNWRAP_KEY);

        LocalKeyWrapAlgorithm localKeyWrapAlgorithm = (LocalKeyWrapAlgorithm) baseAlgorithm;

        try {
            byte[] decrypted = localKeyWrapAlgorithm.createDecryptor(aesKey, null, null).doFinal(encryptedKey);

            return new UnwrapResult(decrypted, algorithm, jsonWebKey.getId());
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Mono signDataAsync(SignatureAlgorithm algorithm, byte[] data, Context context) {
        return signAsync(algorithm, data, context);
    }

    @Override
    public SignResult signData(SignatureAlgorithm algorithm, byte[] data, Context context) {
        return sign(algorithm, data, context);
    }

    @Override
    public Mono verifyDataAsync(SignatureAlgorithm algorithm, byte[] data, byte[] signature,
                                              Context context) {
        return verifyAsync(algorithm, data, signature, context);
    }

    public VerifyResult verifyData(SignatureAlgorithm algorithm, byte[] data, byte[] signature, Context context) {
        return verify(algorithm, data, signature, context);
    }

    private static byte[] generateIv(int sizeInBytes) throws NoSuchAlgorithmException {
        SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
        byte[] iv = new byte[sizeInBytes];

        randomSecureRandom.nextBytes(iv);

        return iv;
    }

    private static boolean isAes(EncryptionAlgorithm encryptionAlgorithm) {
        return (encryptionAlgorithm == EncryptionAlgorithm.A128CBC
            || encryptionAlgorithm == EncryptionAlgorithm.A192CBC
            || encryptionAlgorithm == EncryptionAlgorithm.A256CBC
            || encryptionAlgorithm == EncryptionAlgorithm.A128CBCPAD
            || encryptionAlgorithm == EncryptionAlgorithm.A192CBCPAD
            || encryptionAlgorithm == EncryptionAlgorithm.A256CBCPAD);
    }

    private static boolean isGcm(EncryptionAlgorithm encryptionAlgorithm) {
        return (encryptionAlgorithm == EncryptionAlgorithm.A128GCM
            || encryptionAlgorithm == EncryptionAlgorithm.A192GCM
            || encryptionAlgorithm == EncryptionAlgorithm.A256GCM);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy