当前位置: 首页 > 知识库问答 >
问题:

自签名服务器根CA的验证

岳昊空
2023-03-14

这是一个既试图为我的特定用例找到解决方案,又试图记录我为遵循此过程的任何其他人所做的努力的问题。

我们有一个RESTful服务器和一个iOS应用程序。我们有自己的证书颁发机构,服务器有一个根证书颁发机构和一个自签名证书。我们按照此过程生成以下文件:

http://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/

罗特卡。佩姆·罗特卡。密钥服务器。crt服务器。钥匙

只有服务器证书存储在我们的服务器上,作为SSL过程的一部分,公钥与API调用一起发送以进行验证。

我按照此过程使用AFNetworking来使用证书固定和公钥固定来验证我们的自签名证书:

http://initwithfunk.com/blog/2014/03/12/afnetworking-ssl-pinning-with-self-signed-certificates/

我们把这些钱换成现金。将crt文件转换为一个文件。符合本指南要求的cer文件(DER格式):

https://support.ssl.com/Knowledgebase/Article/View/19/0/der-vs-crt-vs-cer-vs-pem-certificates-and-how-to-convert-them

并在iOS应用捆绑包中包含. cerfile(server.cer)。这成功地允许我们的应用程序向我们的服务器发出GET/POST请求。但是,因为我们的服务器证书可能会过期或重新颁发,我们希望使用根CA,就像AFNetwork上的这个线程中的人员所做的那样:

https://github.com/AFNetworking/AFNetworking/issues/1944

目前我们已经更新到AFNetwork2.6.0,所以我们的网络库应该包括所有的更新,包括本讨论中的更新:

html" target="_blank">https://github.com/AFNetworking/AFNetworking/issues/2744

用于创建我们的安全策略的代码:

    var manager: AFHTTPRequestOperationManager = AFHTTPRequestOperationManager()
    manager.requestSerializer = AFJSONRequestSerializer() // force serializer to use JSON encoding

    let policy: AFSecurityPolicy = AFSecurityPolicy(pinningMode: AFSSLPinningMode.PublicKey)
    var data: [NSData] = [NSData]()
    for name: String in ["rootCA", "server"] {
        let path: String? = NSBundle.mainBundle().pathForResource(name, ofType: "cer")
        let keyData: NSData = NSData(contentsOfFile: path!)!
        data.append(keyData)
    }
    policy.pinnedCertificates = data
    policy.allowInvalidCertificates = true 
    policy.validatesDomainName = false 
    manager.securityPolicy = policy

包括server.cer,我们可以通过锁定公钥来信任我们的服务器(也尝试了AFSecurityPolcyPinningMode。证书);这有效,因为包含了确切的证书。然而,因为我们可能会更改服务器的server.crt文件,所以我们希望能够只使用rootCA. cers来完成。

然而,由于应用程序包中只包含rootCA,这似乎不起作用。是不是rootCA没有足够的关于公钥的信息来验证服务器证书,而服务器证书是用rootCA签名的?服务器。crt文件也可能有一个不断变化的通用名。

另外,由于我对SSL术语的熟练程度相当低,如果有人能澄清我问的问题是否正确,那就太好了。具体问题如下:

  1. 我是否正确生成证书,以便服务器可以使用自签名服务器证明其身份。crt文件
  2. 是否可以只包含rootCA。cer文件,并能够验证叶证书服务器。阴极射线管?它是否能够验证另一台服务器2。由同一根CA签名的crt文件?或者我们应该在根CA和叶之间包含一个中间证书
  3. 公钥固定或证书固定是正确的解决方案吗?我读过的每个论坛和博客帖子都说是的,但即使是最新的AFNetworking库,我们也没有任何运气
  4. 服务器是否需要以某种方式同时发送这两个服务器。crt和roomCA。pem签名结束了吗

共有3个答案

壤驷棋
2023-03-14

