☰
Moto 中 ACM Private CA(acm-pca)服务模拟实现全解析:创建 CA、签发证书与吊销机制的完整实战指南
2026/9/25 2:52:55 网站建设 项目流程
  • Mock
  • 测试

【免费下载链接】moto

A library that allows you to easily mock out tests based on AWS infrastructure.

项目地址:https://gitcode.com/gh_mirrors/mo/moto
点击查看免费下载

导读

本文以 Moto 仓库中 acm-pca 服务文档 为核心,系统讲解如何在本地开发与测试中完整模拟 AWS Private Certificate Authority(ACM Private CA / acm-pca)服务:从创建私有 CA、获取 CSR、签发根证书与终端实体证书,到吊销证书、管理标签与资源策略的完整生命周期。读完本文,你将掌握该服务在 Moto 中已实现的全部 API 用法、未实现参数的真实行为边界,以及其基于cryptography库的底层实现原理,可直接用 boto3 在测试中复现真实的 PKI 工作流。

一、服务概述:Moto 如何模拟 acm-pca

Moto 的 acm-pca 模拟位于moto/acmpca/目录,遵循 Moto 标准的「Backend(模型层)— Response(协议层)— URL(路由层)」三段式架构:

  • models.py:定义ACMPCABackend与CertificateAuthority模型,持有全部状态与业务逻辑;
  • responses.py:解析 boto3 发来的 JSON 请求体,调用 Backend 方法并序列化响应;
  • urls.py:注册https?://acm-pca\.(.+)\.amazonaws\.com端点,将请求分发给ACMPCAResponse.dispatch,同时在 backend_index.py 中完成服务路由注册。

与真实 AWS 一样,Moto 的 CA 状态机也遵循「先创建(PENDING_CERTIFICATE)→ 签发并导入证书(ACTIVE)→ 可更新(DISABLED)→ 可删除(DELETED)」的流转路径。在测试中,只需为 boto3 客户端加上@mock_aws装饰器,即可让boto3.client("acm-pca")的所有请求落到本地模拟实现上。

二、功能覆盖总览

依据 docs/docs/services/acm-pca.rst 的实现清单,目前共实现 15 个 API,未实现 5 个,具体如下:

状态API说明
✅ 已实现create_certificate_authority创建私有 CA(部分参数未实现,见下文)
❌ 未实现create_certificate_authority_audit_report—
❌ 未实现create_permission—
✅ 已实现delete_certificate_authority将 CA 状态置为DELETED
❌ 未实现delete_permission—
✅ 已实现delete_policy删除附加到私有 CA 的资源策略
✅ 已实现describe_certificate_authority返回 CA 详情
❌ 未实现describe_certificate_authority_audit_report—
✅ 已实现get_certificate获取已签发证书与证书链
✅ 已实现get_certificate_authority_certificate获取 CA 自身的证书与链
✅ 已实现get_certificate_authority_csr获取 CA 的 CSR
✅ 已实现get_policy检索附加到私有 CA 的资源策略
✅ 已实现import_certificate_authority_certificate导入 CA 证书使其激活
✅ 已实现issue_certificate签发证书(部分参数未实现,见下文)
✅ 已实现list_certificate_authorities列出私有 CA(支持分页与ResourceOwner过滤)
❌ 未实现list_permissions—
✅ 已实现list_tags列出标签(分页未实现)
✅ 已实现put_policy附加资源策略到私有 CA
❌ 未实现restore_certificate_authority—
✅ 已实现revoke_certificate吊销证书
✅ 已实现tag_certificate_authority/untag_certificate_authority增删标签

文档同时明确标注了两处参数缺口:create_certificate_authority未实现IdempotencyToken、KeyStorageSecurityStandard、UsageMode三个参数;issue_certificate未实现ApiPassthrough、SigningAlgorithm、Validity、ValidityNotBefore、IdempotencyToken五个参数,且证书的部分字段将采用默认值而非完全取自 CSR。后文将逐一说明这些缺口的实际影响。

三、创建私有 CA:create_certificate_authority

创建 CA 是全部 PKI 流程的起点。以下代码直接取自仓库测试 tests/test_acmpca/test_acmpca.py,可原样运行:

