Quickstart: Cryptography

Get started with the Dapr Cryptography building block

Alpha

The cryptography building block is currently in alpha.

Let’s take a look at the Dapr cryptography building block. In this Quickstart, you’ll create an application that encrypts and decrypts data using the Dapr cryptography APIs. You’ll:

  • Encrypt and then decrypt a short string (using an RSA key), reading the result in-memory, in a Go byte slice.
  • Encrypt and then decrypt a large file (using an AES key), storing the encrypted and decrypted data to files using streams.

Cryptography - 图1

Note

This example uses the Dapr SDK, which leverages gRPC and is strongly recommended when using cryptographic APIs to encrypt and decrypt messages.

Currently, you can experience the cryptography API using the Go SDK.

This quickstart includes a JavaScript application called crypto-quickstart.

Pre-requisites

For this example, you will need:

Step 1: Set up the environment

Clone the sample provided in the Quickstarts repo

  1. git clone https://github.com/dapr/quickstarts.git

In the terminal, from the root directory, navigate to the cryptography sample.

  1. cd cryptography/javascript/sdk

Navigate into the folder with the source code:

  1. cd ./crypto-quickstart

Install the dependencies:

  1. npm install

Step 2: Run the application with Dapr

The application code defines two required keys:

  • Private RSA key
  • A 256-bit symmetric (AES) key

Generate two keys, an RSA key and and AES key using OpenSSL and write these to two files:

  1. mkdir -p keys
  2. # Generate a private RSA key, 4096-bit keys
  3. openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
  4. # Generate a 256-bit key for AES
  5. openssl rand -out keys/symmetric-key-256 32

Run the Go service app with Dapr:

  1. dapr run --app-id crypto-quickstart --resources-path ../../../components/ -- npm start

Expected output

  1. == APP == 2023-10-25T14:30:50.435Z INFO [GRPCClient, GRPCClient] Opening connection to 127.0.0.1:58173
  2. == APP == == Encrypting message using buffers
  3. == APP == Encrypted the message, got 856 bytes
  4. == APP == == Decrypting message using buffers
  5. == APP == Decrypted the message, got 24 bytes
  6. == APP == The secret is "passw0rd"
  7. == APP == == Encrypting message using streams
  8. == APP == Encrypting federico-di-dio-photography-Q4g0Q-eVVEg-unsplash.jpg to encrypted.out
  9. == APP == Encrypted the message to encrypted.out
  10. == APP == == Decrypting message using streams
  11. == APP == Decrypting encrypted.out to decrypted.out.jpg
  12. == APP == Decrypted the message to decrypted.out.jpg

What happened?

local-storage.yaml

Earlier, you created a directory inside crypto-quickstarts called keys. In the local-storage component YAML, the path metadata maps to the newly created keys directory.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: localstorage
  5. spec:
  6. type: crypto.dapr.localstorage
  7. version: v1
  8. metadata:
  9. - name: path
  10. # Path is relative to the folder where the example is located
  11. value: ./keys

index.mjs

The application file encrypts and decrypts messages and files using the RSA and AES keys that you generated. The application creates a new Dapr SDK client:

  1. async function start() {
  2. const client = new DaprClient({
  3. daprHost,
  4. daprPort,
  5. communicationProtocol: CommunicationProtocolEnum.GRPC,
  6. });
  7. // Encrypt and decrypt a message from a buffer
  8. await encryptDecryptBuffer(client);
  9. // Encrypt and decrypt a message using streams
  10. await encryptDecryptStream(client);
  11. }
Encrypting and decrypting a string using the RSA key

