手动验证TLS证书(ios objective-c)

手动验证TLS证书(ios objective-c),ios,objective-c,certificate,nsurlconnection,tls1.2,Ios,Objective C,Certificate,Nsurlconnection,Tls1.2,我正在创建一个iOS应用程序,它使用TLS与web服务器通信。当我访问web服务器时,它会向我的设备提供一个证书,我想验证我是否可以信任它 我将根证书作为der文件嵌入到我的xcode项目中。到目前为止,我的代码在NSURLConnection的委托函数中获取两个证书的NSString版本以进行身份验证质询 关于如何手动验证有什么想法吗 以下是我当前的代码: - (void)connection:(NSURLConnection *)connection willSendRequestForAu

我正在创建一个iOS应用程序,它使用TLS与web服务器通信。当我访问web服务器时,它会向我的设备提供一个证书,我想验证我是否可以信任它

我将根证书作为der文件嵌入到我的xcode项目中。到目前为止,我的代码在NSURLConnection的委托函数中获取两个证书的NSString版本以进行身份验证质询

关于如何手动验证有什么想法吗

以下是我当前的代码:

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@"Certificate challenge");
if (self.stageNum==0) {
    id <NSURLAuthenticationChallengeSender> sender=challenge.sender;
    NSURLProtectionSpace *protectionSpace=challenge.protectionSpace;
    SecTrustRef trust=[protectionSpace serverTrust];

    //Get server certificate
    SecCertificateRef certificate=SecTrustGetCertificateAtIndex(trust, 0);
    NSData *serverCertificateData=(__bridge NSData *)SecCertificateCopyData(certificate);
    NSString *serverBase64Certificate=[serverCertificateData base64EncodedStringWithOptions:0];

    //Get our certificate
    NSData *certData1 = [[NSData alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"SUMOSRoot" ofType:@"der"]];
    NSString *certBase64=[certData1 base64EncodedStringWithOptions:0];

    //Heres where I need to compare the certificates
    //
    //

    [sender useCredential:[NSURLCredential credentialForTrust:protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
}
-(void)连接:(NSURLConnection*)连接将发送RequestForAuthenticationChallenge:(nsurAuthenticationChallenge*)质询{
NSLog(“证书质询”);
if(self.stageNum==0){
id sender=challenge.sender;
NSURLProtectionSpace*protectionSpace=challenge.protectionSpace;
SecTrustRef trust=[protectionSpace serverTrust];
//获取服务器证书
SecCertificateRef certificate=SecTrustGetCertificateAtIndex(信任,0);
NSData*serverCertificateData=(\uu桥NSData*)SecCertificateCopyData(证书);
NSString*serverBase64Certificate=[serverCertificateData base64EncodedStringWithOptions:0];
//拿到我们的证书
NSData*certData1=[[NSData alloc]INITWITHCONTENTSOFILE:[[NSBundle mainBundle]路径资源:@“der”类型的“SUMOSRoot”;
NSString*certBase64=[certData1 base64EncodedStringWithOptions:0];
//这是我需要比较证书的地方
//
//
[sender useCredential:[NSURLCredential Credential ForTrust:protectionSpace.serverTrust]用于验证质询:质询];
}
}

我认为这篇文章解决了你的问题。写得/解释得很好

那些懒得打开链接的人,我会在这里添加一些描述


我不确定您为什么要获得
NSString
表示。一旦有了
SecTrustRef
,就可以调用
SecTrustCopyPublicKey
将公钥复制到SecKeyRef,然后将其与原始密钥数据(原始字节)进行比较。如果公钥匹配,您应该接受证书

这种技术被称为“证书固定”,我相信这里可能有很多关于堆栈溢出的答案,如果您搜索该术语,它们可以提供完整的代码示例

表面上,您也可以比较证书,但这可能不是您想要做的,因为证书会定期过期并必须更新。如果您比较密钥,那么即使他们重新生成证书(假设他们使用相同的私钥/公钥对重新生成证书),它也将继续工作

或者,如果您愿意将其绑定到一个特定的长期私有根证书,您可以将其添加到受信任的锚列表中,并重新评估信任对象,但需要注意的是,当私有根证书过期时,这将中断。另一方面,如果您锁定了密钥,如果您出于某种原因必须更改私钥,它将断开。。。所以这是一个折衷


当然,如果可能的话,最好的选择通常是获得一个真正的CA证书,并且不要干扰任何链验证,但我假设这是不可能的(例如,因为服务器位于一个.local域中,在一个物理设备上,该设备生成自己的证书而不访问Internet,或者其他类似的原因)。因此,对于像在本地网络上与任意硬件进行安全通信这样的事情,密钥固定是最好的方法。如果密钥不匹配,则要求用户将其添加为新设备或其他任何设备。

这会起作用,但并不理想,因为证书最终将过期并重新生成,因此将不再匹配预期的哈希。最好从信任对象获取公钥,并将其与期望的密钥进行比较。这样,即使管理员重新生成证书(假设管理员不更改密钥),它仍将继续信任服务器。感谢链接!我来看看谢谢你的回答。我将进一步研究证书固定。您是对的,在这种情况下不可能使用真正的CA证书