or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdauthentication.mdclient-traits.mdcryptography.mdexceptions.mdhttp-client.mdhttp-policies.mdindex.mdmodels.mdserialization.mdutilities.md

cryptography.mddocs/

0

# Cryptography and Key Management

1

2

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

3

4

## Capabilities

5

6

### KeyEncryptionKey

7

8

Synchronous interface for key encryption and decryption operations.

9

10

```java { .api }

11

/**

12

* Synchronous key encryption key interface for cryptographic operations.

13

*/

14

interface KeyEncryptionKey {

15

/**

16

* Gets the identifier of the key encryption key.

17

* @return The key identifier

18

*/

19

String getKeyId();

20

21

/**

22

* Encrypts the specified plaintext.

23

* @param algorithm The encryption algorithm to use

24

* @param plaintext The plaintext to encrypt

25

* @return The encrypted data (ciphertext)

26

*/

27

byte[] wrapKey(String algorithm, byte[] plaintext);

28

29

/**

30

* Decrypts the specified ciphertext.

31

* @param algorithm The decryption algorithm to use

32

* @param ciphertext The ciphertext to decrypt

33

* @return The decrypted data (plaintext)

34

*/

35

byte[] unwrapKey(String algorithm, byte[] ciphertext);

36

}

37

```

38

39

### AsyncKeyEncryptionKey

40

41

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

42

43

```java { .api }

44

/**

45

* Asynchronous key encryption key interface for cryptographic operations.

46

*/

47

interface AsyncKeyEncryptionKey {

48

/**

49

* Gets the identifier of the key encryption key.

50

* @return A Mono containing the key identifier

51

*/

52

Mono<String> getKeyId();

53

54

/**

55

* Encrypts the specified plaintext asynchronously.

56

* @param algorithm The encryption algorithm to use

57

* @param plaintext The plaintext to encrypt

58

* @return A Mono containing the encrypted data (ciphertext)

59

*/

60

Mono<byte[]> wrapKey(String algorithm, byte[] plaintext);

61

62

/**

63

* Decrypts the specified ciphertext asynchronously.

64

* @param algorithm The decryption algorithm to use

65

* @param ciphertext The ciphertext to decrypt

66

* @return A Mono containing the decrypted data (plaintext)

67

*/

68

Mono<byte[]> unwrapKey(String algorithm, byte[] ciphertext);

69

}

70

```

71

72

### KeyEncryptionKeyResolver

73

74

Synchronous resolver interface for retrieving key encryption keys by identifier.

75

76

```java { .api }

77

/**

78

* Synchronous resolver interface for key encryption keys.

79

*/

80

interface KeyEncryptionKeyResolver {

81

/**

82

* Resolves a key encryption key by its identifier.

83

* @param keyId The identifier of the key to resolve

84

* @return The resolved KeyEncryptionKey, or null if not found

85

*/

86

KeyEncryptionKey resolveKey(String keyId);

87

}

88

```

89

90

### AsyncKeyEncryptionKeyResolver

91

92

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

93

94

```java { .api }

95

/**

96

* Asynchronous resolver interface for key encryption keys.

97

*/

98

interface AsyncKeyEncryptionKeyResolver {

99

/**

100

* Resolves a key encryption key by its identifier asynchronously.

101

* @param keyId The identifier of the key to resolve

102

* @return A Mono containing the resolved AsyncKeyEncryptionKey, or empty if not found

103

*/

104

Mono<AsyncKeyEncryptionKey> resolveKey(String keyId);

105

}

106

```

107

108

## Usage Examples

109

110

### Implementing KeyEncryptionKey

111

112