Once the client is created, the application encrypts a message:

  1. async function encryptDecryptBuffer(client) {
  2. // Message to encrypt
  3. const plaintext = `The secret is "passw0rd"`
  4. // First, encrypt the message
  5. console.log("== Encrypting message using buffers");
  6. const encrypted = await client.crypto.encrypt(plaintext, {
  7. componentName: "localstorage",
  8. keyName: "rsa-private-key.pem",
  9. keyWrapAlgorithm: "RSA",
  10. });
  11. console.log("Encrypted the message, got", encrypted.length, "bytes");

The application then decrypts the message:

  1. // Decrypt the message
  2. console.log("== Decrypting message using buffers");
  3. const decrypted = await client.crypto.decrypt(encrypted, {
  4. componentName: "localstorage",
  5. });
  6. console.log("Decrypted the message, got", decrypted.length, "bytes");
  7. console.log(decrypted.toString("utf8"));
  8. // ...
  9. }
Encrypt and decrpyt a large file using the AES key

Next, the application encrypts a large image file:

  1. async function encryptDecryptStream(client) {
  2. // First, encrypt the message
  3. console.log("== Encrypting message using streams");
  4. console.log("Encrypting", testFileName, "to encrypted.out");
  5. await pipeline(
  6. createReadStream(testFileName),
  7. await client.crypto.encrypt({
  8. componentName: "localstorage",
  9. keyName: "symmetric-key-256",
  10. keyWrapAlgorithm: "A256KW",
  11. }),
  12. createWriteStream("encrypted.out"),
  13. );
  14. console.log("Encrypted the message to encrypted.out");

The application then decrypts the large image file:

  1. // Decrypt the message
  2. console.log("== Decrypting message using streams");
  3. console.log("Decrypting encrypted.out to decrypted.out.jpg");
  4. await pipeline(
  5. createReadStream("encrypted.out"),
  6. await client.crypto.decrypt({
  7. componentName: "localstorage",
  8. }),
  9. createWriteStream("decrypted.out.jpg"),
  10. );
  11. console.log("Decrypted the message to decrypted.out.jpg");
  12. }

This quickstart includes a Go application called crypto-quickstart.

Pre-requisites

For this example, you will need:

Step 1: Set up the environment

Clone the sample provided in the Quickstarts repo

  1. git clone https://github.com/dapr/quickstarts.git

In the terminal, from the root directory, navigate to the cryptography sample.

  1. cd cryptography/go/sdk

Step 2: Run the application with Dapr

Navigate into the folder with the source code:

  1. cd ./crypto-quickstart

The application code defines two required keys:

  • Private RSA key
  • A 256-bit symmetric (AES) key

Generate two keys, an RSA key and and AES key using OpenSSL and write these to two files:

  1. mkdir -p keys
  2. # Generate a private RSA key, 4096-bit keys
  3. openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
  4. # Generate a 256-bit key for AES
  5. openssl rand -out keys/symmetric-key-256 32

Run the Go service app with Dapr:

  1. dapr run --app-id crypto-quickstart --resources-path ../../../components/ -- go run .

Expected output

  1. == APP == dapr client initializing for: 127.0.0.1:52407
  2. == APP == Encrypted the message, got 856 bytes
  3. == APP == Decrypted the message, got 24 bytes
  4. == APP == The secret is "passw0rd"
  5. == APP == Wrote decrypted data to encrypted.out
  6. == APP == Wrote decrypted data to decrypted.out.jpg

What happened?

local-storage.yaml

Earlier, you created a directory inside crypto-quickstarts called keys. In the local-storage component YAML, the path metadata maps to the newly created keys directory.

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: localstorage
  5. spec:
  6. type: crypto.dapr.localstorage
  7. version: v1
  8. metadata:
  9. - name: path
  10. # Path is relative to the folder where the example is located
  11. value: ./keys

app.go

The application file encrypts and decrypts messages and files using the RSA and AES keys that you generated. The application creates a new Dapr SDK client:

  1. func main() {
  2. // Create a new Dapr SDK client
  3. client, err := dapr.NewClient()
  4. //...
  5. // Step 1: encrypt a string using the RSA key, then decrypt it and show the output in the terminal
  6. encryptDecryptString(client)
  7. // Step 2: encrypt a large file and then decrypt it, using the AES key
  8. encryptDecryptFile(client)
  9. }
Encrypting and decrypting a string using the RSA key

