CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-azure--azure-core

Azure Core provides shared primitives, abstractions, and helpers for modern Java Azure SDK client libraries

Pending
Overview
Eval results
Files

cryptography.mddocs/

Cryptography and Key Management

Interfaces for key encryption and cryptographic operations, providing abstractions for both synchronous and asynchronous key encryption scenarios with key resolution capabilities.

Capabilities

KeyEncryptionKey

Synchronous interface for key encryption and decryption operations.

/**
 * Synchronous key encryption key interface for cryptographic operations.
 */
interface KeyEncryptionKey {
    /**
     * Gets the identifier of the key encryption key.
     * @return The key identifier
     */
    String getKeyId();
    
    /**
     * Encrypts the specified plaintext.
     * @param algorithm The encryption algorithm to use
     * @param plaintext The plaintext to encrypt
     * @return The encrypted data (ciphertext)
     */
    byte[] wrapKey(String algorithm, byte[] plaintext);
    
    /**
     * Decrypts the specified ciphertext.
     * @param algorithm The decryption algorithm to use
     * @param ciphertext The ciphertext to decrypt
     * @return The decrypted data (plaintext)
     */
    byte[] unwrapKey(String algorithm, byte[] ciphertext);
}

AsyncKeyEncryptionKey

Asynchronous interface for key encryption and decryption operations using reactive patterns.

/**
 * Asynchronous key encryption key interface for cryptographic operations.
 */
interface AsyncKeyEncryptionKey {
    /**
     * Gets the identifier of the key encryption key.
     * @return A Mono containing the key identifier
     */
    Mono<String> getKeyId();
    
    /**
     * Encrypts the specified plaintext asynchronously.
     * @param algorithm The encryption algorithm to use
     * @param plaintext The plaintext to encrypt
     * @return A Mono containing the encrypted data (ciphertext)
     */
    Mono<byte[]> wrapKey(String algorithm, byte[] plaintext);
    
    /**
     * Decrypts the specified ciphertext asynchronously.
     * @param algorithm The decryption algorithm to use  
     * @param ciphertext The ciphertext to decrypt
     * @return A Mono containing the decrypted data (plaintext)
     */
    Mono<byte[]> unwrapKey(String algorithm, byte[] ciphertext);
}

KeyEncryptionKeyResolver

Synchronous resolver interface for retrieving key encryption keys by identifier.

/**
 * Synchronous resolver interface for key encryption keys.
 */
interface KeyEncryptionKeyResolver {
    /**
     * Resolves a key encryption key by its identifier.
     * @param keyId The identifier of the key to resolve
     * @return The resolved KeyEncryptionKey, or null if not found
     */
    KeyEncryptionKey resolveKey(String keyId);
}

AsyncKeyEncryptionKeyResolver

Asynchronous resolver interface for retrieving key encryption keys by identifier using reactive patterns.

/**
 * Asynchronous resolver interface for key encryption keys.
 */
interface AsyncKeyEncryptionKeyResolver {
    /**
     * Resolves a key encryption key by its identifier asynchronously.
     * @param keyId The identifier of the key to resolve
     * @return A Mono containing the resolved AsyncKeyEncryptionKey, or empty if not found
     */
    Mono<AsyncKeyEncryptionKey> resolveKey(String keyId);
}

Usage Examples

Implementing KeyEncryptionKey

import com.azure.core.cryptography.*;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;

class AesKeyEncryptionKey implements KeyEncryptionKey {
    private final String keyId;
    private final Key key;
    
    public AesKeyEncryptionKey(String keyId, byte[] keyBytes) {
        this.keyId = keyId;
        this.key = new SecretKeySpec(keyBytes, "AES");
    }
    
    @Override
    public String getKeyId() {
        return keyId;
    }
    
    @Override
    public byte[] wrapKey(String algorithm, byte[] plaintext) {
        try {
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(plaintext);
        } catch (Exception e) {
            throw new RuntimeException("Encryption failed", e);
        }
    }
    
    @Override
    public byte[] unwrapKey(String algorithm, byte[] ciphertext) {
        try {
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(ciphertext);
        } catch (Exception e) {
            throw new RuntimeException("Decryption failed", e);
        }
    }
}

Implementing AsyncKeyEncryptionKey

import com.azure.core.cryptography.*;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