```java

113

import com.azure.core.cryptography.*;

114

import javax.crypto.Cipher;

115

import javax.crypto.spec.SecretKeySpec;

116

import java.security.Key;

117

118

class AesKeyEncryptionKey implements KeyEncryptionKey {

119

private final String keyId;

120

private final Key key;

121

122

public AesKeyEncryptionKey(String keyId, byte[] keyBytes) {

123

this.keyId = keyId;

124

this.key = new SecretKeySpec(keyBytes, "AES");

125

}

126

127

@Override

128

public String getKeyId() {

129

return keyId;

130

}

131

132

@Override

133

public byte[] wrapKey(String algorithm, byte[] plaintext) {

134

try {

135

Cipher cipher = Cipher.getInstance(algorithm);

136

cipher.init(Cipher.ENCRYPT_MODE, key);

137

return cipher.doFinal(plaintext);

138

} catch (Exception e) {

139

throw new RuntimeException("Encryption failed", e);

140

}

141

}

142

143

@Override

144

public byte[] unwrapKey(String algorithm, byte[] ciphertext) {

145

try {

146

Cipher cipher = Cipher.getInstance(algorithm);

147

cipher.init(Cipher.DECRYPT_MODE, key);

148

return cipher.doFinal(ciphertext);

149

} catch (Exception e) {

150

throw new RuntimeException("Decryption failed", e);

151

}

152

}

153

}

154

```

155

156

### Implementing AsyncKeyEncryptionKey

157

158

```java

159

import com.azure.core.cryptography.*;

160

import reactor.core.publisher.Mono;

161

import reactor.core.scheduler.Schedulers;

162

163

class AsyncAesKeyEncryptionKey implements AsyncKeyEncryptionKey {

164

private final KeyEncryptionKey syncKey;

165

166

public AsyncAesKeyEncryptionKey(KeyEncryptionKey syncKey) {

167

this.syncKey = syncKey;

168

}

169

170

@Override

171

public Mono<String> getKeyId() {

172

return Mono.fromCallable(syncKey::getKeyId)

173

.subscribeOn(Schedulers.boundedElastic());

174

}

175

176

@Override

177

public Mono<byte[]> wrapKey(String algorithm, byte[] plaintext) {

178

return Mono.fromCallable(() -> syncKey.wrapKey(algorithm, plaintext))

179

.subscribeOn(Schedulers.boundedElastic());

180

}

181

182

@Override

183

public Mono<byte[]> unwrapKey(String algorithm, byte[] ciphertext) {

184

return Mono.fromCallable(() -> syncKey.unwrapKey(algorithm, ciphertext))

185

.subscribeOn(Schedulers.boundedElastic());

186

}

187

}

188

```

189

190

### Implementing Key Resolvers

191

192

```java

193

import com.azure.core.cryptography.*;

194

import java.util.concurrent.ConcurrentHashMap;

195

import java.util.Map;

196

197

class InMemoryKeyResolver implements KeyEncryptionKeyResolver {

198

private final Map<String, KeyEncryptionKey> keys = new ConcurrentHashMap<>();

199

200

public void addKey(String keyId, KeyEncryptionKey key) {

201

keys.put(keyId, key);

202

}

203

204

@Override

205

public KeyEncryptionKey resolveKey(String keyId) {

206

return keys.get(keyId);

207

}

208

}

209

210

class AsyncInMemoryKeyResolver implements AsyncKeyEncryptionKeyResolver {

211

private final KeyEncryptionKeyResolver syncResolver;

212

213

public AsyncInMemoryKeyResolver(KeyEncryptionKeyResolver syncResolver) {

214

this.syncResolver = syncResolver;

215

}

216

217

@Override

218

public Mono<AsyncKeyEncryptionKey> resolveKey(String keyId) {

219

return Mono.fromCallable(() -> {

220

KeyEncryptionKey syncKey = syncResolver.resolveKey(keyId);

221

return syncKey != null ? new AsyncAesKeyEncryptionKey(syncKey) : null;

222

}).subscribeOn(Schedulers.boundedElastic());

223

}

224

}

225

```

226

227

### Azure Key Vault Integration Example

228

229