我有同样的问题,我已经通过比较AFURLSessionManagerdidReceiveChallenge方法中链的公钥来修复它。

-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
        // Get remote certificate
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;

        NSMutableArray *policies = [NSMutableArray array];
        [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef) challenge.protectionSpace.host)];

        SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
        NSUInteger trustedPublicKeyCount = 0;
        NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);

        for (id trustChainPublicKey in publicKeys) {
            for (id pinnedPublicKey in self.pinnedPublicKeys) {
                if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
                    trustedPublicKeyCount += 1;
                }
            }
        }

        // The pinnning check
        if (trustedPublicKeyCount > 0) {
            NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
        } else {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, NULL);
        }
    }

以下是固定公钥的初始化:

    // Get local certificates
    NSArray *certNames = @[@"root_cert"];
    self.pinnedPublicKeys = [NSMutableSet new];

    for (NSString *certName in certNames) {
        NSString *path = [bundle pathForResource:certName ofType:@"der"];
        NSData *certificate = [NSData dataWithContentsOfFile:path];

        id publicKey = AFPublicKeyForCertificate(certificate);
        if (publicKey) {
            [self.pinnedPublicKeys addObject:publicKey];
        }
    }

以下是获取密钥信任链的帮助方法(AFPublicKeyTrustChainForServerTrust),比较公钥(AFSecKeyIsEqualToKey)和从证书获取公钥的方法(AFPublicKeyTrustChainForServerTrust):

static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
    NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
    for (CFIndex i = 0; i < certificateCount; i++) {
        SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);

        SecCertificateRef someCertificates[] = {certificate};
        CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);

        SecTrustRef trust;
        SecTrustCreateWithCertificates(certificates, policy, &trust);

        SecTrustResultType result;
        SecTrustEvaluate(trust, &result);

        [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];

        if (trust) {
            CFRelease(trust);
        }

        if (certificates) {
            CFRelease(certificates);
        }

        continue;
    }
    CFRelease(policy);

    return [NSArray arrayWithArray:trustChain];
}

static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
    return [(__bridge id)key1 isEqual:(__bridge id)key2];
}

static id AFPublicKeyForCertificate(NSData *certificate) {
    id allowedPublicKey = nil;
    SecCertificateRef allowedCertificate;
    SecCertificateRef allowedCertificates[1];
    CFArrayRef tempCertificates = nil;
    SecPolicyRef policy = nil;
    SecTrustRef allowedTrust = nil;
    SecTrustResultType result;

    allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);

    allowedCertificates[0] = allowedCertificate;
    tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);

    policy = SecPolicyCreateBasicX509();
    SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust);
    SecTrustEvaluate(allowedTrust, &result);

    allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);

    if (allowedTrust) {
        CFRelease(allowedTrust);
    }

    if (policy) {
        CFRelease(policy);
    }

    if (tempCertificates) {
        CFRelease(tempCertificates);
    }

    if (allowedCertificate) {
        CFRelease(allowedCertificate);
    }

    return allowedPublicKey;
}

方鸿羲
2023-03-14

如果您正在使用cocopods,那么将AFSecurityPolicy类划分为子类,并根据mitrenegade的答案实现安全检查https://stackoverflow.com/a/32469609/4000434

这是我的密码。

在发布请求时初始化AFHttpRequestOperationManager,如下所示。

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    manager.securityPolicy = [RootCAAFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [manager POST:Domain_Name parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
        success(operation,responseObject);
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
        NSLog(@"Error  %@",error);
        failure(operation,error);
    }];

RootCaafSecurity策略是AFSecurityPolicy类的子类。有关RootCAAFSecurityPolicy,请参见下文。h和。m类重写该方法

-(BOOL)评估ServerTrust:(SecTrustRef)serverTrust forDomain:(NSString*)域

RootCAAFSecurityPolics. h类

#import <AFNetworking/AFNetworking.h>

@interface RootCAAFSecurityPolicy : AFSecurityPolicy