class AsyncAesKeyEncryptionKey implements AsyncKeyEncryptionKey {
    private final KeyEncryptionKey syncKey;
    
    public AsyncAesKeyEncryptionKey(KeyEncryptionKey syncKey) {
        this.syncKey = syncKey;
    }
    
    @Override
    public Mono<String> getKeyId() {
        return Mono.fromCallable(syncKey::getKeyId)
                   .subscribeOn(Schedulers.boundedElastic());
    }
    
    @Override
    public Mono<byte[]> wrapKey(String algorithm, byte[] plaintext) {
        return Mono.fromCallable(() -> syncKey.wrapKey(algorithm, plaintext))
                   .subscribeOn(Schedulers.boundedElastic());
    }
    
    @Override
    public Mono<byte[]> unwrapKey(String algorithm, byte[] ciphertext) {
        return Mono.fromCallable(() -> syncKey.unwrapKey(algorithm, ciphertext))
                   .subscribeOn(Schedulers.boundedElastic());
    }
}

Implementing Key Resolvers

import com.azure.core.cryptography.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

class InMemoryKeyResolver implements KeyEncryptionKeyResolver {
    private final Map<String, KeyEncryptionKey> keys = new ConcurrentHashMap<>();
    
    public void addKey(String keyId, KeyEncryptionKey key) {
        keys.put(keyId, key);
    }
    
    @Override
    public KeyEncryptionKey resolveKey(String keyId) {
        return keys.get(keyId);
    }
}

class AsyncInMemoryKeyResolver implements AsyncKeyEncryptionKeyResolver {
    private final KeyEncryptionKeyResolver syncResolver;
    
    public AsyncInMemoryKeyResolver(KeyEncryptionKeyResolver syncResolver) {
        this.syncResolver = syncResolver;
    }
    
    @Override
    public Mono<AsyncKeyEncryptionKey> resolveKey(String keyId) {
        return Mono.fromCallable(() -> {
            KeyEncryptionKey syncKey = syncResolver.resolveKey(keyId);
            return syncKey != null ? new AsyncAesKeyEncryptionKey(syncKey) : null;
        }).subscribeOn(Schedulers.boundedElastic());
    }
}

Azure Key Vault Integration Example

import com.azure.core.cryptography.*;
import com.azure.security.keyvault.keys.cryptography.*;
import com.azure.identity.DefaultAzureCredentialBuilder;

// Example using Azure Key Vault for key encryption
class KeyVaultKeyEncryptionKey implements KeyEncryptionKey {
    private final CryptographyClient cryptoClient;
    private final String keyId;
    
    public KeyVaultKeyEncryptionKey(String keyVaultUrl, String keyName) {
        CryptographyClientBuilder builder = new CryptographyClientBuilder()
            .keyIdentifier(keyVaultUrl + "/keys/" + keyName)
            .credential(new DefaultAzureCredentialBuilder().build());
        
        this.cryptoClient = builder.buildClient();
        this.keyId = keyName;
    }
    
    @Override
    public String getKeyId() {
        return keyId;
    }
    
    @Override
    public byte[] wrapKey(String algorithm, byte[] plaintext) {
        EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);
        EncryptResult result = cryptoClient.encrypt(encryptAlg, plaintext);
        return result.getCipherText();
    }
    
    @Override
    public byte[] unwrapKey(String algorithm, byte[] ciphertext) {
        EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);
        DecryptResult result = cryptoClient.decrypt(encryptAlg, ciphertext);
        return result.getPlainText();
    }
}

// Async version for Key Vault
class AsyncKeyVaultKeyEncryptionKey implements AsyncKeyEncryptionKey {
    private final CryptographyAsyncClient cryptoClient;
    private final String keyId;
    
    public AsyncKeyVaultKeyEncryptionKey(String keyVaultUrl, String keyName) {
        CryptographyClientBuilder builder = new CryptographyClientBuilder()
            .keyIdentifier(keyVaultUrl + "/keys/" + keyName)
            .credential(new DefaultAzureCredentialBuilder().build());
        
        this.cryptoClient = builder.buildAsyncClient();
        this.keyId = keyName;
    }
    
    @Override
    public Mono<String> getKeyId() {
        return Mono.just(keyId);
    }
    