import boto3 from moto import mock_aws @mock_aws def test_create_certificate_authority(): client = boto3.client("acm-pca", region_name="eu-west-1") resp = client.create_certificate_authority( CertificateAuthorityConfiguration={ "KeyAlgorithm": "RSA_4096", "SigningAlgorithm": "SHA512WITHRSA", "Subject": {"CommonName": "yscb41lw.test"}, }, CertificateAuthorityType="SUBORDINATE", IdempotencyToken="terraform-20221125230308947400000001", ) print(resp["CertificateAuthorityArn"]) # 输出形如:arn:aws:acm-pca:eu-west-1:123456789012:certificate-authority/<uuid>

3.1 核心参数说明

  • CertificateAuthorityConfiguration(必填):含KeyAlgorithm(如RSA_4096)、SigningAlgorithm(如SHA512WITHRSA)与Subject。Subject支持CommonName、Country、State、Organization、OrganizationalUnit等字段,模型层在 models.py 的issuer属性中按固定顺序映射为 X.509 名称属性。
  • CertificateAuthorityType(必填):ROOT或SUBORDINATE。
  • RevocationConfiguration(可选):吊销配置,详见下文 CRL 配置一节。
  • Tags(可选):创建时即打标签,backend 会同步到内部TaggingService(见 models.py)。

3.2 未实现参数的真实行为

文档明确声明IdempotencyToken、KeyStorageSecurityStandard、UsageMode尚未实现。从 models.py 的签名可以看出,backend 的create_certificate_authority只接收配置、吊销配置、类型、安全标准与标签五个参数——IdempotencyToken被 boto3 侧直接忽略,重复请求会创建新的 CA 而非幂等复用;UsageMode在模型中被固定为"SHORT_LIVED_CERTIFICATE"(models.py);而KeyStorageSecurityStandard参数虽然会被接收并透传,但即便不传,模型也会默认赋值为"FIPS_140_2_LEVEL_3_OR_HIGHER"(models.py),测试test_describe_certificate_authority验证了这一默认值。

3.3 创建后的内部状态

每次创建时,模型会同步执行以下初始化(见 models.py):

  • 生成一个随机 UUID 作为 CA 的id,并组装出标准 ARN:arn:{partition}:acm-pca:{region}:{account_id}:certificate-authority/{id};
  • 用cryptography生成RSA-2048 私钥(rsa.generate_private_key(public_exponent=65537, key_size=2048)),并以随机口令做 PEM 加密存储,私钥字节以BestAvailableEncryption方式持久化,保证 backend 可被 pickle 序列化(测试test_acmpca_backend_is_serialisable专门验证了这一点);
  • 状态初始为PENDING_CERTIFICATE,记录created_at时间戳;
  • 吊销配置默认值为{"CrlConfiguration": {"Enabled": False}}。

四、CA 生命周期:获取 CSR 并激活 CA

4.1 获取 CSR:get_certificate_authority_csr

创建后 CA 处于PENDING_CERTIFICATE,需要先取出 CSR 自签根证书才能激活:

csr = client.get_certificate_authority_csr(CertificateAuthorityArn=ca_arn)["Csr"] # csr 为 PEM 格式的字符串,可直接交给 openssl 或 cryptography 解析

从 models.py 看,CSR 由x509.CertificateSigningRequestBuilder生成,主体使用 CA 的issuer名称(即创建时传入的Subject),并附带 critical 的BasicConstraints(ca=True, path_length=None)扩展,用 SHA256 签名。测试test_get_certificate_authority_csr验证了 CSR 中CommonName、Country、State、Organization、OrganizationalUnit各字段与创建参数一致。

4.2 签发根证书并导入激活

激活 CA 的标准流程分三步:先issue_certificate用根 CA 模板签发自身证书,再get_certificate取回证书,最后import_certificate_authority_certificate导入:

# 1) 签发根证书 cert_arn = client.issue_certificate( CertificateAuthorityArn=ca_arn, Csr=csr, SigningAlgorithm="SHA256WITHRSA", TemplateArn="arn:aws:acm-pca:::template/RootCACertificate/V1", Validity={"Type": "YEARS", "Value": 10}, )["CertificateArn"] # 2) 取回证书 PEM ca_cert = client.get_certificate( CertificateAuthorityArn=ca_arn, CertificateArn=cert_arn )["Certificate"] # 3) 导入证书,CA 状态变为 ACTIVE client.import_certificate_authority_certificate( CertificateAuthorityArn=ca_arn, Certificate=ca_cert )

import_certificate_authority_certificate的实现(models.py)会先尝试解析 PEM 证书,若解析失败则抛出MalformedCertificateAuthorityException(测试用"invalid certificate pem"验证);成功后写入证书字节、可选证书链,并将状态置为ACTIVE、刷新updated_at。导入后,describe_certificate_authority返回的NotBefore/NotAfter字段也会随证书填充(见 models.py)。

4.3 状态约束

注意get_certificate_authority_certificate(获取 CA 自身证书)要求 CA 状态为ACTIVE,否则抛出InvalidStateException(models.py);测试test_get_certificate_authority_certificate验证了在PENDING_CERTIFICATE状态下调用会返回错误码InvalidStateException及对应错误消息。而get_certificate_authority_csr则不受状态限制,随时可调用。

五、签发终端实体证书:issue_certificate

CA 激活后即可对外签发证书。核心实现位于 models.py 的issue_certificate与 models.py 的_x509_extensions:

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) ee_csr = create_csr(private_key, "US", "WA", "BezosCorp", "bezoscorp.com") # 自行生成终端实体 CSR ee_cert_arn = client.issue_certificate( CertificateAuthorityArn=ca_arn, Csr=ee_csr, SigningAlgorithm="SHA256WITHRSA", Validity={"Type": "YEARS", "Value": 1}, )["CertificateArn"]

5.1 模板(TemplateArn)驱动的扩展逻辑

实现区分两类模板,并据此注入不同的 X.509 扩展:

  • arn:aws:acm-pca:::template/RootCACertificate/V1:注入 critical 的BasicConstraints(ca=True, path_length=None)、KeyUsage(crl_sign, key_cert_sign, digital_signature)以及SubjectKeyIdentifier;
  • arn:aws:acm-pca:::template/EndEntityCertificate/V1(或未传 TemplateArn):注入BasicConstraints(ca=False)、AuthorityKeyIdentifier(取自已导入的 CA 公钥)、SubjectKeyIdentifier、KeyUsage(digital_signature, key_encipherment)以及含SERVER_AUTH/CLIENT_AUTH的ExtendedKeyUsage。

此外,CSR 中的 Subject Alternative Name(SAN)扩展会被原样透传到新证书(models.py)。测试test_end_entity_certificate_issuance验证了 SAN 中的 DNS、邮箱(RFC822)、OtherName 与 URI 均被保留。

5.2 签发产物的内部行为

  • 新证书用 SHA512 签名,有效期固定为自当前时间起 365 天(models.py),即文档所述「证书部分字段使用默认值而非 CSR」的具体体现——Validity、SigningAlgorithm等参数虽可传入但未生效;
  • 证书序列号由x509.random_serial_number()随机生成;
  • 签发的证书以certificate-authority/{ca_id}/certificate/{cert_id}形式的 ARN 存储,其中cert_id为去除连字符的 UUID;
  • 根 CA 证书(RootCACertificate 模板签发)不带证书链,而终端实体证书会附带 CA 证书作为链(models.py)。测试test_root_certificate_has_no_chain与test_end_entity_certificate_includes_chain分别验证了两种行为。

5.3 签名算法默认值说明

由于SigningAlgorithm参数未实现,所有证书的签名算法由代码固定:CSR 生成用 SHA256,证书签发用 SHA512。如果你在测试中断言签名算法,需要注意这一点。

六、检索证书:get_certificate 与 get_certificate_authority_certificate

两个 API 用途不同:

  • get_certificate:检索issue_certificate签发的证书(含链)。响应中根证书不包含CertificateChain字段,终端实体证书则返回 CA 证书作为链(responses.py)。如果证书已被吊销,该方法会抛出RequestInProgressException(详见下节)。
  • get_certificate_authority_certificate:检索 CA 自身的证书与链,要求状态为ACTIVE。响应中的CertificateChain会尝试 Base64 解码,解码失败则按原始字符串返回(responses.py)。

