This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

密码学

在不向应用程序暴露密钥的情况下执行加密操作

1 - Cryptography 概述

Dapr Cryptography 概述

通过 cryptography 构建块,您可以以安全且一致的方式使用加密功能。Dapr 公开的 API 允许您在密钥保管库或 Dapr 边车中执行加密和解密等操作,而不会将加密密钥暴露给您的应用程序。

为什么需要 Cryptography?

应用程序广泛使用加密技术,如果实施正确,即使数据泄露,也能使解决方案更加安全。在某些情况下,您可能需要使用加密来满足行业法规(例如金融领域)或法律要求(包括 GDPR 等隐私法规)。

然而,正确使用加密技术可能很困难。您需要:

  • 选择正确的算法和选项
  • 学习正确管理和保护密钥的方法
  • 在希望限制对加密密钥材料的访问时处理操作复杂性

安全的一个重要要求是限制对加密密钥的访问,这通常被称为"原始密钥材料"。Dapr 可以与密钥保管库(如 Azure Key Vault,未来的版本将支持更多组件)集成,这些保管库在安全飞地中存储密钥并在保管库内执行加密操作,而不会将密钥暴露给您的应用程序或 Dapr。

或者,您可以配置 Dapr 为您管理加密密钥,在边车内执行操作,同样不会将原始密钥材料暴露给您的应用程序。

Dapr 中的 Cryptography

使用 Dapr,您可以执行加密操作而不会将加密密钥暴露给您的应用程序。

显示 Dapr 加密如何与您的应用程序配合工作的图表

通过使用 cryptography 构建块,您可以:

  • 以更安全的方式更轻松地执行加密操作。Dapr 提供了防止使用不安全算法或使用不安全选项的防护措施。
  • 将密钥保存在应用程序之外。应用程序永远不会看到"原始密钥材料",但可以请求保管库使用密钥执行操作。当使用 Dapr 的加密引擎时,操作在 Dapr 边车内安全执行。
  • 实现更好的关注点分离。通过使用外部保管库或加密组件,只有授权团队可以访问私钥材料。
  • 更轻松地管理和轮换密钥。密钥在保管库中管理,不在应用程序中,轮换时无需开发人员参与(甚至无需重启应用程序)。
  • 支持更好的审计日志,以监控何时使用保管库中的密钥执行了操作。

功能特性

Cryptography 组件

Dapr cryptography 构建块包含两类组件:

  • 允许与管理服务或保管库(“密钥保管库”)交互的组件。
    类似于 Dapr 如何在各种密钥存储或状态存储之上提供"抽象层",这些组件允许与各种密钥保管库(如 Azure Key Vault,未来 Dapr 版本将支持更多)交互。使用这些组件时,对私钥的加密操作在保管库内执行,Dapr 永远不会看到您的私钥。

  • 基于 Dapr 自身加密引擎的组件。
    当密钥保管库不可用时,您可以利用基于 Dapr 自身加密引擎的组件。这些名称中包含 .dapr. 的组件在 Dapr 边车内执行加密操作,密钥存储在文件、Kubernetes Secret 或其他来源中。虽然 Dapr 知道私钥,但您的应用程序仍然无法访问它们。

两类组件(无论是利用密钥保管库还是使用 Dapr 中的加密引擎)都提供相同的抽象层。这允许您的解决方案根据需要在各种保管库和/或加密组件之间切换。例如,您可以在开发期间使用本地存储的密钥,在生产环境中使用云保管库。

Cryptography API

Cryptography API 允许使用 Dapr Crypto Scheme v1 加密和解密数据。这是一种固执己见的加密方案,旨在使用现代、安全的加密标准,并将数据(包括大文件)作为流高效处理。

体验 Cryptography

快速入门和教程

想测试 Dapr Cryptography API?请参阅以下快速入门和教程,了解 cryptography 的实际应用:

快速入门/教程描述
Cryptography 快速入门使用 RSA 和 AES 密钥通过 cryptography API 加密和解密消息和大文件。