```java

230

import com.azure.core.cryptography.*;

231

import com.azure.security.keyvault.keys.cryptography.*;

232

import com.azure.identity.DefaultAzureCredentialBuilder;

233

234

// Example using Azure Key Vault for key encryption

235

class KeyVaultKeyEncryptionKey implements KeyEncryptionKey {

236

private final CryptographyClient cryptoClient;

237

private final String keyId;

238

239

public KeyVaultKeyEncryptionKey(String keyVaultUrl, String keyName) {

240

CryptographyClientBuilder builder = new CryptographyClientBuilder()

241

.keyIdentifier(keyVaultUrl + "/keys/" + keyName)

242

.credential(new DefaultAzureCredentialBuilder().build());

243

244

this.cryptoClient = builder.buildClient();

245

this.keyId = keyName;

246

}

247

248

@Override

249

public String getKeyId() {

250

return keyId;

251

}

252

253

@Override

254

public byte[] wrapKey(String algorithm, byte[] plaintext) {

255

EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);

256

EncryptResult result = cryptoClient.encrypt(encryptAlg, plaintext);

257

return result.getCipherText();

258

}

259

260

@Override

261

public byte[] unwrapKey(String algorithm, byte[] ciphertext) {

262

EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);

263

DecryptResult result = cryptoClient.decrypt(encryptAlg, ciphertext);

264

return result.getPlainText();

265

}

266

}

267

268

// Async version for Key Vault

269

class AsyncKeyVaultKeyEncryptionKey implements AsyncKeyEncryptionKey {

270

private final CryptographyAsyncClient cryptoClient;

271

private final String keyId;

272

273

public AsyncKeyVaultKeyEncryptionKey(String keyVaultUrl, String keyName) {

274

CryptographyClientBuilder builder = new CryptographyClientBuilder()

275

.keyIdentifier(keyVaultUrl + "/keys/" + keyName)

276

.credential(new DefaultAzureCredentialBuilder().build());

277

278

this.cryptoClient = builder.buildAsyncClient();

279

this.keyId = keyName;

280

}

281

282

@Override

283

public Mono<String> getKeyId() {

284

return Mono.just(keyId);

285

}

286

287

@Override

288

public Mono<byte[]> wrapKey(String algorithm, byte[] plaintext) {

289

EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);

290

return cryptoClient.encrypt(encryptAlg, plaintext)

291

.map(EncryptResult::getCipherText);

292

}

293

294

@Override

295

public Mono<byte[]> unwrapKey(String algorithm, byte[] ciphertext) {

296

EncryptionAlgorithm encryptAlg = EncryptionAlgorithm.fromString(algorithm);

297

return cryptoClient.decrypt(encryptAlg, ciphertext)

298

.map(DecryptResult::getPlainText);

299

}

300

}

301

```

302

303

### Client-Side Encryption Pattern

304

305

