

# 使用 CloudFront Functions 和 KeyValueStore 为双向 TLS（查看器）实施证书吊销
<a name="implement-certificate-revocation"></a>

您可以将 CloudFront 连接函数与 KeyValueStore 结合使用来实施证书吊销检查。这可让您维护已吊销证书序列号列表，并在 TLS 握手过程中根据此列表检查客户端证书。

要实施证书吊销，您需要以下组件：
+ 已配置查看器 mTLS 的分配
+ 包含已吊销证书序列号的 KeyValueStore
+ 一个连接函数，用于查询 KeyValueStore 以检查证书状态

当客户端连接时，CloudFront 会根据信任存储验证证书，然后运行您的连接函数。您的函数会根据 KeyValueStore 检查证书序列号，并允许或拒绝连接。

**Topics**
+ [第 1 步：为已吊销证书创建 KeyValueStore](create-kvs-revoked-certificates.md)
+ [第 2 步：创建吊销连接函数](create-revocation-connection-function.md)
+ [第 3 步：测试吊销函数](test-revocation-function.md)
+ [第 4 步：将函数关联到分配](associate-function-distribution.md)
+ [高级吊销场景](advanced-revocation-scenarios.md)

# 第 1 步：为已吊销证书创建 KeyValueStore
<a name="create-kvs-revoked-certificates"></a>

创建 KeyValueStore 来存储已吊销证书的序列号，连接函数可以在 mTLS 连接期间检查这些序列号。

首先，以 JSON 格式准备已吊销证书的序列号：

```
{
  "data": [
    {
      "key": "ABC123DEF456",
      "value": ""
    },
    {
      "key": "789XYZ012GHI", 
      "value": ""
    }
  ]
}
```

将此 JSON 文件上传到 S3 存储桶，然后创建 KeyValueStore：

```
aws s3 cp revoked-serials.json s3://your-bucket-name/revoked-serials.json
aws cloudfront create-key-value-store \
  --name revoked-serials-kvs \
  --import-source '{
    "SourceType": "S3",
    "SourceARN": "arn:aws:s3:::your-bucket-name/revoked-serials.json"
  }'
```

等待 KeyValueStore 完成预调配。通过以下方式查看状态：

```
aws cloudfront get-key-value-store --name "revoked-serials-kvs"
```

# 第 2 步：创建吊销连接函数
<a name="create-revocation-connection-function"></a>

创建一个连接函数，该函数可根据 KeyValueStore 中的数据检查证书序列号，以确定是否吊销证书。

创建一个连接函数，该函数会根据 KeyValueStore 中的数据检查证书序列号：

```
aws cloudfront create-connection-function \
  --name "revocation-control" \
  --connection-function-config file://connection-function-config.json \
  --connection-function-code file://connection-function-code.txt
```

配置文件用于指定 KeyValueStore 关联：

```
{
  "Runtime": "cloudfront-js-2.0",
  "Comment": "A function that implements revocation control via KVS",
  "KeyValueStoreAssociations": {
    "Quantity": 1,
    "Items": [
      {
        "KeyValueStoreArn": "arn:aws:cloudfront::account-id:key-value-store/kvs-id"
      }
    ]
  }
}
```

连接函数代码会检查 KeyValueStore 中是否有已吊销证书：

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Get parsed client serial number from client certificate
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    
    // Check KVS to see if serial number exists as a key
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    // Deny connection if serial number exists in KVS
    if (serialNumberExistsInKvs) {
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Allow connections that don't exist in kvs
    console.log("Connection allowed");
    return connection.allow();
}
```

# 第 3 步：测试吊销函数
<a name="test-revocation-function"></a>

使用 CloudFront 控制台，通过示例证书测试您的连接函数。在控制台中导航到连接函数，然后使用“测试”选项卡。

**使用示例证书进行测试**

1. 将 PEM 格式的示例证书粘贴到测试界面中

1. （可选）指定用于测试基于 IP 的逻辑的客户端 IP 地址

1. 选择**测试函数**以查看函数执行结果

1. 查看执行日志以验证函数逻辑

使用有效证书和已吊销证书进行测试，确保您的函数可以正确处理这两种场景。执行日志显示 console.log 输出以及函数执行期间出现的任何错误。

# 第 4 步：将函数关联到分配
<a name="associate-function-distribution"></a>

在发布连接函数后，将其与已启用 mTLS 的分配相关联，以激活证书吊销检查。

您可以从分配设置页面或连接函数的关联分配表中关联该函数。导航到您的分配设置，滚动到**查看器双向身份验证（mTLS）**部分，选择您的连接函数，然后保存更改。

# 高级吊销场景
<a name="advanced-revocation-scenarios"></a>

对于更复杂的证书吊销要求，请考虑以下其他配置：

**Topics**
+ [将证书吊销列表（CRL）转换为 KeyValueStore 格式](#convert-crl-kvs-format)
+ [处理多个证书颁发机构](#handle-multiple-cas)
+ [将自定义数据添加到连接日志](#add-custom-data-logs)
+ [管理 CRL 更新](#manage-crl-updates)
+ [规划 KeyValueStore 容量](#plan-kvs-capacity)

## 将证书吊销列表（CRL）转换为 KeyValueStore 格式
<a name="convert-crl-kvs-format"></a>

如果您拥有证书吊销列表（CRL）文件，则可以使用 OpenSSL 和 jq 将其转换为 KeyValueStore JSON 格式：

**将 CRL 转换为 KeyValueStore 格式**

从 CRL 文件中提取序列号：

```
openssl crl -text -noout -in rfc5280_CRL.crl | \
  awk '/Serial Number:/ {print $3}' | \
  cut -d'=' -f2 | \
  sed 's/../&:/g;s/:$//' >> serialnumbers.txt