测试 tests/test_acmpca/test_acmpca.py 还验证了证书链必须是合法 PEM 且可被cryptography正常加载,保证返回内容具备真实的 X.509 可验证性——在test_end_entity_certificate_issuance中,Moto 签发的终端实体证书甚至能通过cryptography.x509.verification的证书链验证(信任存储中放入 CA 证书,验证出长度为 2 的信任链)。

七、吊销证书:revoke_certificate

# 从证书 PEM 中解析序列号(大写十六进制,按字节加冒号分隔) cert_obj = x509.load_pem_x509_certificate(test_cert.encode("utf-8")) serial_number = format(cert_obj.serial_number, "X") serial_number = ":".join( serial_number[i : i + 2] for i in range(0, len(serial_number), 2) ) client.revoke_certificate( CertificateAuthorityArn=ca_arn, CertificateSerial=serial_number, RevocationReason="KEY_COMPROMISE", )

吊销的底层行为(models.py):

  • 仅允许对ACTIVE状态的 CA 执行吊销,否则抛InvalidStateException;
  • 吊销记录以证书序列号为键、以{"revocation_reason": ..., "revocation_time": ...}为值存入 CA 模型;
  • 之后调用get_certificate时,models.py 会从证书中反解序列号并比对吊销表,命中即抛RequestInProgressException,消息形如"The certificate has been revoked with reason: KEY_COMPROMISE"。

测试test_revoke_certificate完整覆盖了「吊销前可取回 → 吊销后 get_certificate 报错」的闭环,并直接通过 backend 状态断言了吊销原因与吊销时间均已记录。

八、查询与管理 CA

8.1 describe_certificate_authority

返回 CA 的完整 JSON 视图(models.py),包含:Arn、OwnerAccount、CertificateAuthorityConfiguration、Type、RevocationConfiguration、CreatedAt、Status、UsageMode、KeyStorageSecurityStandard,以及状态变化后的LastStateChangeAt和导入证书后的NotBefore/NotAfter。对不存在的 ARN 调用会抛ResourceNotFoundException(测试test_describe_unknown_certificate_authority验证)。

8.2 list_certificate_authorities

支持分页与ResourceOwner过滤(models.py):

  • 分页模型定义于PAGINATION_MODEL(models.py),MaxResults默认上限为 100,响应携带NextToken;测试验证了MaxResults=1时返回单条记录与分页令牌;
  • ResourceOwner取值SELF(默认,仅本账号)或OTHER_ACCOUNTS(仅其他账号),通过比对account_id过滤;
  • 结果按created_at倒序排列。

8.3 update_certificate_authority 与 delete_certificate_authority

  • update_certificate_authority可更新Status(如DISABLED)与RevocationConfiguration,并刷新LastStateChangeAt(测试验证更新后Status变为DISABLED且出现LastStateChangeAt);
  • delete_certificate_authority将状态置为DELETED(软删除语义,与真实 AWS 的删除后保留期行为方向一致)。

九、CRL 吊销配置的校验细节

RevocationConfiguration.CrlConfiguration中有一个值得注意的S3ObjectAcl字段,行为如下(models.py):

  • 未显式传入时,默认值为"PUBLIC_READ";
  • 仅接受"PUBLIC_READ"与"BUCKET_OWNER_FULL_CONTROL"两个取值,其他值抛出InvalidS3ObjectAclInCrlConfiguration,错误消息中会列出合法取值(exceptions.py)。

测试test_update_certificate_authority完整覆盖了默认值填充、显式传值覆盖与非法值报错三种场景,是排查吊销配置问题时的最佳参考。

十、标签管理:tag / untag / list_tags

三个 API 均基于 Moto 通用的TaggingService(models.py):

client.tag_certificate_authority( CertificateAuthorityArn=ca_arn, Tags=[{"Key": "t1", "Value": "v1"}, {"Key": "t2", "Value": "v2"}], ) resp = client.list_tags(CertificateAuthorityArn=ca_arn) # resp["Tags"] == [{"Key": "t1", "Value": "v1"}, ...] client.untag_certificate_authority( CertificateAuthorityArn=ca_arn, Tags=[{"Key": "t1", "Value": "v1"}] )