```java

306

import com.azure.core.cryptography.*;

307

import com.azure.core.util.BinaryData;

308

309

class EncryptedDataService {

310

private final AsyncKeyEncryptionKey keyEncryptionKey;

311

private final AsyncKeyEncryptionKeyResolver keyResolver;

312

313

public EncryptedDataService(AsyncKeyEncryptionKey kek, AsyncKeyEncryptionKeyResolver resolver) {

314

this.keyEncryptionKey = kek;

315

this.keyResolver = resolver;

316

}

317

318

public Mono<EncryptedBlob> encryptData(BinaryData data) {

319

// Generate a content encryption key (CEK)

320

byte[] contentKey = generateRandomKey(32); // 256-bit key

321

322

return keyEncryptionKey.getKeyId()

323

.flatMap(keyId -> keyEncryptionKey.wrapKey("AES/GCM/NoPadding", contentKey)

324

.map(encryptedKey -> {

325

// Encrypt the actual data with the CEK

326

byte[] encryptedData = encryptWithContentKey(contentKey, data.toBytes());

327

328

return new EncryptedBlob(keyId, encryptedKey, encryptedData);

329

}));

330

}

331

332

public Mono<BinaryData> decryptData(EncryptedBlob encryptedBlob) {

333

return keyResolver.resolveKey(encryptedBlob.getKeyId())

334

.switchIfEmpty(Mono.error(new IllegalArgumentException("Key not found: " + encryptedBlob.getKeyId())))

335

.flatMap(kek -> kek.unwrapKey("AES/GCM/NoPadding", encryptedBlob.getEncryptedKey()))

336

.map(contentKey -> {

337

// Decrypt the data with the recovered content key

338

byte[] decryptedData = decryptWithContentKey(contentKey, encryptedBlob.getEncryptedData());

339

return BinaryData.fromBytes(decryptedData);

340

});

341

}

342

343

private byte[] generateRandomKey(int size) {

344

byte[] key = new byte[size];

345

new java.security.SecureRandom().nextBytes(key);

346

return key;

347

}

348

349

private byte[] encryptWithContentKey(byte[] key, byte[] data) {

350

// Implementation of AES encryption

351

// This is a simplified example - real implementation would handle IV, etc.

352

try {

353

SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

354

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

355

cipher.init(Cipher.ENCRYPT_MODE, keySpec);

356

return cipher.doFinal(data);

357

} catch (Exception e) {

358

throw new RuntimeException("Content encryption failed", e);

359

}

360

}

361

362

private byte[] decryptWithContentKey(byte[] key, byte[] encryptedData) {

363

// Implementation of AES decryption

364

try {

365

SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

366

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

367

cipher.init(Cipher.DECRYPT_MODE, keySpec);

368

return cipher.doFinal(encryptedData);

369

} catch (Exception e) {

370

throw new RuntimeException("Content decryption failed", e);

371

}

372

}

373

}

374

375

// Helper class for encrypted data

376

class EncryptedBlob {

377

private final String keyId;

378

private final byte[] encryptedKey;

379

private final byte[] encryptedData;

380

381

public EncryptedBlob(String keyId, byte[] encryptedKey, byte[] encryptedData) {

382

this.keyId = keyId;

383

this.encryptedKey = encryptedKey.clone();

384

this.encryptedData = encryptedData.clone();

385

}

386

387

public String getKeyId() { return keyId; }

388

public byte[] getEncryptedKey() { return encryptedKey.clone(); }

389

public byte[] getEncryptedData() { return encryptedData.clone(); }

390

}

391

```

392

393

### Multi-Key Encryption Service

394

395

