Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ jobs:
run: sleep 30 && docker logs dapr_scheduler && nc -vz localhost 50006
- name: Install jars
run: ./mvnw clean install -DskipTests -q
- name: Validate crypto example
working-directory: ./examples
run: |
mm.py ./src/main/java/io/dapr/examples/crypto/README.md
- name: Validate workflows example
working-directory: ./examples
run: |
Expand Down Expand Up @@ -186,3 +190,5 @@ jobs:
run: |
mm.py ./src/main/java/io/dapr/examples/pubsub/stream/README.md



12 changes: 12 additions & 0 deletions examples/components/crypto/localstorage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localstoragecrypto
spec:
type: crypto.dapr.localstorage
version: v1
metadata:
# Path to the directory containing keys (PEM files)
# This path is relative to where the dapr run command is executed
- name: path
value: "./components/crypto/keys"
173 changes: 173 additions & 0 deletions examples/src/main/java/io/dapr/examples/crypto/CryptoExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Copyright 2021 The Dapr Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
limitations under the License.
*/

package io.dapr.examples.crypto;

import io.dapr.client.DaprClientBuilder;
import io.dapr.client.DaprPreviewClient;
import io.dapr.client.domain.DecryptRequestAlpha1;
import io.dapr.client.domain.EncryptRequestAlpha1;
import io.dapr.config.Properties;
import io.dapr.config.Property;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;

/**
* CryptoExample demonstrates using the Dapr Cryptography building block
* to encrypt and decrypt data using a cryptography component.
*
* <p>This example shows:
* <ul>
* <li>Encrypting plaintext data with a specified key and algorithm</li>
* <li>Decrypting ciphertext data back to plaintext</li>
* <li>Automatic key generation if keys don't exist</li>
* </ul>
*
* <p>Prerequisites:
* <ul>
* <li>Dapr installed and initialized</li>
* <li>A cryptography component configured (e.g., local storage crypto)</li>
* </ul>
*/
public class CryptoExample {

private static final String CRYPTO_COMPONENT_NAME = "localstoragecrypto";
private static final String KEY_NAME = "rsa-private-key";
private static final String KEY_WRAP_ALGORITHM = "RSA";
private static final String KEYS_DIR = "components/crypto/keys";

/**
* The main method demonstrating encryption and decryption with Dapr.
*
* @param args Command line arguments (unused).
*/
public static void main(String[] args) throws Exception {
// Generate keys if they don't exist
generateKeysIfNeeded();

Map<Property<?>, String> overrides = Map.of(
Properties.HTTP_PORT, "3500",
Properties.GRPC_PORT, "50001"
);

try (DaprPreviewClient client = new DaprClientBuilder().withPropertyOverrides(overrides).buildPreviewClient()) {

String originalMessage = "This is a secret message";
byte[] plainText = originalMessage.getBytes(StandardCharsets.UTF_8);

System.out.println("=== Dapr Cryptography Example ===");
System.out.println("Original message: " + originalMessage);
System.out.println();

// Encrypt the message
System.out.println("Encrypting message...");
EncryptRequestAlpha1 encryptRequest = new EncryptRequestAlpha1(
CRYPTO_COMPONENT_NAME,
Flux.just(plainText),
KEY_NAME,
KEY_WRAP_ALGORITHM
);

byte[] encryptedData = client.encrypt(encryptRequest)
.collectList()
.map(CryptoExample::combineChunks)
.block();

System.out.println("Encryption successful!");
System.out.println("Encrypted data length: " + encryptedData.length + " bytes");
System.out.println();

// Decrypt the message
System.out.println("Decrypting message...");
DecryptRequestAlpha1 decryptRequest = new DecryptRequestAlpha1(
CRYPTO_COMPONENT_NAME,
Flux.just(encryptedData)
);

byte[] decryptedData = client.decrypt(decryptRequest)
.collectList()
.map(CryptoExample::combineChunks)
.block();

String decryptedMessage = new String(decryptedData, StandardCharsets.UTF_8);
System.out.println("Decryption successful!");
System.out.println("Decrypted message: " + decryptedMessage);
System.out.println();

if (originalMessage.equals(decryptedMessage)) {
System.out.println("SUCCESS: The decrypted message matches the original.");
} else {
System.out.println("ERROR: The decrypted message does not match the original.");
}

} catch (Exception e) {
System.err.println("Error during crypto operations: " + e.getMessage());
throw new RuntimeException(e);
}
}

/**
* Generates RSA key pair if the key file doesn't exist.
*/
private static void generateKeysIfNeeded() throws NoSuchAlgorithmException, IOException {
Path keysDir = Paths.get(KEYS_DIR);
Path keyFile = keysDir.resolve(KEY_NAME + ".pem");

if (Files.exists(keyFile)) {
System.out.println("Using existing key: " + keyFile.toAbsolutePath());
return;
}

System.out.println("Generating RSA key pair...");
Files.createDirectories(keysDir);

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(4096);
KeyPair keyPair = keyGen.generateKeyPair();

String privateKeyPem = "-----BEGIN PRIVATE KEY-----\n"
+ Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(keyPair.getPrivate().getEncoded())
+ "\n-----END PRIVATE KEY-----\n";

String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n"
+ Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(keyPair.getPublic().getEncoded())
+ "\n-----END PUBLIC KEY-----\n";

Files.writeString(keyFile, privateKeyPem + publicKeyPem);
System.out.println("Key generated: " + keyFile.toAbsolutePath());
}

/**
* Combines byte array chunks into a single byte array.
*/
private static byte[] combineChunks(java.util.List<byte[]> chunks) {
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
byte[] result = new byte[totalSize];
int pos = 0;
for (byte[] chunk : chunks) {
System.arraycopy(chunk, 0, result, pos, chunk.length);
pos += chunk.length;
}
return result;
}
}
164 changes: 164 additions & 0 deletions examples/src/main/java/io/dapr/examples/crypto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
## Dapr Cryptography API Examples