直接在您的应用程序中开始使用 Cryptography

想跳过快速入门?没问题。您可以直接在应用程序中试用 cryptography 构建块来加密和解密您的应用程序。安装 Dapr 后,您可以开始使用 cryptography API,从 cryptography 操作指南 开始。

演示

观看 Dapr Community Call #83 中 Cryptography API 的演示视频

下一步

使用 Cryptography API >>

相关链接

2 - 操作指南:使用 Cryptography API

了解如何加密和解密文件

现在您已经阅读了 Cryptography 作为 Dapr 构建块 的相关内容,让我们通过 SDK 来演练如何使用 cryptography API。

加密

在您的项目中使用 Dapr SDK(通过 gRPC API),您可以加密数据流,例如文件或字符串:

# 当传递数据(缓冲区或字符串)时,`encrypt` 返回包含加密消息的 Buffer
def encrypt_decrypt_string(dapr: DaprClient):
    message = 'The secret is "passw0rd"'

    # 加密消息
    resp = dapr.encrypt(
        data=message.encode(),
        options=EncryptOptions(
            # Cryptography 组件的名称(必需)
            component_name=CRYPTO_COMPONENT_NAME,
            # 存储在 cryptography 组件中的密钥(必需)
            key_name=RSA_KEY_NAME,
            # 用于包装密钥的算法,必须由上述命名的密钥支持。
            # 选项包括:"RSA"、"AES"
            key_wrap_algorithm='RSA',
        ),
    )

    # 该方法返回一个可读流,我们在内存中完整读取它
    encrypt_bytes = resp.read()
    print(f'Encrypted the message, got {len(encrypt_bytes)} bytes')

在您的项目中使用 Dapr SDK(通过 gRPC API),您可以加密缓冲区或字符串中的数据:

// 当传递数据(缓冲区或字符串)时,`encrypt` 返回包含加密消息的 Buffer
const ciphertext = await client.crypto.encrypt(plaintext, {
    // Dapr 组件的名称(必需)
    componentName: "mycryptocomponent",
    // 存储在组件中的密钥名称(必需)
    keyName: "mykey",
    // 用于包装密钥的算法,必须由上述命名的密钥支持。
    // 选项包括:"RSA"、"AES"
    keyWrapAlgorithm: "RSA",
});

这些 API 也可以与流一起使用,以便在数据来自流时更高效地加密数据。下面的示例使用流加密文件,并将其写入另一个文件:

// `encrypt` 可以用作 Duplex 流
await pipeline(
    fs.createReadStream("plaintext.txt"),
    await client.crypto.encrypt({
        // Dapr 组件的名称(必需)
        componentName: "mycryptocomponent",
        // 存储在组件中的密钥名称(必需)
        keyName: "mykey",
        // 用于包装密钥的算法,必须由上述命名的密钥支持。
        // 选项包括:"RSA"、"AES"
        keyWrapAlgorithm: "RSA",
    }),
    fs.createWriteStream("ciphertext.out"),
);

在您的项目中使用 Dapr SDK(通过 gRPC API),您可以加密字符串或字节数组中的数据:

using var client = new DaprClientBuilder().Build();

const string componentName = "azurekeyvault"; // 更改此项以匹配您的 cryptography 组件
const string keyName = "myKey"; // 更改此项以匹配您的加密存储中密钥的名称

const string plainText = "This is the value we're going to encrypt today";

// 将字符串编码为 UTF-8 字节数组并加密
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa));

在您的项目中使用 Dapr SDK,您可以加密数据流,例如文件。

out, err := sdkClient.Encrypt(context.Background(), rf, dapr.EncryptOptions{
	// Dapr 组件的名称(必需)
	ComponentName: "mycryptocomponent",
	// 存储在组件中的密钥名称(必需)
	KeyName:       "mykey",
	// 用于包装密钥的算法,必须由上述命名的密钥支持。
	// 选项包括:"RSA"、"AES"
	Algorithm:     "RSA",
})