```java

396

import com.azure.core.cryptography.*;

397

import java.util.List;

398

import java.util.concurrent.ThreadLocalRandom;

399

400

class MultiKeyEncryptionService {

401

private final List<AsyncKeyEncryptionKey> availableKeys;

402

private final AsyncKeyEncryptionKeyResolver keyResolver;

403

404

public MultiKeyEncryptionService(List<AsyncKeyEncryptionKey> keys, AsyncKeyEncryptionKeyResolver resolver) {

405

this.availableKeys = keys;

406

this.keyResolver = resolver;

407

}

408

409

public Mono<EncryptedBlob> encryptWithRandomKey(BinaryData data) {

410

// Select a random key for encryption

411

int keyIndex = ThreadLocalRandom.current().nextInt(availableKeys.size());

412

AsyncKeyEncryptionKey selectedKey = availableKeys.get(keyIndex);

413

414

return encryptWithSpecificKey(selectedKey, data);

415

}

416

417

public Mono<EncryptedBlob> encryptWithSpecificKey(AsyncKeyEncryptionKey kek, BinaryData data) {

418

byte[] contentKey = generateRandomKey(32);

419

420

return kek.getKeyId()

421

.flatMap(keyId -> kek.wrapKey("RSA-OAEP", contentKey)

422

.map(encryptedKey -> {

423

byte[] encryptedData = encryptWithContentKey(contentKey, data.toBytes());

424

return new EncryptedBlob(keyId, encryptedKey, encryptedData);

425

}));

426

}

427

428

public Flux<BinaryData> decryptMultiple(List<EncryptedBlob> encryptedBlobs) {

429

return Flux.fromIterable(encryptedBlobs)

430

.flatMap(blob -> keyResolver.resolveKey(blob.getKeyId())

431

.flatMap(kek -> kek.unwrapKey("RSA-OAEP", blob.getEncryptedKey()))

432

.map(contentKey -> {

433

byte[] decryptedData = decryptWithContentKey(contentKey, blob.getEncryptedData());

434

return BinaryData.fromBytes(decryptedData);

435

})

436

.onErrorResume(error -> {

437

// Log error and continue with next blob

438

System.err.println("Failed to decrypt blob with key " + blob.getKeyId() + ": " + error.getMessage());

439

return Mono.empty();

440

}));

441

}

442

443

private byte[] generateRandomKey(int size) {

444

byte[] key = new byte[size];

445

new java.security.SecureRandom().nextBytes(key);

446

return key;

447

}

448

449

// Content encryption methods (same as previous example)

450

private byte[] encryptWithContentKey(byte[] key, byte[] data) { /* ... */ }

451

private byte[] decryptWithContentKey(byte[] key, byte[] encryptedData) { /* ... */ }

452

}

453

```

454

455

### Key Rotation Example

456

457

```java

458

import com.azure.core.cryptography.*;

459

import java.time.Duration;

460

import java.util.concurrent.atomic.AtomicReference;

461

462

class RotatingKeyEncryptionService {

463

private final AtomicReference<AsyncKeyEncryptionKey> currentKey = new AtomicReference<>();

464

private final AsyncKeyEncryptionKeyResolver keyResolver;

465

private final List<AsyncKeyEncryptionKey> keyRotationOrder;

466

private volatile int currentKeyIndex = 0;

467

468

public RotatingKeyEncryptionService(List<AsyncKeyEncryptionKey> keys, AsyncKeyEncryptionKeyResolver resolver) {

469

this.keyRotationOrder = keys;

470

this.keyResolver = resolver;

471

this.currentKey.set(keys.get(0));

472

473

// Start key rotation timer

474

startKeyRotation();

475

}

476

477

public Mono<EncryptedBlob> encrypt(BinaryData data) {

478

AsyncKeyEncryptionKey key = currentKey.get();

479

return encryptWithKey(key, data);

480

}

481

482

public Mono<BinaryData> decrypt(EncryptedBlob encryptedBlob) {

483

return keyResolver.resolveKey(encryptedBlob.getKeyId())

484

.switchIfEmpty(Mono.error(new IllegalArgumentException("Key not found: " + encryptedBlob.getKeyId())))

485

.flatMap(kek -> decryptWithKey(kek, encryptedBlob));

486

}

487

488

private void startKeyRotation() {

489

// Rotate keys every hour (simplified example)

490

Flux.interval(Duration.ofHours(1))

491

.subscribe(tick -> rotateKey());

492

}

493

494

private void rotateKey() {

495

currentKeyIndex = (currentKeyIndex + 1) % keyRotationOrder.size();

496

currentKey.set(keyRotationOrder.get(currentKeyIndex));

497

System.out.println("Rotated to key index: " + currentKeyIndex);

498

}

499

500

private Mono<EncryptedBlob> encryptWithKey(AsyncKeyEncryptionKey kek, BinaryData data) {

501

// Implementation similar to previous examples

502

return Mono.empty(); // Placeholder

503

}

504

505

private Mono<BinaryData> decryptWithKey(AsyncKeyEncryptionKey kek, EncryptedBlob blob) {

506

// Implementation similar to previous examples

507

return Mono.empty(); // Placeholder

508

}

509

}

510

```