    @Override
    public Mono<byte[]> wrapKey(String algorithm, byte[] plaintext) {
        EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);
        return cryptoClient.encrypt(encryptAlg, plaintext)
                          .map(EncryptResult::getCipherText);
    }
    
    @Override
    public Mono<byte[]> unwrapKey(String algorithm, byte[] ciphertext) {
        EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);
        return cryptoClient.decrypt(encryptAlg, ciphertext)
                          .map(DecryptResult::getPlainText);
    }
}

Client-Side Encryption Pattern

import com.azure.core.cryptography.*;
import com.azure.core.util.BinaryData;

class EncryptedDataService {
    private final AsyncKeyEncryptionKey keyEncryptionKey;
    private final AsyncKeyEncryptionKeyResolver keyResolver;
    
    public EncryptedDataService(AsyncKeyEncryptionKey kek, AsyncKeyEncryptionKeyResolver resolver) {
        this.keyEncryptionKey = kek;
        this.keyResolver = resolver;
    }
    
    public Mono<EncryptedBlob> encryptData(BinaryData data) {
        // Generate a content encryption key (CEK)
        byte[] contentKey = generateRandomKey(32); // 256-bit key
        
        return keyEncryptionKey.getKeyId()
            .flatMap(keyId -> keyEncryptionKey.wrapKey("AES/GCM/NoPadding", contentKey)
                .map(encryptedKey -> {
                    // Encrypt the actual data with the CEK
                    byte[] encryptedData = encryptWithContentKey(contentKey, data.toBytes());
                    
                    return new EncryptedBlob(keyId, encryptedKey, encryptedData);
                }));
    }
    
    public Mono<BinaryData> decryptData(EncryptedBlob encryptedBlob) {
        return keyResolver.resolveKey(encryptedBlob.getKeyId())
            .switchIfEmpty(Mono.error(new IllegalArgumentException("Key not found: " + encryptedBlob.getKeyId())))
            .flatMap(kek -> kek.unwrapKey("AES/GCM/NoPadding", encryptedBlob.getEncryptedKey()))
            .map(contentKey -> {
                // Decrypt the data with the recovered content key
                byte[] decryptedData = decryptWithContentKey(contentKey, encryptedBlob.getEncryptedData());
                return BinaryData.fromBytes(decryptedData);
            });
    }
    
    private byte[] generateRandomKey(int size) {
        byte[] key = new byte[size];
        new java.security.SecureRandom().nextBytes(key);
        return key;
    }
    
    private byte[] encryptWithContentKey(byte[] key, byte[] data) {
        // Implementation of AES encryption
        // This is a simplified example - real implementation would handle IV, etc.
        try {
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException("Content encryption failed", e);
        }
    }
    
    private byte[] decryptWithContentKey(byte[] key, byte[] encryptedData) {
        // Implementation of AES decryption
        try {
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            return cipher.doFinal(encryptedData);
        } catch (Exception e) {
            throw new RuntimeException("Content decryption failed", e);
        }
    }
}

// Helper class for encrypted data
class EncryptedBlob {
    private final String keyId;
    private final byte[] encryptedKey;
    private final byte[] encryptedData;
    
    public EncryptedBlob(String keyId, byte[] encryptedKey, byte[] encryptedData) {
        this.keyId = keyId;
        this.encryptedKey = encryptedKey.clone();
        this.encryptedData = encryptedData.clone();
    }
    
    public String getKeyId() { return keyId; }
    public byte[] getEncryptedKey() { return encryptedKey.clone(); }
    public byte[] getEncryptedData() { return encryptedData.clone(); }
}

Multi-Key Encryption Service

import com.azure.core.cryptography.*;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

class MultiKeyEncryptionService {
    private final List<AsyncKeyEncryptionKey> availableKeys;
    private final AsyncKeyEncryptionKeyResolver keyResolver;
    
    public MultiKeyEncryptionService(List<AsyncKeyEncryptionKey> keys, AsyncKeyEncryptionKeyResolver resolver) {
        this.availableKeys = keys;
        this.keyResolver = resolver;
    }
    
    public Mono<EncryptedBlob> encryptWithRandomKey(BinaryData data) {
        // Select a random key for encryption
        int keyIndex = ThreadLocalRandom.current().nextInt(availableKeys.size());
        AsyncKeyEncryptionKey selectedKey = availableKeys.get(keyIndex);
        
        return encryptWithSpecificKey(selectedKey, data);
    }
    
