在 x509 基本约束中检测 CA:TRUE

Posted

技术标签:

【中文标题】在 x509 基本约束中检测 CA:TRUE【英文标题】:Detecting CA:TRUE in x509 Basic Contraints 【发布时间】:2012-02-07 12:36:05 【问题描述】:

检查证书基本约束的正确方法是什么?下面是我目前用来模仿钥匙串中显示的图标的代码(下面的原因是,虽然我们有一个

 SFChooseIdentityPanel * identityPanel = [SFChooseIdentityPanel sharedChooseIdentityPanel];

不存在用于选择 CA 或主机/叶证书的等效项。这在设置/锁定 SSL 连接时很有用。

不幸的是 - 我无法在头文件中找到 OID 字符串以从证书中干净地提取 CA:TRUE 或 false(或以错误的方式使用 API)。

所以问题是

我如何干净利落地检查 CA:TRUE - 虽然下面的工作正常 - 我可以想象它会被格式错误的证书与正确位置的文本字符串挫败。

其次 - 我正在使用 Issuer==Subject 的启发式方法来检测自签名。有没有更清洁的方法来做到这一点?

最后 - 通过反复试验 - 下面似乎模仿了苹果在钥匙串中的选择 - 但文档相当难以理解。 kSecTrustResultProceed 是否真的意味着用户设置了覆盖和 kSecTrustResultUnspecified 实际上信任是由系统基本信任指定的?虽然它'工作' - 我不能完全理解文档的确切解释。

非常感谢。代码如下。

Dw.

@implementation NSImage (CertificateSelectionPanelExtensions)

+(NSImage *)iconForCertificate:(SecCertificateRef)certificateRef small:(BOOL)isSmall

    BOOL isCA = FALSE, isInvalid = TRUE, isUserTrust = FALSE;

    NSString * issuer = nil, * subject = nil;

    const void *keys[] =  kSecOIDX509V1SubjectName, kSecOIDX509V1IssuerName, kSecOIDExtendedKeyUsage, kSecOIDBasicConstraints ;
    CFArrayRef keySelection = CFArrayCreate(NULL, keys , sizeof(keys)/sizeof(keys[0]), &kCFTypeArrayCallBacks);
    CFDictionaryRef vals = SecCertificateCopyValues(certificateRef, keySelection, NULL);

    CFArrayRef values;
    CFDictionaryRef dict;

    dict = CFDictionaryGetValue(vals, kSecOIDBasicConstraints );
    values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
    if (values) 
        for(int i = 0; i < CFArrayGetCount(values); i++) 
            CFDictionaryRef subDict = CFArrayGetValueAtIndex(values, i);

            // We cannot find OID defines for the CA - so rely on the lower libraries to give us a string
            // of sorts. Not a good idea - as now this code can be foiled by a actual string.
            //
            NSString *k = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(subDict, kSecPropertyKeyLabel)];
            NSString *v = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(subDict, kSecPropertyKeyValue)];
            if ([@"Certificate Authority" isEqualToString:k] && [@"Yes" isEqualToString:v]) 
                isCA = TRUE;
            
        
    ;

    // Fall back on a simple self-sign check if there where no kSecOIDBasicConstraints.
    // set on the cert. Note that it is a DN is equal check - in some cases
    // doing a 509v3 Subject/Authority Key Identifier may be better ?? XXXX
    //
    if (!isCA && !values) 
        dict = CFDictionaryGetValue(vals, kSecOIDX509V1SubjectName);
        values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
        subject = [NSString stringWithFormat:@"%@", values];

        dict = CFDictionaryGetValue(vals, kSecOIDX509V1IssuerName);
        values = dict ? CFDictionaryGetValue(dict, kSecPropertyKeyValue) : NULL;
        issuer = [NSString stringWithFormat:@"%@", values];

        // Crap way of secondgessing CA ness.
        if ([issuer isEqualToString:subject])
            isCA = TRUE;
    ;

    SecPolicyRef policy = SecPolicyCreateBasicX509(); // SecPolicyCreateSSL(YES,nil);
    CFArrayRef chain = CFArrayCreate(NULL, (const void**)(&certificateRef), 1, NULL);

    SecTrustRef trustRef;    
    SecTrustCreateWithCertificates(chain, policy, &trustRef);

    SecTrustResultType result;
    SecTrustEvaluate (trustRef, &result);

    if(result == kSecTrustResultProceed) 
        isUserTrust = TRUE;
        isInvalid = FALSE;
     else
    if (result == kSecTrustResultUnspecified)
        isInvalid = FALSE;

    CFRelease(trustRef);
    CFRelease(chain);

    // Images as per /System/Library/Frameworks/SecurityInterface.framework/Versions/A/Resources
    // Cert <Small | Large> <Personal | Root> [_Invalid | _UserTrust ]
    //
    return [NSImage imageNamed:[NSString stringWithFormat:@"Cert%@%@%@",
                                isSmall ? @"Small" : @"Large",
                                isCA ? @"Root" : @"Personal",
                                isInvalid ? @"_Invalid" : (isUserTrust ? @"_UserTrust" : @"")]];

