I want to install/save a certificate in keychain before the user visits the site. I have a HTTPS server, and my app authenticates the user before he goes to the https://mysite. Is there a way that I can install/save the certificate via post request in the keychain. OR I copy that certificate (the file) to resource bundle to mark it trusted.
我想在用户访问网站之前在钥匙串中安装/保存证书。我有一个HTTPS服务器,我的应用程序在访问https:// mysite之前对用户进行身份验证。有没有办法可以通过钥匙串中的邮政请求安装/保存证书。或者我将该证书(文件)复制到资源包以将其标记为受信任。
thanks
al
2 个解决方案
#1
14
Once you have the server certificate in der format you can try the following code:
获得der格式的服务器证书后,您可以尝试以下代码:
+ (void) addCertToKeychain:(NSData*)certInDer
{
OSStatus err = noErr;
SecCertificateRef cert;
cert = SecCertificateCreateWithData(NULL, (CFDataRef) certInDer);
assert(cert != NULL);
CFTypeRef result;
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassCertificate, kSecClass,
cert, kSecValueRef,
nil];
err = SecItemAdd((CFDictionaryRef)dict, &result);
assert(err == noErr || err == errSecDuplicateItem);
CFRelease(cert);
}
It will add the certificate to the keychain sandbox of your application i.e. no other application will trust your cert.
它会将证书添加到应用程序的keychain沙箱中,即其他应用程序不会信任您的证书。
#2
7
You have two options available: add your server’s certificate to the keychain or perform validation manually. Regardless of your approach, you’ll need to include a DER-encoded X.509 public certificate in your app. In the example below, it is named “ios-trusted-cert.der”) and create a SecCertificateRef with it. (If your server’s certificate is part of a chain to a root certificate authority, you should install the root certificate authority rather than your server’s certificate.)
您有两个选项:将服务器的证书添加到钥匙串或手动执行验证。无论您采用何种方法,都需要在应用中包含DER编码的X.509公共证书。在下面的示例中,它名为“ios-trusted-cert.der”)并使用它创建SecCertificateRef。 (如果服务器的证书是根证书颁发机构链的一部分,则应安装根证书颁发机构而不是服务器的证书。)
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSData *iosTrustedCertDerData =
[NSData dataWithContentsOfFile:[bundle pathForResource:@"ios-trusted-cert"
ofType:@"der"]];
SecCertificateRef certificate =
SecCertificateCreateWithData(NULL,
(CFDataRef) iosTrustedCertDerData);
Remember that SecCertificateCreateWithData follows the create rule of memory ownership, so you must CFRelease it when you no longer need it to avoid memory leaks.
请记住,SecCertificateCreateWithData遵循内存所有权的创建规则,因此您必须在不再需要它时CFRelease它以避免内存泄漏。
Next, you can add your cert to your app’s keychain. This is appropriate when you want iOS to trust your cert for every new socket you create.
接下来,您可以将证书添加到应用程序的钥匙串中。当您希望iOS为您创建的每个新套接字信任您的证书时,这是合适的。
- (void) useKeychain: (SecCertificateRef) certificate {
OSStatus err =
SecItemAdd((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
certificate, kSecValueRef,
nil],
NULL);
if ((err == noErr) || // success!
(err == errSecDuplicateItem)) { // the cert was already added. Success!
// create your socket normally.
// This is oversimplified. Refer to the CFNetwork Guide for more details.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
} else {
// handle the error. There is probably something wrong with your cert.
}
}
If you only want to verify the cert for the socket you are creating and for no other sockets in your app, you can verify your trust in the cert manually. First, create a socket (assuming your server is listening on port 8443 on the same machine as your client) and disable its certificate chain validation in its ssl settings:
如果您只想验证要创建的套接字的证书,并且您的应用中没有其他套接字,则可以手动验证您对证书的信任。首先,创建一个套接字(假设您的服务器正在侦听与您的客户端在同一台机器上的端口8443)并在其ssl设置中禁用其证书链验证:
- (void) verifiesManually: (SecCertificateRef) certificate {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
// Set this kCFStreamPropertySocketSecurityLevel before
// setting kCFStreamPropertySSLSettings.
// Setting kCFStreamPropertySocketSecurityLevel
// appears to override previous settings in kCFStreamPropertySSLSettings
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
// this disables certificate chain validation in ssl settings.
NSDictionary *sslSettings =
[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
nil];
CFReadStreamSetProperty(readStream,
kCFStreamPropertySSLSettings,
sslSettings);
NSInputStream *inputStream = (NSInputStream *)readStream;
NSOutputStream *outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
Then, when you receive a callback that your socket is ready to write data, you should verify trust in the certificate your server included before writing any data to or reading any data from the server. First (1), create a client SSL policy with the hostname of the server to which you connected. The hostname is included in the server’s cert to authenticate that the server to which DNS directed you is the server you trust. Next (2), you grab the actual server certificates from the socket. There may be multiple certificates associated with the server if the server’s certificate is part of a certificate chain. When you have the actual server certificates, you can (3) create a trust object. The trust object represents a local context for trust evaluations. It isolates individual trust evaluations whereas the keychain certificates apply to all trusted sockets. After you have a trust object, you can (4) set the anchor certificates, which are the certificates you trust. Finally (5), you can evaluate the trust object and discover whether the server can be trusted.
然后,当您收到套接字已准备好写入数据的回调时,您应该在将任何数据写入服务器或从服务器读取任何数据之前验证服务器所包含的证书中的信任。首先(1),使用您连接的服务器的主机名创建客户端SSL策略。主机名包含在服务器的证书中,用于验证DNS指示您的服务器是您信任的服务器。接下来(2),从套接字中获取实际的服务器证书。如果服务器的证书是证书链的一部分,则可能有多个与服务器关联的证书。拥有实际的服务器证书后,您可以(3)创建信任对象。信任对象表示信任评估的本地上下文。它隔离了单个信任评估,而密钥链证书适用于所有可信套接字。拥有信任对象后,您可以(4)设置锚证书,即您信任的证书。最后(5),您可以评估信任对象并发现服务器是否可信任。
#pragma mark -
#pragma mark NSStreamDelegate
- (void)stream:(NSStream *)aStream
handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
break;
case NSStreamEventHasSpaceAvailable:
// #1
// NO for client, YES for server. In this example, we are a client
// replace "localhost" with the name of the server to which you are connecting
SecPolicyRef policy = SecPolicyCreateSSL(NO, CFSTR("localhost"));
SecTrustRef trust = NULL;
// #2
CFArrayRef streamCertificates =
[aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates];
// #3
SecTrustCreateWithCertificates(streamCertificates,
policy,
&trust);
// #4
SecTrustSetAnchorCertificates(trust,
(CFArrayRef) [NSArray arrayWithObject:(id) self.certificate]);
// #5
SecTrustResultType trustResultType = kSecTrustResultInvalid;
OSStatus status = SecTrustEvaluate(trust, &trustResultType);
if (status == errSecSuccess) {
// expect trustResultType == kSecTrustResultUnspecified
// until my cert exists in the keychain see technote for more detail.
if (trustResultType == kSecTrustResultUnspecified) {
NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType);
} else {
NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType);
}
} else {
NSLog(@"Creating trust failed: %d", status);
[aStream close];
}
if (trust) {
CFRelease(trust);
}
if (policy) {
CFRelease(policy);
}
break;
case NSStreamEventErrorOccurred:
NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]);
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
}
#1
14
Once you have the server certificate in der format you can try the following code:
获得der格式的服务器证书后,您可以尝试以下代码:
+ (void) addCertToKeychain:(NSData*)certInDer
{
OSStatus err = noErr;
SecCertificateRef cert;
cert = SecCertificateCreateWithData(NULL, (CFDataRef) certInDer);
assert(cert != NULL);
CFTypeRef result;
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassCertificate, kSecClass,
cert, kSecValueRef,
nil];
err = SecItemAdd((CFDictionaryRef)dict, &result);
assert(err == noErr || err == errSecDuplicateItem);
CFRelease(cert);
}
It will add the certificate to the keychain sandbox of your application i.e. no other application will trust your cert.
它会将证书添加到应用程序的keychain沙箱中,即其他应用程序不会信任您的证书。
#2
7
You have two options available: add your server’s certificate to the keychain or perform validation manually. Regardless of your approach, you’ll need to include a DER-encoded X.509 public certificate in your app. In the example below, it is named “ios-trusted-cert.der”) and create a SecCertificateRef with it. (If your server’s certificate is part of a chain to a root certificate authority, you should install the root certificate authority rather than your server’s certificate.)
您有两个选项:将服务器的证书添加到钥匙串或手动执行验证。无论您采用何种方法,都需要在应用中包含DER编码的X.509公共证书。在下面的示例中,它名为“ios-trusted-cert.der”)并使用它创建SecCertificateRef。 (如果服务器的证书是根证书颁发机构链的一部分,则应安装根证书颁发机构而不是服务器的证书。)
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSData *iosTrustedCertDerData =
[NSData dataWithContentsOfFile:[bundle pathForResource:@"ios-trusted-cert"
ofType:@"der"]];
SecCertificateRef certificate =
SecCertificateCreateWithData(NULL,
(CFDataRef) iosTrustedCertDerData);
Remember that SecCertificateCreateWithData follows the create rule of memory ownership, so you must CFRelease it when you no longer need it to avoid memory leaks.
请记住,SecCertificateCreateWithData遵循内存所有权的创建规则,因此您必须在不再需要它时CFRelease它以避免内存泄漏。
Next, you can add your cert to your app’s keychain. This is appropriate when you want iOS to trust your cert for every new socket you create.
接下来,您可以将证书添加到应用程序的钥匙串中。当您希望iOS为您创建的每个新套接字信任您的证书时,这是合适的。
- (void) useKeychain: (SecCertificateRef) certificate {
OSStatus err =
SecItemAdd((CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
certificate, kSecValueRef,
nil],
NULL);
if ((err == noErr) || // success!
(err == errSecDuplicateItem)) { // the cert was already added. Success!
// create your socket normally.
// This is oversimplified. Refer to the CFNetwork Guide for more details.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
} else {
// handle the error. There is probably something wrong with your cert.
}
}
If you only want to verify the cert for the socket you are creating and for no other sockets in your app, you can verify your trust in the cert manually. First, create a socket (assuming your server is listening on port 8443 on the same machine as your client) and disable its certificate chain validation in its ssl settings:
如果您只想验证要创建的套接字的证书,并且您的应用中没有其他套接字,则可以手动验证您对证书的信任。首先,创建一个套接字(假设您的服务器正在侦听与您的客户端在同一台机器上的端口8443)并在其ssl设置中禁用其证书链验证:
- (void) verifiesManually: (SecCertificateRef) certificate {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@"localhost",
8443,
&readStream,
&writeStream);
// Set this kCFStreamPropertySocketSecurityLevel before
// setting kCFStreamPropertySSLSettings.
// Setting kCFStreamPropertySocketSecurityLevel
// appears to override previous settings in kCFStreamPropertySSLSettings
CFReadStreamSetProperty(readStream,
kCFStreamPropertySocketSecurityLevel,
kCFStreamSocketSecurityLevelTLSv1);
// this disables certificate chain validation in ssl settings.
NSDictionary *sslSettings =
[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanFalse, (id)kCFStreamSSLValidatesCertificateChain,
nil];
CFReadStreamSetProperty(readStream,
kCFStreamPropertySSLSettings,
sslSettings);
NSInputStream *inputStream = (NSInputStream *)readStream;
NSOutputStream *outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
}
Then, when you receive a callback that your socket is ready to write data, you should verify trust in the certificate your server included before writing any data to or reading any data from the server. First (1), create a client SSL policy with the hostname of the server to which you connected. The hostname is included in the server’s cert to authenticate that the server to which DNS directed you is the server you trust. Next (2), you grab the actual server certificates from the socket. There may be multiple certificates associated with the server if the server’s certificate is part of a certificate chain. When you have the actual server certificates, you can (3) create a trust object. The trust object represents a local context for trust evaluations. It isolates individual trust evaluations whereas the keychain certificates apply to all trusted sockets. After you have a trust object, you can (4) set the anchor certificates, which are the certificates you trust. Finally (5), you can evaluate the trust object and discover whether the server can be trusted.
然后,当您收到套接字已准备好写入数据的回调时,您应该在将任何数据写入服务器或从服务器读取任何数据之前验证服务器所包含的证书中的信任。首先(1),使用您连接的服务器的主机名创建客户端SSL策略。主机名包含在服务器的证书中,用于验证DNS指示您的服务器是您信任的服务器。接下来(2),从套接字中获取实际的服务器证书。如果服务器的证书是证书链的一部分,则可能有多个与服务器关联的证书。拥有实际的服务器证书后,您可以(3)创建信任对象。信任对象表示信任评估的本地上下文。它隔离了单个信任评估,而密钥链证书适用于所有可信套接字。拥有信任对象后,您可以(4)设置锚证书,即您信任的证书。最后(5),您可以评估信任对象并发现服务器是否可信任。
#pragma mark -
#pragma mark NSStreamDelegate
- (void)stream:(NSStream *)aStream
handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
break;
case NSStreamEventHasSpaceAvailable:
// #1
// NO for client, YES for server. In this example, we are a client
// replace "localhost" with the name of the server to which you are connecting
SecPolicyRef policy = SecPolicyCreateSSL(NO, CFSTR("localhost"));
SecTrustRef trust = NULL;
// #2
CFArrayRef streamCertificates =
[aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates];
// #3
SecTrustCreateWithCertificates(streamCertificates,
policy,
&trust);
// #4
SecTrustSetAnchorCertificates(trust,
(CFArrayRef) [NSArray arrayWithObject:(id) self.certificate]);
// #5
SecTrustResultType trustResultType = kSecTrustResultInvalid;
OSStatus status = SecTrustEvaluate(trust, &trustResultType);
if (status == errSecSuccess) {
// expect trustResultType == kSecTrustResultUnspecified
// until my cert exists in the keychain see technote for more detail.
if (trustResultType == kSecTrustResultUnspecified) {
NSLog(@"We can trust this certificate! TrustResultType: %d", trustResultType);
} else {
NSLog(@"Cannot trust certificate. TrustResultType: %d", trustResultType);
}
} else {
NSLog(@"Creating trust failed: %d", status);
[aStream close];
}
if (trust) {
CFRelease(trust);
}
if (policy) {
CFRelease(policy);
}
break;
case NSStreamEventErrorOccurred:
NSLog(@"unexpected NSStreamEventErrorOccurred: %@", [aStream streamError]);
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
}