Device Attestation certificates (DAC)
Device Attestation(DA) 是验证 Matter 设备是否经过认证并由连接标准联盟成员制造商生产的过程。 目标是确保没有假冒设备加入 Matter 网络,并且被检查的设备是正品。 设备证明过程基于信任链概念,对于所有 Matter 设备都是必需的。
设备证明验证过程在调试期间进行。 在commissioner将新设备预配到 Fabric之前,它必须检查设备的证书并获取有关它的信息。在设备证明过程中,commissioner会检查设备上的证书等。
CSA 将产品认证机构 Product Attestation Authority(PAA) 的状态分配给选定的组织,这些组织将成为设备认证过程的根证书颁发机构。 每个 PAA 都拥有一个密钥对,该密钥对由私钥和公钥以及自签名 PAA 证书组成。 PAA 使用私钥对产品认证中间体Product Attestation Intermediate (PAI) 的证书进行签名,PAI 是直接负责颁发设备认证证书(DAC) 的实体。 每个 PAI 证书都是由 PAI 以证书签名请求的形式创建的,并提交给 PAA,PAA使用其私钥进行认证。
PAI证书位于**认证链**的中间层。 它还包括分配的私钥和公钥,并由它源自的更高级别的 PAA 证书签名。 PAI 私钥用于对设备证明证书 (DAC) 进行签名。 虽然单个 PAI 只能颁发属于单个设备供应商的 DAC,但单个设备供应商可以操作多个 PAI,例如,为不同的产品系列颁发证书。
Public Key Infrastructure (PKI)
PAA、PAI 和 DAC 证书构成公钥基础设施(
PKI),这是一种安全结构,其中实体的真实性由另一个实体确认,每个此类证书链都以其中一个 PAA 证书结束。 证书的所有权是使用关联的私钥来证明的。DAC certificate chain
PAA certificate, PAI certificate, Device Attestation Certificate
设备证明数据
若要通过设备证明过程,想要加入 Matter 结构的 Matter 设备必须包含设备证明过程所需的两组信息:工厂数据和证书声明。
制造商需要从 CSA 获取以下信息:
Vendor ID(VID) - 标识制造商的唯一 16 位数字。 当制造商成为连接标准联盟成员之一时,可以从 CSA 获得.Certification Declaration(CD) - CSA 为每种设备类型创建的加密文档,用于确认给定类型的设备是否经过认证。 从CSA获取CD之前,您可以在调试期间使用通用的CD进行测试。
制造商获得(Certification Declaration)认证声明后,可以将其提供给具有新固件版本的设备,例如使用设备固件升级功能或直接在制造过程中提供。 另一方面,VID 是用于生成工厂数据的数据元素之一.
除了 VID,制造商还需要分配一个唯一的 16 位Product ID (PID) 编号来识别其产品。 最后,工厂数据必须包括 PAI 和 DAC 证书,以及制造商必须事先生成的 DAC 私钥。
如何生成DAC证书
-
借助联盟成员的 PKI 生成 DAC
制造商可以向作为CSA 成员的 PKI 提供商之一请求 PAI 和 DAC。 这样,制造商就不必设置自己的证书颁发机构链,并且可以使用符合 PKI 认证策略的受信任认证源。 PKI 提供商的 PAA 证书不包含任何特定的 VID,这使得提供商可以为多个制造商提供服务。 从 PKI 提供商的 PAA 证书生成的制造商的 PAI 证书具有制造商的 VID,可用于颁发和签署 DAC。
-
使用自己的 PKI 生成 DAC
制造商可以设置自己的公钥基础设施,以使用自己现有的 PKI 生成 PAI 和 DAC 证书。 Matter Certification Policy 文档中概述了设置自己的 PKI 的规则。 通过设置自己的 PKI,制造商可以获取以下证书:
PAA- 前提是制造商在 DCL 中具有根证书颁发机构。 PAA 可以嵌入 VID(VID-scoped PAA)或 (Non-Vendor Scoped PAA)。PAI- 前提是制造商拥有足够且安全的逻辑链,可以为大量设备提供 DAC,因为每个设备都有一个唯一的 DAC。
Matter Node Operational Credentials (NOC)
NOC 是节点在 Fabric 上运行的凭证;NOC 应由 Fabric 内信任的根 CA 或由中间证书颁发机构 (ICA) 颁发,其 ICA 证书由此类根 CA 直接颁发。在调试过程(commissioning)是将 Fabric 凭据分配给新节点,以便它可以与同一 Fabric 中的其他节点进行通信。
在中间 CA (ICA) 颁发 NOC 的情况下,ICA 证书用于证明 NOC 的有效性。与 ICA 证书颁发者关联的根 CA 证书依次用于证明 ICA 证书的有效性。根 CA 证书是自签名的。它们未经验证,但值得信赖,因为它们是由值得信赖的专员提供的。在根 CA 颁发 NOC 的情况下,根 CA 证书用于证明 NOC 的有效性。
Operational credentials
Commissioner有权访问CA。因此,它代表被委托的节点向 CA 请求节点操作凭证。凭据由两部分组成:
- 节点操作标识符(
Node Operational Identifier)是一个 64 位数字,用于唯一标识结构中的每个节点。 - 节点操作证书(NOC)是节点用于在结构中通信和标识自身的一组凭据。它们由节点操作证书签名请求 (NOCSR) 过程生成。
NOCSR 是在正在调试的节点上运行的过程。它绑定了多个加密元素,然后将它们发送给 Commissioner,并请求 CA 生态系统提供其相应的 NOC。
NOTE:
NOC由 CA 生态系统在现实世界的fabric上发行。NOC以加密方式绑定到唯一的节点操作密钥对 (NOKP)。NOKP是由被委托节点在调试过程中生成的。- 发送到生态系统的
NOCSR信息包括节点操作公钥,但节点操作私钥永远不会发送给 Commissioner 或 CA。 NOCSR过程使用来自证明流程的输入,对 CSRSR 信息进行签名,从而验证 CA 生成受信任 NOC 的请求。
NOC 证书结构
Matter TLV Schema Format
matter-certificate = {
serial-num = 3E FC FF 17 02 B9 A1 7A,
sig-algo = 0x01U,
issuer = [[ matter-icac-id = 0xCACACACA00000003U ]],
not-before = 0x271B17EFU,
not-after = 0x4CB9B56EU,
subject = [[ matter-node-id = 0xDEDEDEDE00010001U,
matter-fabric-id = 0xFAB000000000001DU
]],
pub-key-algo = 0x01U,
ec-curve-id = 0x01U,
ec-pub-key = 04 9A 2A 21 6F B3 9D D6 B6 FA 21 1B 83 5C 89 E3
E6 AF B6 6C 14 F7 58 31 95 4F 9F F4 F7 A3 F0 11
2C 8A 0D 8E AF 29 C6 53 29 4D 48 EE E0 70 8A 03
2C CA 39 39 3C 3A 7B 46 F1 81 AE A0 78 FE AD 83
83,
extensions = [[
basic-constraints = {
is-ca = false
# For Matter ICA Certificate and Matter Root CA Certificate:
# The basic constraints extension SHALL be encoded with is-ca set to true
},
key-usage = 0x01U,
extended-key-usage = [ 0x02U, 0x01U ],
subject-key-id = 9F 55 A2 6B 7E 43 03 E6 08 83 E9 13 BF 94 F4 FB
5E 2A 61 61,
authority-key-id = 53 52 D7 05 9E 9C 15 A5 08 90 68 62 86 48 01 A2
9F 1F 41 D3,
]],
signature = 79 55 C2 02 63 0B 4B A4 D5 91 25 26 32 2F DF 28
F8 9E DF E5 AF 9C 0E 57 2B D8 A1 4A AA BB 4D 12
B8 3C A1 7C 7B 05 FB 16 4B 77 D7 9C 52 96 13 31
6B CF D1 78 95 E4 B2 A4 F2 40 4B 98 17 32 71 59
}
// 下面的证书操作代码,关于CaTs字段的使用
subject = [[
common-name = "NOC Example",
matter-node-id = 0x0102030405060708U,
matter-fabric-id = 0xFAB000000000001DU,
matter-noc-cat = 0xABCD0002U // CASE Authenticated Tag
]]
subject = [[
matter-node-id = 0x0102030405060708U,
matter-fabric-id = 0xFAB000000000001DU,
// 存在具有两个不同版本号的相同 CASE Authenticated Tag 值而导致的非法主体
matter-noc-cat = 0xABCD0004U, # <-- Value 0xABCD, Version 0x0004
matter-noc-cat = 0xABCD0002U, # <-- Value 0xABCD, Version 0x0002
]]
Subjects identified by CASE Authenticated Tag
CASE 身份验证标签 (CAT) 是在 CASE 会话建立期间共享的操作证书内的特殊主体专有名称,充当类似组的标签。这样的标签可以应用于多个节点,从而促进使用同一组节点作为主题的访问控制条目的管理。由于这些标签是在 CASE 会话上下文中进行身份验证的,因此管理信任根确实会通过单个源节点链接回 Fabric 的受信任根。这使得 CAT 适合群组使用,同时保持安全身份验证和归因能力。 最多可以编码三个 matter-noc-cat 属性。
iOS证书操作示例代码
- (NSDate *)startDateWithTimeIntervalSinceNow:(NSTimeInterval)interval {
__auto_type * startDate = [NSDate dateWithTimeIntervalSinceNow:interval];
NSTimeInterval seconds = floor([startDate timeIntervalSinceReferenceDate]);
return [NSDate dateWithTimeIntervalSinceReferenceDate:seconds];
}
// 测试生成根证书
- (void)testGenerateRootCert {
__auto_type * testKeys = [[MTRTestKeys alloc] init];
__auto_type * rootCert = [MTRCertificates createRootCertificate:testKeys
issuerID:nil
fabricID:nil
error:nil];
// Test TLV format.
__auto_type * tlvCert = [MTRCertificates convertX509Certificate:rootCert];
__auto_type * derCert = [MTRCertificates convertMatterCertificate:tlvCert];
NSLog(@"%@, %@", rootCert, derCert);
}
// 生成根证书【带有过期时间】
- (void)testGenerateRootCertWithValidityPeriod {
__auto_type * testKeys = [[MTRTestKeys alloc] init];
// 设置过期时间
__auto_type * startDate = [self startDateWithTimeIntervalSinceNow:100];
__auto_type * validityPeriod = [[NSDateInterval alloc] initWithStartDate:startDate
duration:200];
__auto_type * rootCert = [MTRCertificates createRootCertificate:testKeys
issuerID:nil
fabricID:nil
validityPeriod:validityPeriod
error:nil];
NSLog(@"%@, %@", validityPeriod.startDate, rootCert.notBefore);
NSLog(@"%@, %@", validityPeriod.endDate, rootCert.notAfter);
}
// 生成中间证书
- (void)testGenerateIntermediateCert {
__auto_type * rootKeys = [[MTRTestKeys alloc] init];
__auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys
issuerID:nil
fabricID:nil
error:nil];
__auto_type * intermediateKeys = [[MTRTestKeys alloc] init];
__auto_type * intermediateCert = [MTRCertificates createIntermediateCertificate:rootKeys
rootCertificate:rootCert
intermediatePublicKey:intermediateKeys.publicKey
issuerID:nil
fabricID:nil
error:nil];
NSLog(@"%@", intermediateCert);
}
// 生成操作证书【不使用中间证书】
- (void)testGenerateOperationalCertNoIntermediate {
__auto_type * rootKeys = [[MTRTestKeys alloc] init];
__auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys
issuerID:nil
fabricID:nil
error:nil];
__auto_type * operationalKeys = [[MTRTestKeys alloc] init];
__auto_type * cats = [[NSMutableSet alloc] initWithCapacity:3];
// High bits are identifier, low bits are version.
[cats addObject:@0x00010001];
[cats addObject:@0x00020001];
[cats addObject:@0x0003FFFF];
__auto_type * operationalCert = [MTRCertificates createOperationalCertificate:rootKeys
signingCertificate:rootCert
operationalPublicKey:operationalKeys.publicKey
fabricID:@1
nodeID:@1
caseAuthenticatedTags:cats
error:nil];
NSLog(@"%@", operationalCert);
}
// 生成操作证书【使用中间证书】
- (void)testGenerateOperationalCertWithIntermediate
{
__auto_type * rootKeys = [[MTRTestKeys alloc] init];
__auto_type * rootCert = [MTRCertificates createRootCertificate:rootKeys
issuerID:nil
fabricID:nil
error:nil];
__auto_type * intermediateKeys = [[MTRTestKeys alloc] init];
__auto_type * intermediateCert = [MTRCertificates createIntermediateCertificate:rootKeys
rootCertificate:rootCert
intermediatePublicKey:intermediateKeys.publicKey
issuerID:nil
fabricID:nil
error:nil];
__auto_type * operationalKeys = [[MTRTestKeys alloc] init];
__auto_type * operationalCert = [MTRCertificates createOperationalCertificate:intermediateKeys
signingCertificate:intermediateCert
operationalPublicKey:operationalKeys.publicKey
fabricID:@1
nodeID:@1
caseAuthenticatedTags:nil
error:nil];
NSLog(@"%@", operationalCert);
}
// 生成 Certificate Signing Request
- (void)testGenerateCSR {
__auto_type * testKeys = [[MTRTestKeys alloc] init];
__auto_type * csr = [MTRCertificates createCertificateSigningRequest:testKeys
error:nil];
__auto_type * publicKey = [MTRCertificates publicKeyFromCSR:csr
error:nil];
SecKeyRef originalKeyRef = [testKeys publicKey];
NSData * originalPublicKey = ( __bridge_transfer NSData *) SecKeyCopyExternalRepresentation(originalKeyRef, nil);
NSLog(@"%@, %@", publicKey, originalPublicKey);
}
设备调试初始化时候如果自定义证书颁布,需要实现 MTROperationalCertificateIssuer
/**
MTRDeviceControllerStartupParams
params.operationalCertificateIssuer = certificateIssuer;
params.operationalCertificateIssuerQueue = dispatch_get_main_queue();
*/
@interface MTRTestCertificateIssuer : NSObject <MTROperationalCertificateIssuer>
@property (nonatomic, strong) MTRTestKeys * rootKey;
@property (nonatomic, strong) MTRCertificateDERBytes rootCertificate;
@property (nonatomic, assign) NSDateInterval * validityPeriod;
@property (nonatomic, strong) NSNumber * fabricID;
@property (nonatomic, readonly) BOOL shouldSkipAttestationCertificateValidation;
- (nullable instancetype)initWithRootKey:(MTRTestKeys *)key
fabricID:(NSNumber *)fabricID
validityPeriod:(NSDateInterval *)validityPeriod;
@end
@implementation MTRTestCertificateIssuer
- (nullable instancetype)initWithRootKey:(MTRTestKeys *)key
fabricID:(NSNumber *)fabricID
validityPeriod:(NSDateInterval *)validityPeriod
{
if (!(self = [super init])) {
return nil;
}
NSError * error;
__auto_type * rootCertificate = [MTRCertificates createRootCertificate:key
issuerID:nil
fabricID:fabricID
validityPeriod:validityPeriod
error:&error];
if (rootCertificate == nil) {
return nil;
}
_validityPeriod = validityPeriod;
_rootCertificate = rootCertificate;
_rootKey = key;
_fabricID = fabricID;
_shouldSkipAttestationCertificateValidation = YES;
return self;
}
- (nullable MTRCertificateDERBytes)issueOperationalCertificateForNode:(NSNumber *)nodeID
operationalPublicKey:(SecKeyRef)operationalPublicKey {
return [MTRCertificates createOperationalCertificate:self.rootKey
signingCertificate:self.rootCertificate
operationalPublicKey:operationalPublicKey
fabricID:self.fabricID
nodeID:nodeID
caseAuthenticatedTags:nil
validityPeriod:self.validityPeriod
error:nil];
}
- (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
attestationInfo:(MTRDeviceAttestationInfo *)attestationInfo
controller:(MTRDeviceController *)controller
completion:(void (^)(MTROperationalCertificateChain * _Nullable info,
NSError * _Nullable error))completion {
NSError * error;
__auto_type * publicKey = [MTRCertificates publicKeyFromCSR:csrInfo.csr error:&error];
NSDictionary * attributes =
@{ (id) kSecAttrKeyType : (id) kSecAttrKeyTypeECSECPrimeRandom, (id) kSecAttrKeyClass : (id) kSecAttrKeyClassPublic };
CFErrorRef keyCreationError = NULL;
SecKeyRef operationalPublicKey
= SecKeyCreateWithData(( __bridge CFDataRef) publicKey, ( __bridge CFDictionaryRef) attributes, &keyCreationError);
__auto_type * operationalCertificate = [self issueOperationalCertificateForNode:@(kDeviceId)
operationalPublicKey:operationalPublicKey];
__auto_type * certChain = [[MTROperationalCertificateChain alloc] initWithOperationalCertificate:operationalCertificate
intermediateCertificate:nil
rootCertificate:self.rootCertificate
adminSubject:nil];
completion(certChain, nil);
}
@end