

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 X.509 客户端证书进行自定义身份验证
<a name="custom-auth-509cert"></a>

将设备连接到时 AWS IoT Core，您有多种[身份验证类型](protocols.md#connection-protocol-auth-mode)可用。您可以使用可用于对客户端和设备连接进行身份验证的 [X.509 客户端证书](https://docs.aws.amazon.com//iot/latest/developerguide/x509-client-certs.html)，也可以定义[自定义授权方](https://docs.aws.amazon.com//iot/latest/developerguide/custom-authentication.html)来管理自己的客户端身份验证和授权逻辑。本主题介绍如何使用 X.509 客户端证书进行自定义身份验证。

如果您已经使用 X.509 证书对设备进行了身份验证，并且想要执行额外的验证和自定义授权，则使用 X.509 证书进行自定义身份验证会很有帮助。例如，如果您将设备的数据（例如序列号）存储在 X.509 客户端证书中，则在对 X.509 客户端证书 AWS IoT Core 进行身份验证后，您可以使用自定义授权方根据证书字段中存储的信息来识别特定设备。 CommonName 将自定义身份验证与 X.509 证书配合使用可以增强设备在连接设备时的安全管理，并为管理身份验证 AWS IoT Core 和授权逻辑提供更大的灵活性。 AWS IoT Core [https://docs.aws.amazon.com//iot/latest/developerguide/mqtt.html](https://docs.aws.amazon.com//iot/latest/developerguide/mqtt.html)有关 AWS IoT Core 设备端点支持的身份验证类型和应用程序协议的更多信息，请参阅[设备通信协议](https://docs.aws.amazon.com//iot/latest/developerguide/protocols.html)。

**注意**  
各区域不支持使用 X.509 客户端证书进行自定义身份验证。 AWS GovCloud (US) 

**重要**  
您必须使用通过[域配置](iot-custom-endpoints-configurable.md)创建的端点。此外，客户端在连接到时必须提供[服务器名称指示 (SNI)](https://www.rfc-editor.org/rfc/rfc3546#section-3.1) 扩展名。 AWS IoT Core

**Topics**
+ [第 1 步：使用注册您的 X.509 客户端证书 AWS IoT Core](#custom-auth-509cert-client)
+ [第 2 步：创建 Lambda 函数](#custom-auth-509cert-lambda)
+ [步骤 3：创建自定义授权方](#custom-auth-509cert-authorizer)
+ [步骤 4：在域配置中设置身份验证类型和应用程序协议](#custom-auth-509cert-domainconfig)

## 第 1 步：使用注册您的 X.509 客户端证书 AWS IoT Core
<a name="custom-auth-509cert-client"></a>

如果您尚未执行此操作，请使用注册并激活您的 [X.509 客户端证书](https://docs.aws.amazon.com//iot/latest/developerguide/x509-client-certs.html)。 AWS IoT Core否则，请跳到下一步。

要使用注册和激活您的客户端证书 AWS IoT Core，请按照以下步骤操作：

1. 如果您[直接使用创建客户证书 AWS IoT](https://docs.aws.amazon.com//iot/latest/developerguide/device-certs-create.html)。这些客户证书将自动向注册 AWS IoT Core。

1. 如果您[创建自己的客户证书](https://docs.aws.amazon.com//iot/latest/developerguide/device-certs-your-own.html)，请按照[以下说明进行注册 AWS IoT Core](https://docs.aws.amazon.com//iot/latest/developerguide/register-device-cert.html)。

1. 要激活您的客户端证书，请按照[以下说明](https://docs.aws.amazon.com//iot/latest/developerguide/activate-or-deactivate-device-cert.html)进行操作。

## 第 2 步：创建 Lambda 函数
<a name="custom-auth-509cert-lambda"></a>

AWS IoT Core 使用自定义授权方来实现自定义身份验证和授权方案。自定义授权方与 Lambda 函数相关联，该函数确定设备是否经过身份验证以及允许该设备执行哪些操作。当设备连接到时，会 AWS IoT Core 检索授权方详细信息 AWS IoT Core，包括授权方名称和关联的 Lambda 函数，并调用 Lambda 函数。Lambda 函数会收到一个事件，其中包含一个 JSON 对象以及设备的 X.509 客户端证书数据。您的 Lambda 函数使用此事件 JSON 对象来评估身份验证请求、决定要采取的操作并发回响应。

### Lambda 函数事件示例
<a name="custom-auth-509cert-event"></a>

以下示例 JSON 对象包含可以包含的所有可能字段。实际的 JSON 对象将仅包含与特定连接请求相关的字段。

```
{
	"token": "aToken",
	"signatureVerified": true,
	"protocols": [
		"tls",
		"mqtt"
	],
	"protocolData": {
		"tls": {
			"serverName": "serverName",
			"x509CertificatePem": "x509CertificatePem",
			"principalId": "principalId"
		},
		"mqtt": {
			"clientId": "myClientId",
                     "username": "myUserName",
                     "password": "myPassword"
		}
	},
	"connectionMetadata": {
		"id": "UUID"
	}
}
```

`signatureVerified`  
一个布尔值，用于指示在调用授权方的 Lambda 函数之前是否验证了在授权方配置的令牌签名。如果授权方配置为禁用令牌签名，则此字段将为 false。

`protocols`  
包含请求预期协议的数组。

`protocolData`  
包含连接中所使用的协议信息的对象。它提供了特定于协议的详细信息，对于身份验证、授权等非常有用。  
`tls` - 此对象包含与 TLS（传输层安全性）协议相关的信息。  
+ `serverName` - [服务器名称指示（SNI）](https://www.rfc-editor.org/rfc/rfc3546#section-3.1)主机名字符串。 AWS IoT Core 要求设备将 [SNI 扩展](https://www.rfc-editor.org/rfc/rfc3546#section-3.1)发送到传输层安全性（TLS）协议，并在 `host_name` 字段中提供完整的端点地址。
+ `x509CertificatePem` - PEM 格式的 X.509 证书，用于在 TLS 连接中进行客户端身份验证。
+ `principalId` - 在 TLS 连接中与客户端关联的主体标识符。
`mqtt` - 此对象保存与 MQTT 协议相关的信息。  
+ `clientId` - 只有在设备发送该值的事件中才需要包含的一个字符串。
+ `username` - MQTT Connect 数据包中提供的用户名。
+ `password` - MQTT Connect 数据包中提供的密码。

`connectionMetadata`  
连接的元数据。  
`id` - 连接 ID，可用于日志记录和故障排除。

**注意**  
在本事件 JSON 对象中，`x509CertificatePem` 和 `principalId` 是请求中的两个新字段。`principalId` 的值与 `certificateId` 的值相同。有关更多信息，请参阅[证书](https://docs.aws.amazon.com//iot/latest/apireference/API_Certificate.html)。

### Lambda 函数响应示例
<a name="custom-auth-509cert-response"></a>

Lambda 函数应使用来自事件 JSON 对象的信息，对传入连接进行身份验证，并决定允许在连接中执行哪些操作。

以下 JSON 对象包含 Lambda 函数可以发送的响应示例。

```
{
	"isAuthenticated": true,
	"principalId": "xxxxxxxx",
	"disconnectAfterInSeconds": 86400,
	"refreshAfterInSeconds": 300,
	"policyDocuments": [
		{
			"Version": "2012-10-17",		 	 	 
			"Statement": [
				{
					"Effect": "Allow",
					"Action": "iot:Publish",
					"Resource": "arn:aws:iot:us-east-1:123456789012:topic/customauthtesting"
				}
			]
		}
	]
}
```

在本示例中，该函数应发送包含以下值的响应。

`isAuthenticated`  
一个布尔值，指示是否已对请求进行身份验证。

`principalId`  
一个字母数字字符串，它充当自定义授权请求发送的令牌的标识符。该值必须是包含至少一个字符且不超过 128 个字符的字母数字字符串。它在日志中标识连接。`principalId` 的值必须与事件 JSON 对象中的 `principalId` 的值相同（即 X.509 证书的 certificateId）。

`policyDocuments`  
JSON 格式的 AWS IoT Core 策略文件列表。该值是可选的，支持[事物策略变量](https://docs.aws.amazon.com//iot/latest/developerguide/thing-policy-variables.html)和[证书策略变量](https://docs.aws.amazon.com//iot/latest/developerguide/cert-policy-variables.html)。策略文档的数量最多为 10 个。每个策略文档最多可以包含 2048 个字符。如果您的客户端证书和 Lambda 函数附加了多个策略，则该权限是所有策略的集合。有关创建 AWS IoT Core 策略的更多信息，请参阅[策略](https://docs.aws.amazon.com//iot/latest/developerguide/iot-policies.html)。

`disconnectAfterInSeconds`  
一个整数，它指定 AWS IoT Core 网关连接的最大持续时间（以秒为单位）。最小值为 300 秒，最大值为 86400 秒。`disconnectAfterInSeconds` 在连接的生命周期内有效，连续刷新策略时不会刷新。

`refreshAfterInSeconds`  
指定策略刷新之间间隔的整数。此间隔过后， AWS IoT Core 调用 Lambda 函数以允许刷新策略。最小值为 300 秒，最大值为 86400 秒。

### 示例 Lambda 函数
<a name="custom-auth-509cert-js-example"></a>

下面是一个 Node.js Lambda 函数示例。该函数检查客户端的 X.509 证书并提取相关信息，例如序列号、指纹和主题名称。如果提取的信息与预期值相匹配，则授予客户端连接访问权限。此机制可确保只有拥有有效证书的授权客户端才能建立连接。

```
const crypto = require('crypto');

exports.handler = async (event) => {
    
    // Extract the certificate PEM from the event
    const certPem = event.protocolData.tls.x509CertificatePem;
    
    // Parse the certificate using Node's crypto module
    const cert = new crypto.X509Certificate(certPem);
    
    var effect = "Deny";
    // Allow permissions only for a particular certificate serial, fingerprint, and subject
    if (cert.serialNumber === "7F8D2E4B9C1A5036DE8F7C4B2A91E5D80463BC9A1257" // This is a random serial
       && cert.fingerprint === "F2:9A:C4:1D:B5:E7:08:3F:6B:D0:4E:92:A7:C1:5B:8D:16:0F:E3:7A" // This is a random fingerprint
       && cert.subject === "allow.example.com") {
      effect = "Allow";
    }
    
    return generateAuthResponse(event.protocolData.tls.principalId, effect);
};


// Helper function to generate the authorization response.
function generateAuthResponse(principalId, effect) {
    const authResponse = {
        isAuthenticated: true,
        principalId,
        disconnectAfterInSeconds: 3600,
        refreshAfterInSeconds: 300,
        policyDocuments: [
          {
            Version: "2012-10-17",		 	 	 
            Statement: [
              {
                Action: ["iot:Connect"],
                Effect: effect,
                Resource: [
                  "arn:aws:iot:us-east-1:123456789012:client/myClientName"
                ]
              },
              {
                Action: ["iot:Publish"],
                Effect: effect,
                Resource: [
                  "arn:aws:iot:us-east-1:123456789012:topic/telemetry/myClientName"
                ]
              },
              {
                Action: ["iot:Subscribe"],
                Effect: effect,
                Resource: [
                   "arn:aws:iot:us-east-1:123456789012:topicfilter/telemetry/myClientName"
                ]
              },
              {
                Action: ["iot:Receive"],
                Effect: effect,
                Resource: [
                   "arn:aws:iot:us-east-1:123456789012:topic/telemetry/myClientName"
                ]
              }
            ]
          }
        ]
      };

  return authResponse;
}
```

上述 Lambda 函数在收到包含预期序列号、指纹和主题的证书时返回以下 JSON。`x509CertificatePem` 的值将是 TLS 握手中提供的客户端证书。有关更多信息，请参阅[定义 Lambda 函数](https://docs.aws.amazon.com//iot/latest/developerguide/config-custom-auth.html#custom-auth-lambda)。

```
{
	"isAuthenticated": true,
	"principalId": "principalId in the event JSON object",
	"policyDocuments": [
		{
			"Version": "2012-10-17",		 	 	 
			"Statement": [
				{
					"Action": "iot:Connect",
					"Effect": "Allow",
					"Resource": "arn:aws:iot:us-east-1:123456789012:client/myClientName"
				},
				{
					"Action": "iot:Publish",
					"Effect": "Allow",
					"Resource": "arn:aws:iot:us-east-1:123456789012:topic/telemetry/myClientName"
				},
				{
					"Action": "iot:Subscribe",
					"Effect": "Allow",
					"Resource": "arn:aws:iot:us-east-1:123456789012:topicfilter/telemetry/myClientName"
				},
				{
					"Action": "iot:Receive",
					"Effect": "Allow",
					"Resource": "arn:aws:iot:us-east-1:123456789012:topic/telemetry/myClientName"
				}
			]
		}
	],
	"disconnectAfterInSeconds": 3600,
	"refreshAfterInSeconds": 300
}
```

## 步骤 3：创建自定义授权方
<a name="custom-auth-509cert-authorizer"></a>

[定义 Lambda 函数](#custom-auth-509cert-lambda)后，创建自定义授权方来管理您自己的客户端身份验证和授权逻辑。您可以按照[步骤 3：创建客户授权方资源及其授权](https://docs.aws.amazon.com//iot/latest/developerguide/custom-auth-tutorial.html#custom-auth-tutorial-authorizer)中的详细说明进行操作。有关更多信息，请参阅[创建授权方](https://docs.aws.amazon.com//iot/latest/developerguide/config-custom-auth.html)。

在创建自定义授权方的过程中，您必须授予 AWS IoT 在创建 Lambda 函数后调用该函数的权限。有关详细说明，请参阅[授权 AWS IoT 调用 Lambda](custom-auth-authorize.md) 函数。

## 步骤 4：在域配置中设置身份验证类型和应用程序协议
<a name="custom-auth-509cert-domainconfig"></a>

要结合 X.509 客户端证书使用自定义身份验证对设备进行身份验证，必须在域配置中设置身份验证类型和应用程序协议，并且必须发送 SNI 扩展。`authenticationType` 的值必须为 `CUSTOM_AUTH_X509`，`applicationProtocol` 的值可以是 `SECURE_MQTT` 或 `HTTPS`。

### 在域配置中设置身份验证类型和应用程序协议（CLI）
<a name="custom-auth-509cert-cli"></a>

如果您没有域配置，请使用 [https://docs.aws.amazon.com//cli/latest/reference/iot/create-domain-configuration.html](https://docs.aws.amazon.com//cli/latest/reference/iot/create-domain-configuration.html) 命令创建一个。`authenticationType` 的值必须为 `CUSTOM_AUTH_X509`，`applicationProtocol` 的值可以是 `SECURE_MQTT` 或 `HTTPS`。

```
aws iot create-domain-configuration \
    --domain-configuration-name domainConfigurationName \
    --authentication-type CUSTOM_AUTH_X509 \  
    --application-protocol SECURE_MQTT \ 
    --authorizer-config '{
        "defaultAuthorizerName": my-custom-authorizer
    }'
```

如果您已经有域配置，请根据需要使用 [https://docs.aws.amazon.com//cli/latest/reference/iot/update-domain-configuration.html](https://docs.aws.amazon.com//cli/latest/reference/iot/update-domain-configuration.html) 命令更新 `authenticationType` 和 `applicationProtocol`。请注意，您不能更改默认端点 (`iot:Data-ATS`) 上的身份验证类型或协议。

```
aws iot update-domain-configuration \
    --domain-configuration-name domainConfigurationName \
    --authentication-type CUSTOM_AUTH_X509 \  
    --application-protocol SECURE_MQTT \
    --authorizer-config '{
        "defaultAuthorizerName": my-custom-authorizer
    }'
```

`domain-configuration-name`  
域配置的名称。

`authentication-type`  
域配置的身份验证类型。有关更多信息，请参阅[选择身份验证类型](protocols.md#connection-protocol-auth-mode)。

`application-protocol`  
设备用来与 AWS IoT Core进行通信的应用程序协议。有关更多信息，请参阅[选择应用程序协议](protocols.md#protocol-selection)。

`--authorizer-config`  
指定域配置中的授权方配置的对象。

`defaultAuthorizerName`  
域配置的授权方的名称。

有关更多信息，请参阅 *AWS IoT API 参考[UpdateDomainConfiguration](https://docs.aws.amazon.com//iot/latest/apireference/API_UpdateDomainConfiguration.html)*中的[CreateDomainConfiguration](https://docs.aws.amazon.com//iot/latest/apireference/API_CreateDomainConfiguration.html)和。有关域配置的更多信息，请参阅[域配置](https://docs.aws.amazon.com//iot/latest/developerguide/iot-custom-endpoints-configurable.html)。