下面的示例将 Encrypt API 放在上下文中,代码读取文件、加密文件,然后将结果存储在另一个文件中。

// 输入文件,明文
rf, err := os.Open("input")
if err != nil {
	panic(err)
}
defer rf.Close()

// 输出文件,已加密
wf, err := os.Create("output.enc")
if err != nil {
	panic(err)
}
defer wf.Close()

// 使用 Dapr 加密数据
out, err := sdkClient.Encrypt(context.Background(), rf, dapr.EncryptOptions{
	// 这是 3 个必需参数
	ComponentName: "mycryptocomponent",
	KeyName:       "mykey",
	Algorithm:     "RSA",
})
if err != nil {
	panic(err)
}

// 读取流并将其复制到输出文件
n, err := io.Copy(wf, out)
if err != nil {
	panic(err)
}
fmt.Println("Written", n, "bytes")

下面的示例使用 Encrypt API 来加密字符串。

// 输入字符串
rf := strings.NewReader("Amor, ch'a nullo amato amar perdona, mi prese del costui piacer sì forte, che, come vedi, ancor non m'abbandona")

// 使用 Dapr 加密数据
enc, err := sdkClient.Encrypt(context.Background(), rf, dapr.EncryptOptions{
	ComponentName: "mycryptocomponent",
	KeyName:       "mykey",
	Algorithm:     "RSA",
})
if err != nil {
	panic(err)
}

// 将加密数据读入字节切片
enc, err := io.ReadAll(enc)
if err != nil {
	panic(err)
}

解密

要解密数据流,请使用 decrypt

def encrypt_decrypt_string(dapr: DaprClient):
    message = 'The secret is "passw0rd"'

    # ...

    # 解密加密的数据
    resp = dapr.decrypt(
        data=encrypt_bytes,
        options=DecryptOptions(
            # Cryptography 组件的名称(必需)
            component_name=CRYPTO_COMPONENT_NAME,
            # 存储在 cryptography 组件中的密钥(必需)
            key_name=RSA_KEY_NAME,
        ),
    )

    # 该方法返回一个可读流,我们在内存中完整读取它
    decrypt_bytes = resp.read()
    print(f'Decrypted the message, got {len(decrypt_bytes)} bytes')

    print(decrypt_bytes.decode())
    assert message == decrypt_bytes.decode()

使用 Dapr SDK,您可以解密缓冲区中的数据或使用流。

// 当将数据作为缓冲区传递时,`decrypt` 返回包含解密消息的 Buffer
const plaintext = await client.crypto.decrypt(ciphertext, {
    // 唯一必需的选项是组件名称
    componentName: "mycryptocomponent",
});

// `decrypt` 也可以用作 Duplex 流
await pipeline(
    fs.createReadStream("ciphertext.out"),
    await client.crypto.decrypt({
        // 唯一必需的选项是组件名称
        componentName: "mycryptocomponent",
    }),
    fs.createWriteStream("plaintext.out"),
);

要解密字符串,请在您的项目中使用 ‘DecryptAsync’ gRPC API。

在下面的示例中,我们将获取一个字节数组(例如来自上面的示例)并将其解密为 UTF-8 编码的字符串。

public async Task<string> DecryptBytesAsync(byte[] encryptedBytes)
{
  using var client = new DaprClientBuilder().Build();

  const string componentName = "azurekeyvault"; // 更改此项以匹配您的 cryptography 组件
  const string keyName = "myKey"; // 更改此项以匹配您的加密存储中密钥的名称

  var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytes, keyName);
  var decryptedString = Encoding.UTF8.GetString(decryptedBytes.ToArray());
  return decryptedString;
}

要解密文件,请在您的项目中使用 Decrypt gRPC API。

在下面的示例中,out 是一个可以写入文件或在内存中读取的流,如上面的示例所示。

out, err := sdkClient.Decrypt(context.Background(), rf, dapr.EncryptOptions{
	// 唯一必需的选项是组件名称
	ComponentName: "mycryptocomponent",
})

后续步骤

Cryptography 组件规范