Once the client is created, the application encrypts a message:

  1. func encryptDecryptString(client dapr.Client) {
  2. // ...
  3. // Encrypt the message
  4. encStream, err := client.Encrypt(context.Background(),
  5. strings.NewReader(message),
  6. dapr.EncryptOptions{
  7. ComponentName: CryptoComponentName,
  8. // Name of the key to use
  9. // Since this is a RSA key, we specify that as key wrapping algorithm
  10. KeyName: RSAKeyName,
  11. KeyWrapAlgorithm: "RSA",
  12. },
  13. )
  14. // ...
  15. // The method returns a readable stream, which we read in full in memory
  16. encBytes, err := io.ReadAll(encStream)
  17. // ...
  18. fmt.Printf("Encrypted the message, got %d bytes\n", len(encBytes))

The application then decrypts the message:

  1. // Now, decrypt the encrypted data
  2. decStream, err := client.Decrypt(context.Background(),
  3. bytes.NewReader(encBytes),
  4. dapr.DecryptOptions{
  5. // We just need to pass the name of the component
  6. ComponentName: CryptoComponentName,
  7. // Passing the name of the key is optional
  8. KeyName: RSAKeyName,
  9. },
  10. )
  11. // ...
  12. // The method returns a readable stream, which we read in full in memory
  13. decBytes, err := io.ReadAll(decStream)
  14. // ...
  15. // Print the message on the console
  16. fmt.Printf("Decrypted the message, got %d bytes\n", len(decBytes))
  17. fmt.Println(string(decBytes))
  18. }
Encrypt and decrpyt a large file using the AES key

Next, the application encrypts a large image file:

  1. func encryptDecryptFile(client dapr.Client) {
  2. const fileName = "liuguangxi-66ouBTTs_x0-unsplash.jpg"
  3. // Get a readable stream to the input file
  4. plaintextF, err := os.Open(fileName)
  5. // ...
  6. defer plaintextF.Close()
  7. // Encrypt the file
  8. encStream, err := client.Encrypt(context.Background(),
  9. plaintextF,
  10. dapr.EncryptOptions{
  11. ComponentName: CryptoComponentName,
  12. // Name of the key to use
  13. // Since this is a symmetric key, we specify AES as key wrapping algorithm
  14. KeyName: SymmetricKeyName,
  15. KeyWrapAlgorithm: "AES",
  16. },
  17. )
  18. // ...
  19. // Write the encrypted data to a file "encrypted.out"
  20. encryptedF, err := os.Create("encrypted.out")
  21. // ...
  22. encryptedF.Close()
  23. fmt.Println("Wrote decrypted data to encrypted.out")

The application then decrypts the large image file:

  1. // Now, decrypt the encrypted data
  2. // First, open the file "encrypted.out" again, this time for reading
  3. encryptedF, err = os.Open("encrypted.out")
  4. // ...
  5. defer encryptedF.Close()
  6. // Now, decrypt the encrypted data
  7. decStream, err := client.Decrypt(context.Background(),
  8. encryptedF,
  9. dapr.DecryptOptions{
  10. // We just need to pass the name of the component
  11. ComponentName: CryptoComponentName,
  12. // Passing the name of the key is optional
  13. KeyName: SymmetricKeyName,
  14. },
  15. )
  16. // ...
  17. // Write the decrypted data to a file "decrypted.out.jpg"
  18. decryptedF, err := os.Create("decrypted.out.jpg")
  19. // ...
  20. decryptedF.Close()
  21. fmt.Println("Wrote decrypted data to decrypted.out.jpg")
  22. }

Watch the demo

Watch this demo video of the cryptography API from the Dapr Community Call #83:

Tell us what you think!

We’re continuously working to improve our Quickstart examples and value your feedback. Did you find this Quickstart helpful? Do you have suggestions for improvement?

Join the discussion in our discord channel.

Next steps

Explore Dapr tutorials >>

Last modified March 21, 2024: Merge pull request #4082 from newbe36524/v1.13 (f4b0938)