This example provides the different capabilities provided by Dapr Java SDK for Cryptography. For further information about Cryptography APIs please refer to [this link](https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/)

### Using the Cryptography API

The Java SDK exposes several methods for this -
* `client.encrypt(...)` for encrypting data using a cryptography component.
* `client.decrypt(...)` for decrypting data using a cryptography component.

## Pre-requisites

* [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/).
* Java JDK 11 (or greater):
* [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11)
* [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11)
* [OpenJDK 11](https://jdk.java.net/11/)
* [Apache Maven](https://maven.apache.org/install.html) version 3.x.

### Checking out the code

Clone this repository:

```sh
git clone https://github.com/dapr/java-sdk.git
cd java-sdk
```

Then build the Maven project:

```sh
# make sure you are in the `java-sdk` directory.
mvn install
```

Then get into the examples directory:

```sh
cd examples
```

### Initialize Dapr

Run `dapr init` to initialize Dapr in Self-Hosted Mode if it's not already initialized.

### Running the Example

This example uses the Java SDK Dapr client to **Encrypt and Decrypt** data. The example automatically generates RSA keys if they don't exist.

#### Example 1: Basic Crypto Example

`CryptoExample.java` demonstrates basic encryption and decryption of a simple message.

```java
public class CryptoExample {
private static final String CRYPTO_COMPONENT_NAME = "localstoragecrypto";
private static final String KEY_NAME = "rsa-private-key";
private static final String KEY_WRAP_ALGORITHM = "RSA";

public static void main(String[] args) {
try (DaprPreviewClient client = new DaprClientBuilder().buildPreviewClient()) {

String originalMessage = "This is a secret message";
byte[] plainText = originalMessage.getBytes(StandardCharsets.UTF_8);

// Encrypt the message
EncryptRequestAlpha1 encryptRequest = new EncryptRequestAlpha1(
CRYPTO_COMPONENT_NAME,
Flux.just(plainText),
KEY_NAME,
KEY_WRAP_ALGORITHM
);

byte[] encryptedData = client.encrypt(encryptRequest)
.collectList()
.map(chunks -> /* combine chunks */)
.block();

// Decrypt the message
DecryptRequestAlpha1 decryptRequest = new DecryptRequestAlpha1(
CRYPTO_COMPONENT_NAME,
Flux.just(encryptedData)
);

byte[] decryptedData = client.decrypt(decryptRequest)
.collectList()
.map(chunks -> /* combine chunks */)
.block();
}
}
}
```

Use the following command to run this example:

<!-- STEP
name: Run Crypto Example
expected_stdout_lines:
- "== APP == SUCCESS: The decrypted message matches the original."
background: true
output_match_mode: substring
sleep: 10
-->

```bash
dapr run --resources-path ./components/crypto --app-id crypto-app --dapr-http-port 3500 --dapr-grpc-port 50001 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.crypto.CryptoExample
```

<!-- END_STEP -->

#### Example 2: Streaming Crypto Example

`StreamingCryptoExample.java` demonstrates advanced scenarios including:
- Multi-chunk data encryption
- Large data encryption (100KB+)
- Custom encryption ciphers

```bash
dapr run --resources-path ./components/crypto --app-id crypto-app --dapr-http-port 3500 --dapr-grpc-port 50001 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.crypto.StreamingCryptoExample
```

### Sample Output

```
=== Dapr Cryptography Example ===
Original message: This is a secret message

Encrypting message...
Encryption successful!
Encrypted data length: 512 bytes

Decrypting message...
Decryption successful!
Decrypted message: This is a secret message

SUCCESS: The decrypted message matches the original.
```

### Supported Key Wrap Algorithms

The following key wrap algorithms are supported:
- `A256KW` (alias: `AES`) - AES key wrap
- `A128CBC`, `A192CBC`, `A256CBC` - AES CBC modes
- `RSA-OAEP-256` (alias: `RSA`) - RSA OAEP with SHA-256

### Supported Data Encryption Ciphers

Optional data encryption ciphers:
- `aes-gcm` (default) - AES in GCM mode
- `chacha20-poly1305` - ChaCha20-Poly1305 cipher

### Cleanup

To stop the app, run (or press CTRL+C):

<!-- STEP
name: Cleanup
-->

```bash
dapr stop --app-id crypto-app
```

<!-- END_STEP -->
Loading
Loading