```

将序列号转换为 KeyValueStore JSON 格式：

```
jq -R -s 'split("\n") | map(select(length > 0)) | {data: map({"key": ., "value": ""})}' \
  serialnumbers.txt >> serialnumbers_kvs.json
```

将格式化的文件上传到 S3，然后按照第 1 步的说明创建 KeyValueStore。

## 处理多个证书颁发机构
<a name="handle-multiple-cas"></a>

当您的 TrustStore 包含多个证书颁发机构（CA）时，请在您的 KeyValueStore 键中包含颁发者信息，以避免来自不同 CA 的序列号相同的证书之间发生冲突。

对于多 CA 场景，请使用颁发者的 SHA1 哈希和序列号的组合作为键：

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientCert = connection.clientCertInfo;
    
    // Create composite key with issuer hash and serial number
    const issuer = clientCert.issuer.replace(/[^a-zA-Z0-9]/g, '').substring(0, 20);
    const serialno = clientCert.serialNumber;
    const compositeKey = `${issuer}_${serialno}`;
    
    const cert_revoked = await kvsHandle.exists(compositeKey);
    
    if (cert_revoked) {
        console.log(`Blocking revoked cert: ${serialno} from issuer: ${issuer}`);
        connection.deny();
    } else {
        connection.allow();
    }
}
```

**注意**  
使用颁发者标识符 \$1 序列号可创建较长的键，这可能会减少可存储在 KeyValueStore 中的条目总数。

## 将自定义数据添加到连接日志
<a name="add-custom-data-logs"></a>

连接函数可以使用 logCustomData 方法将自定义数据添加到 CloudFront 连接日志。这使您能够在日志中包含吊销检查结果、证书信息或其他相关数据。

```
async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    if (serialNumberExistsInKvs) {
        // Log revocation details to connection logs
        connection.logCustomData(`REVOKED:${clientSerialNumber}:DENIED`);
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Log successful validation
    connection.logCustomData(`VALID:${clientSerialNumber}:ALLOWED`);
    console.log("Connection allowed");
    return connection.allow();
}
```

自定义数据的限制为 800 字节的有效 UTF-8 文本。如果超过此限制，CloudFront 会将数据截断至最近的有效 UTF-8 边界。

**注意**  
仅在为分配启用连接日志时，自定义数据日志记录才起作用。如果未配置连接日志，则 logCustomData 方法将不执行任何操作。

## 管理 CRL 更新
<a name="manage-crl-updates"></a>

证书颁发机构可颁发两种类型的 CRL：
+ **完整 CRL**：包含所有已吊销证书的完整列表
+ **Delta CRL**：仅列出自上次完整 CRL 以来吊销的证书

对于完整 CRL 更新，使用更新后的数据创建一个新的 KeyValueStore，然后将连接函数关联重定向至此新的 KeyValueStore。这种方法比计算差异并执行增量更新更加简单。

对于 delta CRL 更新，使用 update-keys 命令将新的已吊销证书添加到现有 KeyValueStore：

```
aws cloudfront update-key-value-store \
  --name "revoked-serials-kvs" \
  --if-match "current-etag" \
  --put file://delta-revoked-serials.json
```

## 规划 KeyValueStore 容量
<a name="plan-kvs-capacity"></a>

KeyValueStore 的大小限制为 5 MB，并且最多支持 1000 万个键值对。根据您的键格式和数据大小规划吊销列表容量：
+ **仅序列号**：高效存储，用于进行简单吊销检查
+ **颁发者标识符 \$1 序列号**：用于多 CA 环境的较长键

对于大型吊销列表，可以考虑实施分层方法，为不同的证书类别或时段维护单独的 KeyValueStore。