@end

【问题讨论】:

【参考方案1】:

basicConstraints 扩展在X.509 中定义如下:

basicConstraints EXTENSION ::= 
    SYNTAX BasicConstraintsSyntax
    IDENTIFIED BY id-ce-basicConstraints 
BasicConstraintsSyntax ::= SEQUENCE 
    cA BOOLEAN DEFAULT FALSE, 
    pathLenConstraint INTEGER (0..MAX) OPTIONAL 

这反过来又根据可分辨编码规则 (X.690) 进行编码。 BasicConstraintsSyntax 序列的各个部分没有自己的 OID。

我不会声称自己是专家,但我认为您正在对 cA 标志进行的测试没有问题。请注意,我认为[NSString stringWithFormat:@"%@", ...] 部分是不必要的。

至于检查自签名证书,测试主题和颁发者名称似乎并非不合理;显然,这确实告诉您证书是否声称是自签名的,并且要实际测试它是否是您需要自己检查签名(无论这是否是您想要做的事情,我不知道)不知道)。仅供参考,关于密钥标识符的主题,根据RFC3280,在自签名证书的特定情况下,可以省略授权密钥标识符,因此没有授权密钥标识符的证书可能表明该证书是自签名的,但完全有可能有人故意颁发没有授权密钥标识符的格式错误的证书。

通过查看文档,最后一个问题似乎得到了解决,这表明这些值大致意味着您所说的。

另一件值得一提的是,有一些代码可以帮助解决这类事情;例如,Jens Alfke 的 MYCrypto 库

编辑(2012 年 2 月 8 日)

根据X.509 Style Guide,subject 名称可能为空,在这种情况下,您需要查看 subjectAltName 扩展名。这不太可能破坏依赖于比较主题和颁发者名称的自签名证书的测试 - 向自己颁发证书并为颁发者名称提供 DN 但然后将主题名称留空似乎是不合理的。但是,值得牢记。

【讨论】:

对 - 我通过查看 DER 字节确认确实如此。我更想要的是一种干净的方式来“询问”这个问题;使用 kSecPropertyKeyValue 或一些比较。以这种方式(或字符串比较)查看 DER 似乎有点作弊。而且我想避免编写适当的 DER 解码器。虽然看起来你建议的 MyCrypto 库 - 这正是可能需要的? 也许还值得一提的是,您上面显示的代码在 ios 上根本无法运行,因为它没有公开您正在使用的功能。我想如果 正在编写类似的代码,我会使用 MYCrypto。 谢谢!尝试使用 MYCrypto - 尽管它缺少 CA/主机对等锁定 - 所以看看我是否可以添加它。

以上是关于在 x509 基本约束中检测 CA:TRUE的主要内容,如果未能解决你的问题,请参考以下文章

未检测到文档的语法约束(DTD 或 XML 模式) (Android)

使用带有约束的持续时间动画快速移动按钮并在其中检测触摸

未检测到文档的语法约束(DTD 或 XML 模式)

Android Studio 未检测到约束布局的样式

检测到约束模糊地表明高度为零的情况

在'identityPoolId'处检测到的验证错误未能满足约束