斯威夫特的iOS客户端证书认证 [英] Swift iOS Client Certificate Authentication

查看:138
本文介绍了斯威夫特的iOS客户端证书认证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

该网站的服务,我想消费,需要一个客户端证书。我怎样才能把我的证书呢?

要进一步阐述我不明白如何创建 SecIdentityRef

在我的 NSURLConnection的 didReceiveAuthenticationChallenge 我在 ServerTrust

 如果挑战别的吗?.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate
    {
        VAR secIdent:SecIdentityRef = ?????????
        VAR certCred = NSURLCredential(身份:secIdent,证书:[getClientCertificate()],持久性:NSURLCredentialPersistence.Permanent)
        挑战.sender.useCredential?(certCred,forAuthenticationChallenge:挑战!)
    }

getClientCertificate 方法:

  FUNC getClientCertificate() -  GT; SecCertificateRef
{
    让mainBundle:一个NSBundle = NSBundle.mainBundle()
    VAR mainBund = mainBundle.pathForResource(iosClientCert,ofType:CER)//导出的证书DER格式。
    VAR键:NSData的NSData的=(contentsOfFile:mainBund)!
    VAR turnToCert:SecCertificateRef = SecCertificateCreateWithData(kCFAllocatorDefault,键).takeRetainedValue()    返回turnToCert;
}


解决方案

从技术上讲,如果有人知道我需要实施斯威夫特,他使用,以获得NSURLCredential对象的连接下面的Objective-C的实施;基于包含在一个PKCS12密钥库私钥和X509证书对

对不起,我没有获得与斯威夫特的解决方案的来源。我所知道的是,NSURLCredential返回到斯威夫特,并直接在HTTP URL连接有使用。这是与此类似,虽然。

我不是一个iOS的开发,所以我不能帮你解决了桥接雨燕的一部分。

   - (无效)getMessageWithURL:(* NSString的)网址{    NSURL * URL = [NSURL URLWithString:URL]    NSMutableURLRequest *请求= [[NSMutableURLRequest的alloc]初始化];
    [要求setURL:URL]
    [要求setHTTPMethod:@GET];
    NSURLConnection的*连接= [[NSURLConnection的页头] initWithRequest:要求委托:自我];
    [连接个体经营];
} - (无效)postMessageWithURL:(* NSString的)网址withContent:(* NSString的)含量{    NSData的* POSTDATA = [内容dataUsingEncoding:NSUTF8StringEncoding];
    * NSString的postLength = [的NSString stringWithFormat:@%D,[POSTDATA长度]];    NSURL * myURL = [NSURL URLWithString:URL]
    NSMutableURLRequest *请求= [NSMutableURLRequest requestWithURL:myURL的CachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];    [要求setHTTPMethod:@POST];
    [申请的setValue:postLength forHTTPHeaderField:@内容长度];
    [申请的setValue:@应用程序/ JSONforHTTPHeaderField:@的Content-Type];
    [要求setHTTPBody:POSTDATA];    NSURLConnection的*连接= [[NSURLConnection的页头] initWithRequest:要求委托:自我];
    [连接个体经营];} - (BOOL)连接:(NSURLConnection的*)连接canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    返回[protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
} - (无效)连接:(NSURLConnection的*)连接didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *){挑战
    的NSLog(@didReceiveAuthenticationChallenge);
} - (无效)连接:(NSURLConnection的*)连接didReceiveResponse:(NSURLResponse *){响应
    responseData = [[NSMutableData的alloc]初始化];
} - (无效)连接:(NSURLConnection的*)连接didReceiveData:(NSData的*)数据{
    [responseData appendData:数据]
} - (无效)连接:(NSURLConnection的*)连接didFailWithError:(NSError *)错误{
    的NSLog(@无法获取数据);
    的NSLog(@%@,错误);
} - (无效)connectionDidFinishLoading:(NSURLConnection的*)连接{
    的NSLog(@成功了!接收数据的%lu个字节,(无符号长)responseData
            长度]);    * NSString的responseString = [[NSString的页头] initWithData:responseData编码:NSUTF8StringEncoding];
    的NSLog(@%@,responseString);    [桥callHandler:handlerName数据:responseString];} - (无效)连接:(NSURLConnection的*)连接willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *){挑战    / *
    读出证书和创建的标识
    * /
    NSArray的*路径= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
    的NSString * documentsDirectory =路径[0]; //获取文件目录    NSData的* p12data = [CertificateManager getP12Data] //实际上返回一个包含一个有效的PKCS12证书的字节数组    如果(!p12data){
      返回;
      NSAssert(p12data,@无法加载P12文件...);
    }    CFStringRef密码= CFSTR(密码);    常量无效*键[] = {} kSecImportExportPassphrase;
    常量无效*值[] = {}密码;
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL,键,值1,NULL,NULL);
    CFArrayRef p12Items;    OSStatus结果= SecPKCS12Import((__桥CFDataRef)p12data,optionsDictionary,&安培; p12Items);    如果(结果==诺尔){
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items,0);
        SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);        SecCertificateRef certRef;
        SecIdentityCopyCertificate(identityApp,&安培; certRef);        SecCertificateRef certArray [1] = {certRef};
        CFArrayRef myCerts = CFArrayCreate(NULL,(无效*)certArray,1,NULL);
        CFRelease(certRef);        NSURLCredential *证书= [NSURLCredential credentialWithIdentity:identityApp证书:无持久性:NSURLCredentialPersistenceNone];
        CFRelease(myCerts);        [挑战发件人] useCredential:凭证forAuthenticationChallenge:挑战]
    }
    其他{
        //证书无效或密码无效发出的证明书
        的NSLog(@无效的证书或密码);
        NSError *错误= [NSError errorWithDomain:NSOSStatusErrorDomain code:导致用户信息:无];
        返回;
    }
}