    public Mono<EncryptedBlob> encryptWithSpecificKey(AsyncKeyEncryptionKey kek, BinaryData data) {
        byte[] contentKey = generateRandomKey(32);
        
        return kek.getKeyId()
            .flatMap(keyId -> kek.wrapKey("RSA-OAEP", contentKey)
                .map(encryptedKey -> {
                    byte[] encryptedData = encryptWithContentKey(contentKey, data.toBytes());
                    return new EncryptedBlob(keyId, encryptedKey, encryptedData);
                }));
    }
    
    public Flux<BinaryData> decryptMultiple(List<EncryptedBlob> encryptedBlobs) {
        return Flux.fromIterable(encryptedBlobs)
            .flatMap(blob -> keyResolver.resolveKey(blob.getKeyId())
                .flatMap(kek -> kek.unwrapKey("RSA-OAEP", blob.getEncryptedKey()))
                .map(contentKey -> {
                    byte[] decryptedData = decryptWithContentKey(contentKey, blob.getEncryptedData());
                    return BinaryData.fromBytes(decryptedData);
                })
                .onErrorResume(error -> {
                    // Log error and continue with next blob
                    System.err.println("Failed to decrypt blob with key " + blob.getKeyId() + ": " + error.getMessage());
                    return Mono.empty();
                }));
    }
    
    private byte[] generateRandomKey(int size) {
        byte[] key = new byte[size];
        new java.security.SecureRandom().nextBytes(key);
        return key;
    }
    
    // Content encryption methods (same as previous example)
    private byte[] encryptWithContentKey(byte[] key, byte[] data) { /* ... */ }
    private byte[] decryptWithContentKey(byte[] key, byte[] encryptedData) { /* ... */ }
}

Key Rotation Example

import com.azure.core.cryptography.*;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;

class RotatingKeyEncryptionService {
    private final AtomicReference<AsyncKeyEncryptionKey> currentKey = new AtomicReference<>();
    private final AsyncKeyEncryptionKeyResolver keyResolver;
    private final List<AsyncKeyEncryptionKey> keyRotationOrder;
    private volatile int currentKeyIndex = 0;
    
    public RotatingKeyEncryptionService(List<AsyncKeyEncryptionKey> keys, AsyncKeyEncryptionKeyResolver resolver) {
        this.keyRotationOrder = keys;
        this.keyResolver = resolver;
        this.currentKey.set(keys.get(0));
        
        // Start key rotation timer
        startKeyRotation();
    }
    
    public Mono<EncryptedBlob> encrypt(BinaryData data) {
        AsyncKeyEncryptionKey key = currentKey.get();
        return encryptWithKey(key, data);
    }
    
    public Mono<BinaryData> decrypt(EncryptedBlob encryptedBlob) {
        return keyResolver.resolveKey(encryptedBlob.getKeyId())
            .switchIfEmpty(Mono.error(new IllegalArgumentException("Key not found: " + encryptedBlob.getKeyId())))
            .flatMap(kek -> decryptWithKey(kek, encryptedBlob));
    }
    
    private void startKeyRotation() {
        // Rotate keys every hour (simplified example)
        Flux.interval(Duration.ofHours(1))
            .subscribe(tick -> rotateKey());
    }
    
    private void rotateKey() {
        currentKeyIndex = (currentKeyIndex + 1) % keyRotationOrder.size();
        currentKey.set(keyRotationOrder.get(currentKeyIndex));
        System.out.println("Rotated to key index: " + currentKeyIndex);
    }
    
    private Mono<EncryptedBlob> encryptWithKey(AsyncKeyEncryptionKey kek, BinaryData data) {
        // Implementation similar to previous examples
        return Mono.empty(); // Placeholder
    }
    
    private Mono<BinaryData> decryptWithKey(AsyncKeyEncryptionKey kek, EncryptedBlob blob) {
        // Implementation similar to previous examples  
        return Mono.empty(); // Placeholder
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-azure--azure-core

docs

annotations.md

authentication.md

client-traits.md

cryptography.md

exceptions.md

http-client.md

http-policies.md

index.md

models.md

serialization.md

utilities.md

tile.json