@end

根安全政策。m级

用您的证书文件名替换RootCA

#import "RootCAAFSecurityPolicy.h"

@implementation RootCAAFSecurityPolicy
-(BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
{
    if(self.SSLPinningMode == AFSSLPinningModeCertificate)
    {
        return [self shouldTrustServerTrust:serverTrust];
    }
    else
    {
        return [super evaluateServerTrust:serverTrust forDomain:domain];
    }
}
- (BOOL)shouldTrustServerTrust:(SecTrustRef)serverTrust
{
    // load up the bundled root CA
    NSString *certPath = [[NSBundle mainBundle] pathForResource:@"RootCA" ofType:@"der"];

    NSAssert(certPath != nil, @"Specified certificate does not exist!");

    NSData *certData = [[NSData alloc] initWithContentsOfFile:certPath];
    CFDataRef certDataRef = (__bridge_retained CFDataRef)certData;
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, certDataRef);

    NSAssert(cert != NULL, @"Failed to create certificate object. Is the certificate in DER format?");


    // establish a chain of trust anchored on our bundled certificate
    CFArrayRef certArrayRef = CFArrayCreate(NULL, (void *)&cert, 1, NULL);
    OSStatus anchorCertificateStatus = SecTrustSetAnchorCertificates(serverTrust, certArrayRef);

    NSAssert(anchorCertificateStatus == errSecSuccess, @"Failed to specify custom anchor certificate");


    // trust also built-in certificates besides the specified CA
    OSStatus trustBuiltinCertificatesStatus = SecTrustSetAnchorCertificatesOnly(serverTrust, false);

    NSAssert(trustBuiltinCertificatesStatus == errSecSuccess, @"Failed to reenable trusting built-in anchor certificates");


    // verify that trust
    SecTrustResultType trustResult;
    OSStatus evalStatus =  SecTrustEvaluate(serverTrust, &trustResult);

    NSAssert(evalStatus == errSecSuccess, @"Failed to evaluate certificate trust");


    // clean up
    CFRelease(certArrayRef);
    CFRelease(cert);
    CFRelease(certDataRef);


    // did our custom trust chain evaluate successfully
    return (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified);
}
@end
严宇
2023-03-14

借助大量不同的SSL资源,我找到了启用自签名证书来验证启用SSL的私有服务器的解决方案。我也对SSL、现有的iOS解决方案以及导致它在我的系统中无法工作的每个小问题有了更好的理解。我将试图概述进入我的解决方案的所有资源,以及哪些小事情会有所不同。

我们仍在使用AFNetworking,目前是2.6。0,它应该包括证书固定。这是我们问题的根源;我们无法验证私有服务器的身份,该服务器正在发送一个由自签名CA根签名的叶证书。在我们的iOS应用程序中,我们捆绑了自签名根证书,然后AFNetworking将其设置为受信任的锚。但是,由于服务器是本地服务器(我们的产品包含硬件),IP地址是动态的,因此AFNetworking的证书验证失败,因为我们无法禁用IP检查。