按文档标注,list_tags的分页尚未实现;tag_certificate_authority与untag_certificate_authority则直接透传至 tagger,行为与 AWS 一致(测试test_tag_certificate_authority、test_untag_certificate_authority验证了增删效果)。

十一、资源策略:put_policy / get_policy / delete_policy

三个策略 API 是文档中明确给出语义说明的功能:

  • put_policy:将 JSON 字符串形式的资源策略附加到私有 CA,要求 CA 状态为ACTIVE(否则抛InvalidStateException);
  • get_policy:检索策略;若从未设置过策略,抛ResourceNotFoundException;
  • delete_policy:清除策略。

测试test_policy_operations给出了完整用例:先激活 CA,再put_policy写入包含"Action": ["acm-pca:IssueCertificate"]的策略 JSON,get_policy原样取回,delete_policy后再次get_policy报ResourceNotFoundException。策略以字符串形式存于CertificateAuthority.policy字段(models.py),不做语法校验。

十二、底层实现架构速览

了解请求如何流转,有助于排查边界行为。一次 boto3 调用的完整链路是:

  1. 请求命中 urls.py 注册的acm-pca.{region}.amazonaws.com端点;
  2. responses.py 的ACMPCAResponse按 action 分发到对应处理方法,将请求体json.loads后提取参数(例如create_certificate_authority读取CertificateAuthorityConfiguration、RevocationConfiguration、CertificateAuthorityType、KeyStorageSecurityStandard、Tags);
  3. 调用 models.py 中ACMPCABackend的对应方法(通过acmpca_backends[current_account][region]获取区域隔离的 backend 实例),执行状态变更;
  4. 结果序列化为 JSON 返回。

值得强调的是区域隔离:acmpca_backends = BackendDict(ACMPCABackend, "acm-pca")(models.py)意味着不同 region 的 CA 相互独立,而 ARN 中的 partition 会根据 region 正确推导(如us-gov-east-1→aws-us-gov,eusc-de-east-1→aws-eusc,测试对此做了参数化验证)。

十三、测试验证与常见坑位

仓库的 tests/test_acmpca/test_acmpca.py 是功能行为的最佳规格说明书,覆盖了创建、描述、CSR、签发、导入、吊销、策略、标签、分页、证书链等全部已实现 API。实践中的常见注意事项:

  • get_certificate_authority_certificate与put_policy都要求 CA 处于ACTIVE,测试前务必先走完「签发根证书 → 导入」两步;
  • 签发证书时如果不传TemplateArn,默认按终端实体证书模板处理(含ca=False与服务器/客户端认证扩展);
  • Validity与SigningAlgorithm参数暂未生效,证书有效期固定为 365 天、签名算法固定为 SHA512,若断言这些字段需要以实际实现为准;
  • 吊销后get_certificate抛出的异常类型是RequestInProgressException,而非常见的ResourceNotFoundException,断言错误码时注意区分;
  • backend 是可 pickle 序列化的(私钥以加密字节存储),可在 LocalStack 等下游项目中安全持久化。

结语

Moto 的 acm-pca 实现已经覆盖了私有 CA 生命周期中最核心的 15 个 API,足以支撑基于 boto3 的 PKI 相关测试(证书签发、链验证、吊销、标签与策略管理)。在编写测试时,建议以 docs/docs/services/acm-pca.rst 的实现清单为功能边界,以 tests/test_acmpca/test_acmpca.py 为行为参照,并注意本文点明的若干未实现参数与默认值细节,即可写出与真实 AWS 行为高度对齐的可靠测试。

  • Mock
  • 测试

【免费下载链接】moto

A library that allows you to easily mock out tests based on AWS infrastructure.

项目地址:https://gitcode.com/gh_mirrors/mo/moto
点击查看免费下载
上一篇:Open-Elevation源码解析:深入理解GDAL高程查询实现原理
下一篇:AB Download Manager多线程下载架构深度解析与性能优化实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询