编辑:喀拉HAR,很搞笑,downvoting我两次,当你自己没有理会,而赏金小涨。 *叽*

不管怎么说,使用上面下面,你只需要从斯威夫特访问它。

  FUNC连接(连接:NSURLConnection的,willSendRequestForAuthenticationChallenge挑战:NSURLAuthenticationChallenge){
    如果让p12Data = UserManager.currentP12,
       让凭证= CertificateManager.getCredentialsForP12(p12Data)作为? NSURLCredential {
            challenge.sender.useCredential(凭证,forAuthenticationChallenge:挑战)
    }其他{
        UIApplication.sharedApplication()。networkActivityIndi​​catorVisible = FALSE
    }
}

使用此。

  +(ID)getCredentialsForP12:(NSData的*){P12
    NSData的* p12data = P12;
    常量无效*键[] = {} kSecImportExportPassphrase;
    常量无效*值[] = {CFSTR(thePassword)};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL,键,值1,NULL,NULL);
    CFArrayRef p12Items;
    OSStatus结果= SecPKCS12Import((__桥CFDataRef)p12data,optionsDictionary,&安培; p12Items);
    如果(结果==诺尔){
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items,0);
        SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);
        SecCertificateRef certRef;
        SecIdentityCopyCertificate(identityApp,&安培; certRef);
        SecCertificateRef certArray [1] = {certRef};
        CFArrayRef myCerts = CFArrayCreate(NULL,(无效*)certArray,1,NULL);
        CFRelease(certRef);        NSURLCredential *证书= [NSURLCredential credentialWithIdentity:identityApp证书:无持久性:NSURLCredentialPersistenceNone];
        CFRelease(myCerts);
        返回凭证;    }
    其他{
        //证书无效或密码无效发出的证明书
        的NSLog(@无效的证书或密码);        UIAlertView中* AV = [[UIAlertView中页头] initWithTitle:@错误消息:@无效的证书或通过​​委托:无cancelButtonTitle:@OKotherButtonTitles:无];
        [AV秀]
        NSError *错误= [NSError errorWithDomain:NSOSStatusErrorDomain code:导致用户信息:无];
        回零;
    }