为了找到答案的根源,我们使用了AFHTTPSessionManager来实现自定义SessiondReceiveAuthenticationChallengeCallback。(见:https://gist.github.com/r00m/e450b8b391a4bf312966)在该回调中,我们使用不检查主机名的SecPolicy验证服务器证书;看见http://blog.roderickmann.org/2013/05/validating-a-self-signed-ssl-certificate-in-ios-and-os-x-against-a-changing-host-name/,这是针对NSURLConnection而不是NSURLSession的较旧实现。

代码:

创建AFHTTPSessionManager

    var manager: AFHTTPSessionManager = AFHTTPSessionManager()
    manager.requestSerializer = AFJSONRequestSerializer() // force serializer to use JSON encoding
    manager.setSessionDidReceiveAuthenticationChallengeBlock { (session, challenge, credential) -> NSURLSessionAuthChallengeDisposition in

        if self.shouldTrustProtectionSpace(challenge, credential: credential) {
            // shouldTrustProtectionSpace will evaluate the challenge using bundled certificates, and set a value into credential if it succeeds
            return NSURLSessionAuthChallengeDisposition.UseCredential
        }
        return NSURLSessionAuthChallengeDisposition.PerformDefaultHandling
    }

自定义验证的实现

class func shouldTrustProtectionSpace(challenge: NSURLAuthenticationChallenge, var credential: AutoreleasingUnsafeMutablePointer<NSURLCredential?>) -> Bool {
    // note: credential is a reference; any created credential should be sent back using credential.memory

    let protectionSpace: NSURLProtectionSpace = challenge.protectionSpace
    var trust: SecTrustRef = protectionSpace.serverTrust!

    // load the root CA bundled with the app
    let certPath: String? = NSBundle.mainBundle().pathForResource("rootCA", ofType: "cer")
    if certPath == nil {
        println("Certificate does not exist!")
        return false
    }

    let certData: NSData = NSData(contentsOfFile: certPath!)!
    let cert: SecCertificateRef? = SecCertificateCreateWithData(kCFAllocatorDefault, certData).takeUnretainedValue()

    if cert == nil {
        println("Certificate data could not be loaded. DER format?")
        return false
    }

    // create a policy that ignores hostname
    let domain: CFString? = nil
    let policy:SecPolicy = SecPolicyCreateSSL(1, domain).takeRetainedValue() 

    // takes all certificates from existing trust
    let numCerts = SecTrustGetCertificateCount(trust)
    var certs: [SecCertificateRef] = [SecCertificateRef]()
    for var i = 0; i < numCerts; i++ {
        let c: SecCertificateRef? = SecTrustGetCertificateAtIndex(trust, i).takeUnretainedValue()
        certs.append(c!)
    }

    // and adds them to the new policy
    var newTrust: Unmanaged<SecTrust>? = nil
    var err: OSStatus = SecTrustCreateWithCertificates(certs, policy, &newTrust)
    if err != noErr {
        println("Could not create trust")
    }
    trust = newTrust!.takeUnretainedValue() // replace old trust

    // set root cert
    let rootCerts: [AnyObject] = [cert!]
    err = SecTrustSetAnchorCertificates(trust, rootCerts)

    // evaluate the certificate and product a trustResult
    var trustResult: SecTrustResultType = SecTrustResultType()
    SecTrustEvaluate(trust, &trustResult)

    if Int(trustResult) == Int(kSecTrustResultProceed) || Int(trustResult) == Int(kSecTrustResultUnspecified) {
        // create the credential to be used
        credential.memory = NSURLCredential(trust: trust)
        return true
    }
    return false
}

在浏览这段代码时,我学到了一些关于swft的知识。

>

  • AFNetworking的setSessionDidReceiveAuthenticationChallengeBlock实现具有以下签名:

    • (空)setSessiondiReceiveAuthentiationChallengeBlock:(nullable NSURLSessionAuthChallengeDisplace(^)(NSURL会话*会话,NSURLAuthentiationChallenge*挑战,NSURLCredential*__nullable__autoreleasing*__nullable凭据))块;

    凭据参数是需要分配的引用/inout变量。在swyft它看起来像这样:自动释放不安全的MutablePointer。为了在C中给它分配一些东西,你会做这样的事情:

    *credential = [[NSURLCredential alloc] initWithTrust...];
    

    在swift中,它如下所示:(使用RKValueTransFormer将NSArray转换为RLMArray时,无法将outputValue转换为AutoreleasingUnsafeMutablePointer

    credential.memory = NSURLCredential(trust: trust)
    

    SecPolicyCreateSSL、SecCertificateCreateWithData和SecTrustGetCertificateAtIndex返回未管理!对象,您必须使用takeRetainedValue()或TakeUnrepainedValue()转换/桥接它们。(见http://nshipster.com/unmanaged/)。当我们使用takeRetainedValue()并多次调用该方法时,内存出现问题/崩溃(SecDestroy出现崩溃)。现在,在我们切换到使用takeUnrepainedValue()之后,构建似乎稳定了,因为在验证之后您不需要证书或ssl策略。

    TLS会话缓存。https://developer.apple.com/library/ios/qa/qa1727/_index.html这意味着当您成功验证一个挑战时,您将永远不会再次获得该挑战。当你测试一个有效的证书,然后测试一个无效的证书,然后跳过所有验证,你会从服务器得到一个成功的响应时,这真的会让你头脑混乱。解决办法是生产-

    因此,这里有一个简单的解决方案,可以解决我在服务器上遇到的问题。我想在这里发布所有信息,希望能帮助其他正在运行本地或开发服务器的人,这些服务器具有自签名CA和需要启用SSL的iOS产品。当然,有了iOS 9中的ATS,我预计很快就会再次深入SSL。

    此代码目前存在一些内存管理问题,将在不久的将来更新。另外,如果有人看到这个实现并说“啊哈,这就像为无效证书返回TRUE一样糟糕”,请让我知道!据我自己的测试所知,应用程序拒绝未经我们的根CA签署的无效服务器证书,并接受由根CA生成和签署的叶证书。应用程序捆绑包仅包含根CA,因此服务器证书可以在过期后循环,现有应用程序不会失败。

    如果我更深入地研究AFNetworking并找到一个一到三行的解决方案(通过切换它们提供的所有小标志),我还会发布一个更新。

    如果AlamoFire开始支持SSL,也可以在这里发布解决方案。

  •  类似资料:
    • 问题内容: 我有一个网络应用程序,允许用户上传pkcs12。我将pkcs12作为二进制存储在数据库中。有什么办法让我知道pkcs12中的证书是自签名的还是CA签名的? 我正在tomcat上运行Java Web应用程序,并且可以使用openssl。 问题答案: 但是,我认为还有一些更重要的事情要解决- 为什么 人们想了解自签名证书。目标是什么?解决了什么问题?在大多数情况下,可能尝试将证书分为自签名

    • 我写了一个小的测试程序来创建 自定义自签名CA证书#1 创建由该CA根证书#1颁发的服务器证书#2 创建具有证书#2的服务器 创建一个RootCA指向证书1的客户端 客户端尝试连接到服务器,但出现错误: 得到“https://localhost:2000:x509:由未知颁发机构签署的证书(可能是因为尝试验证候选颁发机构证书“测试ca”时“x509:Ed25519验证失败”) 我知道这方面的例子有

    • 在任何给定的POD中,服务帐户令牌被挂载在位置。 /var/run/secrets/kubernetes.io/serviceaccount/token 我想了解这个令牌是如何签名的。Kubernetes在签名此令牌时使用什么密钥?如何访问有助于脱机签名验证的公钥 /var/run/secrets/kubernetes.io/serviceaccount/ca.crt-我尝试使用与此证书关联的公钥

    • 我有一个带有这些命令的自签名证书链,并在Apache服务器上配置了它们 但是当我尝试 我从openssl中得到一个错误 用于生成证书的命令或配置文件有问题吗? [req] distinguished_name=req_distinguished_name x509_extensions=v3_ca dirstring_type=nobmp [req_distinguished_name] comm

    • 我已经准备好了用于在我的 AWS ELB 上终止外部 HTTPS 连接。我现在尝试使用带有自签名证书的 HTTPS 来保护 EC2 上的 ELB 和后端 NGINX 服务器之间的连接。我已经按照文档进行操作,但是通过HTTPS访问服务器会导致408 HTTP超时。我似乎无法获得任何调试信息来确定故障所在。 < li >我已经确认安全组允许EC2上的ELB和NGINX之间的连接。 < li >我已经