使用证书创建SSL连接 [英] Create SSL connection using certificate
问题描述
我有以文件形式提供给我的ssl证书(.cer). 我将其添加到捆绑包中,并希望使用它与服务器通信.
我使用了苹果提供的代码:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
DLog(@"didReceiveAuthenticationChallenge : %@",challenge);
if ([challenge.protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust])
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:filePath];
CFDataRef myCertData = (__bridge CFDataRef)certData;
SecCertificateRef myCert = SecCertificateCreateWithData(NULL,
myCertData);
SecPolicyRef myPolicy = SecPolicyCreateBasicX509(); // 3
SecCertificateRef certArray[1] = { myCert };
CFArrayRef myCerts = CFArrayCreate(NULL,
(void *)certArray,
1,
NULL);
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(
myCerts,
myPolicy,
&myTrust); // 4
SecTrustResultType trustResult = 0;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult); // 5
}
// If the trust result is kSecTrustResultInvalid, kSecTrustResultDeny, kSecTrustResultFatalTrustFailure, you cannot proceed and should fail gracefully.
BOOL proceed = NO;
switch (trustResult) {
case kSecTrustResultProceed: // 1
DLog(@"Proceed");
proceed = YES;
break;
case kSecTrustResultConfirm: // 2
DLog(@"Confirm");
proceed = YES;
break;
case kSecTrustResultUnspecified: // 4
DLog(@"Unspecified");
break;
case kSecTrustResultRecoverableTrustFailure: // 5
DLog(@"TrustFailure");
proceed = [self recoverFromTrustFailure:myTrust];
break;
case kSecTrustResultDeny: // 3
DLog(@"Deny");
break;
case kSecTrustResultFatalTrustFailure: // 6
DLog(@"FatalTrustFailure");
break;
case kSecTrustResultOtherError: // 7
DLog(@"OtherError");
break;
case kSecTrustResultInvalid: // 0
DLog(@"Invalid");
break;
default:
DLog(@"Default");
break;
}
if (myPolicy)
CFRelease(myPolicy);
if (proceed) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
}else{
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
}
- (BOOL) recoverFromTrustFailure:(SecTrustRef) myTrust
{
SecTrustResultType trustResult;
OSStatus status = SecTrustEvaluate(myTrust, &trustResult); // 1
//Get time used to verify trust
CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
CFDateRef newDate;
if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
trustTime = SecTrustGetVerifyTime(myTrust); // 3
timeIncrement = 31536000; // 4
currentTime = CFAbsoluteTimeGetCurrent(); // 5
newTime = currentTime - timeIncrement; // 6
if (trustTime - newTime){ // 7
newDate = CFDateCreate(NULL, newTime); // 8
SecTrustSetVerifyDate(myTrust, newDate); // 9
status = SecTrustEvaluate(myTrust, &trustResult); // 10
}
}
if (trustResult != kSecTrustResultProceed) {
DLog(@"Failed with status : %li",trustResult); // 11
return NO;
}else{
DLog(@"Procced");
return YES;
}
}
但是我得到 kSecTrustResultRecoverableTrustFailure .在这种情况下也使用了苹果样品,但没有帮助.
也许有人可以帮助我吗?
谢谢.
如果这是用于服务器信任身份验证的自签名证书,则应执行以下操作:
-
将.CRT编码的证书转换为.DER编码的证书.在终端上输入:
$: openssl x509 -in certificate.crt -outform der -out "com.server.trust_cert.der"
(选择您自己的有意义的名称)
将.DER编码的证书放入包中.
-
实施方法
connection:didReceiveAuthenticationChallenge:
如下. 重要提示:如果任何内容有误,请务必检查错误并纾困,并让身份验证失败!彻底测试!
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
{
do
{
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
if (serverTrust == nil)
break; // failed
SecTrustResultType trustResult;
OSStatus status = SecTrustEvaluate(serverTrust, &trustResult);
if (!(errSecSuccess == status))
break; // fatal error in trust evaluation -> failed
if (!((trustResult == kSecTrustResultProceed)
|| (trustResult == kSecTrustResultUnspecified)))
{
break; // see "Certificate, Key, and Trust Services Reference"
// for explanation of result codes.
}
SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
if (serverCertificate == nil)
break; // failed
CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate);
if (serverCertificateData == nil)
break; // failed
const UInt8* const data = CFDataGetBytePtr(serverCertificateData);
const CFIndex size = CFDataGetLength(serverCertificateData);
NSData* server_cert = [NSData dataWithBytes:data length:(NSUInteger)size];
CFRelease(serverCertificateData);
NSString* file = [[NSBundle mainBundle] pathForResource:@"com.server.trust_cert"
ofType:@"der"];
NSData* my_cert = [NSData dataWithContentsOfFile:file];
if (server_cert == nil || my_cert == nil)
break; // failed
const BOOL equal = [server_cert isEqualToData:my_cert];
if (!equal)
break; // failed
// Athentication succeeded:
return [[challenge sender] useCredential:[NSURLCredential credentialForTrust:serverTrust]
forAuthenticationChallenge:challenge];
} while (0);
// Authentication failed:
return [[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
注意:
上述技术的一种可能的改进是使用公钥固定".
必读:
HTTPS服务器信任评估(Apple官方文档,技术说明TN2232) >
证书,密钥和信任服务参考( Apple官方参考文档)
I have ssl certificate (.cer) which was provided to me as file. I added it to bundle and want to use it communicating with server.
I used apple provided code:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
DLog(@"didReceiveAuthenticationChallenge : %@",challenge);
if ([challenge.protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust])
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:filePath];
CFDataRef myCertData = (__bridge CFDataRef)certData;
SecCertificateRef myCert = SecCertificateCreateWithData(NULL,
myCertData);
SecPolicyRef myPolicy = SecPolicyCreateBasicX509(); // 3
SecCertificateRef certArray[1] = { myCert };
CFArrayRef myCerts = CFArrayCreate(NULL,
(void *)certArray,
1,
NULL);
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates(
myCerts,
myPolicy,
&myTrust); // 4
SecTrustResultType trustResult = 0;
if (status == noErr) {
status = SecTrustEvaluate(myTrust, &trustResult); // 5
}
// If the trust result is kSecTrustResultInvalid, kSecTrustResultDeny, kSecTrustResultFatalTrustFailure, you cannot proceed and should fail gracefully.
BOOL proceed = NO;
switch (trustResult) {
case kSecTrustResultProceed: // 1
DLog(@"Proceed");
proceed = YES;
break;
case kSecTrustResultConfirm: // 2
DLog(@"Confirm");
proceed = YES;
break;
case kSecTrustResultUnspecified: // 4
DLog(@"Unspecified");
break;
case kSecTrustResultRecoverableTrustFailure: // 5
DLog(@"TrustFailure");
proceed = [self recoverFromTrustFailure:myTrust];
break;
case kSecTrustResultDeny: // 3
DLog(@"Deny");
break;
case kSecTrustResultFatalTrustFailure: // 6
DLog(@"FatalTrustFailure");
break;
case kSecTrustResultOtherError: // 7
DLog(@"OtherError");
break;
case kSecTrustResultInvalid: // 0
DLog(@"Invalid");
break;
default:
DLog(@"Default");
break;
}
if (myPolicy)
CFRelease(myPolicy);
if (proceed) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust] forAuthenticationChallenge: challenge];
}else{
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
}
- (BOOL) recoverFromTrustFailure:(SecTrustRef) myTrust
{
SecTrustResultType trustResult;
OSStatus status = SecTrustEvaluate(myTrust, &trustResult); // 1
//Get time used to verify trust
CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
CFDateRef newDate;
if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
trustTime = SecTrustGetVerifyTime(myTrust); // 3
timeIncrement = 31536000; // 4
currentTime = CFAbsoluteTimeGetCurrent(); // 5
newTime = currentTime - timeIncrement; // 6
if (trustTime - newTime){ // 7
newDate = CFDateCreate(NULL, newTime); // 8
SecTrustSetVerifyDate(myTrust, newDate); // 9
status = SecTrustEvaluate(myTrust, &trustResult); // 10
}
}
if (trustResult != kSecTrustResultProceed) {
DLog(@"Failed with status : %li",trustResult); // 11
return NO;
}else{
DLog(@"Procced");
return YES;
}
}
However i am getting kSecTrustResultRecoverableTrustFailure . Also used apple sample in this situation but it didn't helped.
Maybe some one could help me on this ?
Thank you.
If this is a self signed certificate to be used in server trust authentication, you should do the following:
Convert the .CRT encoded certificate into a .DER encoded certificate. On the terminal type:
$: openssl x509 -in certificate.crt -outform der -out "com.server.trust_cert.der"
(choose your own meaningful name)
Put the .DER encoded certificate into the bundle.
Implement the method
connection:didReceiveAuthenticationChallenge:
as follows. Important: always check for errors and bail out and let the authentication fail if anything is wrong!!Test it thoroughly!
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust])
{
do
{
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
if (serverTrust == nil)
break; // failed
SecTrustResultType trustResult;
OSStatus status = SecTrustEvaluate(serverTrust, &trustResult);
if (!(errSecSuccess == status))
break; // fatal error in trust evaluation -> failed
if (!((trustResult == kSecTrustResultProceed)
|| (trustResult == kSecTrustResultUnspecified)))
{
break; // see "Certificate, Key, and Trust Services Reference"
// for explanation of result codes.
}
SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
if (serverCertificate == nil)
break; // failed
CFDataRef serverCertificateData = SecCertificateCopyData(serverCertificate);
if (serverCertificateData == nil)
break; // failed
const UInt8* const data = CFDataGetBytePtr(serverCertificateData);
const CFIndex size = CFDataGetLength(serverCertificateData);
NSData* server_cert = [NSData dataWithBytes:data length:(NSUInteger)size];
CFRelease(serverCertificateData);
NSString* file = [[NSBundle mainBundle] pathForResource:@"com.server.trust_cert"
ofType:@"der"];
NSData* my_cert = [NSData dataWithContentsOfFile:file];
if (server_cert == nil || my_cert == nil)
break; // failed
const BOOL equal = [server_cert isEqualToData:my_cert];
if (!equal)
break; // failed
// Athentication succeeded:
return [[challenge sender] useCredential:[NSURLCredential credentialForTrust:serverTrust]
forAuthenticationChallenge:challenge];
} while (0);
// Authentication failed:
return [[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
Note:
A possible improvement of the above technique is to use "public key pinning".
Must Reads:
HTTPS Server Trust Evaluation (Official Apple Documentation, Technical Note TN2232)
Certificate, Key, and Trust Services Reference (Official Apple Reference Documentation)
这篇关于使用证书创建SSL连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!