编辑:上面的一个迅速的版本是在这里,虽然这是很混乱的,我们只是宁可不使用它

  VAR p12items:非托管< CFArrayRef&GT ;?            让指数:CFIndex = 1
            让密码:CFString字符串=密码
            让键= kSecImportExportPassphrase.takeRetainedValue()作为字符串
            变种值= [unsafeAddressOf(密码)]
            VAR键= [unsafeAddressOf(钥匙)            VAR keyCallbacks = kCFTypeDictionaryKeyCallBacks
            VAR valueCallbacks = kCFTypeDictionaryValueCallBacks            让长度:CFIndex = p12Data.length
            让p12CfData:CFData = CFDataCreate(kCFAllocatorDefault,UnsafePointer<&UINT8 GT;(p12Data.bytes),长度)            让选项= CFDictionaryCreate(kCFAllocatorDefault,和放大器;键,&安培;价值指数,和放大器; keyCallbacks,&安培; valueCallbacks)
            让结果= SecPKCS12Import(p12CfData,期权和放大器; p12items)            如果结果== {诺尔                让idIndex:CFIndex = 0
                VAR项目= p12items?.takeRetainedValue()
                VAR identityDict = CFArrayGetValueAtIndex(项目!idIndex)                VAR键= kSecImportItemIdentity.takeRetainedValue()作为字符串
                VAR keyAddress = unsafeAddressOf(键)
                VAR identityApp:SecIdentityRef = CFDictionaryGetValue(identityDict,keyAddress)
                VAR certRef:非托管< SecCertificateRef&GT ;?
                SecIdentityCopyCertificate(identityApp,&安培; certRef)                VAR证书:SecCertificateRef = certRef .takeRetainedValue()!
                VAR certArray = [unsafeAddressOf(CERT)
                VAR arrayCallback = kCFTypeArrayCallBacks
                VAR myCerts:CFArrayRef = CFArrayCreate(kCFAllocatorDefault,&安培; certArray,索引和安培; arrayCallback);                让凭据:NSURLCredential = NSURLCredential(身份:identityApp,证书:[AnyObject](),持久性:NSURLCredentialPersistence.None)

The web service I want to consume requires a client certificate. How can I send my certificate to it?

To further elaborate I don't understand how to create the SecIdentityRef.

In my NSURLConnection didReceiveAuthenticationChallenge I've got this conditional after ServerTrust:

else if challenge?.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate
    {
        var secIdent : SecIdentityRef = ?????????
        var certCred = NSURLCredential(identity: secIdent, certificates: [getClientCertificate()], persistence: NSURLCredentialPersistence.Permanent)
        challenge?.sender.useCredential(certCred, forAuthenticationChallenge: challenge!)
    }

The getClientCertificate method:

func getClientCertificate() -> SecCertificateRef
{
    let mainBundle : NSBundle = NSBundle.mainBundle()
    var mainBund = mainBundle.pathForResource("iosClientCert", ofType: "cer") //exported the cert in der format.
    var key : NSData = NSData(contentsOfFile: mainBund!)!
    var turnToCert : SecCertificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, key).takeRetainedValue()

    return turnToCert;
}

解决方案

Technically, when someone I know needed the implementation in Swift, he used the following Objective-C implementation in order to get the NSURLCredential object to the connection; based on the private key and X509 Certificate pair contained in a PKCS12 keystore.

Sorry, I don't have access to the source with the Swift solution. All I know is that the NSURLCredential was returned to Swift, and used directly in the http url connection there. It's similar to this one, though.

I'm not an iOS dev so I won't be able to help you out with the "bridging to Swift" part.

- (void)getMessageWithURL:(NSString *)url {

    NSURL *URL = [NSURL URLWithString:url];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:URL];
    [request setHTTPMethod:@"GET"];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection self];
}

- (void)postMessageWithURL:(NSString *)url withContent:(NSString *)content {

    NSData *postData = [content dataUsingEncoding:NSUTF8StringEncoding];
    NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];

    NSURL *myURL = [NSURL URLWithString:url];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];

    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection self];

}

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
    return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    NSLog(@"didReceiveAuthenticationChallenge");
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    responseData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Unable to fetch data");
    NSLog(@"%@", error);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSLog(@"Succeeded! Received %lu bytes of data", (unsigned long)[responseData
            length]);

    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    NSLog(@"%@", responseString);

    [bridge callHandler:handlerName data:responseString];

}

- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {

    /*
    Reading the certificate and creating the identity
    */
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = paths[0]; // Get documents directory

    NSData *p12data = [CertificateManager getP12Data]; //returns essentially a byte array containing a valid PKCS12 certificate

    if (!p12data) {
      return;
      NSAssert(p12data, @"Couldn't load p12 file...");
    }

    CFStringRef password = CFSTR("password");

    const void *keys[] = {kSecImportExportPassphrase};
    const void *values[] = {password};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef p12Items;

    OSStatus result = SecPKCS12Import((__bridge CFDataRef) p12data, optionsDictionary, &p12Items);

    if (result == noErr) {
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
        SecIdentityRef identityApp = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity);

        SecCertificateRef certRef;
        SecIdentityCopyCertificate(identityApp, &certRef);

        SecCertificateRef certArray[1] = {certRef};
        CFArrayRef myCerts = CFArrayCreate(NULL, (void *) certArray, 1, NULL);
        CFRelease(certRef);

        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:nil persistence:NSURLCredentialPersistenceNone];
        CFRelease(myCerts);

        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    }
    else {
        // Certificate is invalid or password is invalid given the certificate
        NSLog(@"Invalid certificate or password");
        NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:result userInfo:nil];
        return;
    }
}

EDIT: Har har, very funny, downvoting me twice when you yourself didn't bother while the bounty was up. *grumble *

Anyways, to use the following above, you just need to access it from Swift.

func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
    if let p12Data = UserManager.currentP12,
       let credential = CertificateManager.getCredentialsForP12(p12Data) as? NSURLCredential {
            challenge.sender.useCredential(credential, forAuthenticationChallenge: challenge)
    } else {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    }   
}

That uses this.

+ (id)getCredentialsForP12:(NSData *)p12 {
    NSData* p12data = p12;
    const void *keys[] = {kSecImportExportPassphrase};
    const void *values[] = {CFSTR("thePassword")};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef p12Items;
    OSStatus result = SecPKCS12Import((__bridge CFDataRef) p12data, optionsDictionary, &p12Items);
    if (result == noErr) {
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(p12Items, 0);
        SecIdentityRef identityApp = (SecIdentityRef) CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
        SecCertificateRef certRef;
        SecIdentityCopyCertificate(identityApp, &certRef);
        SecCertificateRef certArray[1] = {certRef};
        CFArrayRef myCerts = CFArrayCreate(NULL, (void *) certArray, 1, NULL);
        CFRelease(certRef);

        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identityApp certificates:nil persistence:NSURLCredentialPersistenceNone];
        CFRelease(myCerts);
        return credential;

    }
    else {
        // Certificate is invalid or password is invalid given the certificate
        NSLog(@"Invalid certificate or password");

        UIAlertView* av = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Invalid cert or pass" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles: nil];
        [av show];
        NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:result userInfo:nil];
        return nil;
    }

EDIT: A swift version of the above is here, although it was messy enough that we rather just didn't use it.

            var p12items : Unmanaged<CFArrayRef>?

            let index: CFIndex = 1
            let password: CFString = "password"
            let key = kSecImportExportPassphrase.takeRetainedValue() as String
            var values = [unsafeAddressOf(password)]
            var keys = [unsafeAddressOf(key)]

            var keyCallbacks = kCFTypeDictionaryKeyCallBacks
            var valueCallbacks = kCFTypeDictionaryValueCallBacks

            let length: CFIndex = p12Data.length
            let p12CfData: CFData = CFDataCreate(kCFAllocatorDefault, UnsafePointer<UInt8>(p12Data.bytes), length)

            let options = CFDictionaryCreate(kCFAllocatorDefault, &keys, &values, index, &keyCallbacks, &valueCallbacks)
            let result = SecPKCS12Import(p12CfData, options, &p12items)

            if result == noErr {

                let idIndex: CFIndex = 0
                var items = p12items?.takeRetainedValue()
                var identityDict = CFArrayGetValueAtIndex(items!, idIndex) 

                var key = kSecImportItemIdentity.takeRetainedValue() as String
                var keyAddress = unsafeAddressOf(key)
                var identityApp: SecIdentityRef = CFDictionaryGetValue(identityDict, keyAddress) 
                var certRef : Unmanaged<SecCertificateRef>?
                SecIdentityCopyCertificate(identityApp, &certRef)

                var cert: SecCertificateRef = certRef!.takeRetainedValue()
                var certArray = [unsafeAddressOf(cert)]
                var arrayCallback = kCFTypeArrayCallBacks
                var myCerts: CFArrayRef = CFArrayCreate(kCFAllocatorDefault, &certArray, index, &arrayCallback);

                let credential: NSURLCredential = NSURLCredential(identity: identityApp, certificates: [AnyObject](), persistence: NSURLCredentialPersistence.None)

这篇关于斯威夫特的iOS客户端证书认证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