首页 > 随笔档案 > Node.js(restify) + Swift HTTPS 双向验证

Node.js(restify) + Swift HTTPS 双向验证

Publish:

相关证书生成 参照: Node.Js TLS(SSL) HTTPS双向验证

在这只简单记录过程,更详细的看上面👆原文

自签名的CA证书:

openssl genrsa -out ca-key.pem -des 1024  

openssl req -new -key ca-key.pem -out ca-csr.pem  

openssl x509 -req -days 3650 -in ca-csr.pem -signkey ca-key.pem -out ca-cert.pem 

生成服务器私钥及证书:

openssl genrsa -out server-key.pem 1024  

openssl req -new -key server-key.pem -out server-csr.pem  

openssl x509 -req -days 730 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in server-csr.pem -out server-cert.pem -extensions v3_req  

生成客户端证书:

openssl genrsa -out client-key.pem  

openssl req -new -key client-key.pem -out client-csr.pem  

openssl x509 -req -days 365 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -in client-csr.pem -out client-cert.pem 

服务器私钥、证书以及CA证书打包成一个单独的.pfx或.p12文件:

openssl pkcs12 -export -in server-cert.pem -inkey server-key.pem -certfile ca-cert.pem -out server.pfx
openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -certfile ca-cert.pem -out client.p12

生成文件:

ca-cert.pem  ca-csr.pem  client-cert.pem  client-key.pem  server-cert.pem  server-key.pem  
ca-cert.srl  ca-key.pem  client-csr.pem   client.p12      server-csr.pem   server.pfx

Node.js 服务器代码: (使用restify)

var restify=require('restify');
var fs=require('fs');

var server=restify.createServer({
    certificate: fs.readFileSync('server-cert.pem'),
    key: fs.readFileSync('server-key.pem'),
    requestCert: true
});

server.get('/', function(req, res, next) {
	res.send(200,"{}");
});
server.listen(8000, function() {
    console.log('servers up...');
});

Swift 客户端代码(app):(使用Alamofire)

import Alamofire
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        //认证相关设置
        let manager = SessionManager.default
        manager.delegate.sessionDidReceiveChallenge = { session, challenge in
            if challenge.protectionSpace.authenticationMethod
                == NSURLAuthenticationMethodServerTrust
            {
                print("服务端证书认证!")
                let serverTrust:SecTrust = challenge.protectionSpace.serverTrust!
                let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
                let remoteCertificateData :Data
                    = CFBridgingRetain(SecCertificateCopyData(certificate))! as! Data
                let cerPath = Bundle.main.path(forResource: "192.168.10.18", ofType: "cer")!
                let cerUrl = URL(fileURLWithPath:cerPath)
                
                let localCertificateData = try! Data(contentsOf: cerUrl)
               
                
                if (remoteCertificateData == localCertificateData) {
                    let credential = URLCredential(trust: serverTrust)
                    challenge.sender?.use(credential, for: challenge)
                    return (URLSession.AuthChallengeDisposition.useCredential,
                            URLCredential(trust: challenge.protectionSpace.serverTrust!))
                } else {
                    return (.cancelAuthenticationChallenge, nil)
                }
            }
                //认证客户端证书
            else if challenge.protectionSpace.authenticationMethod
                == NSURLAuthenticationMethodClientCertificate {
                print("客户端证书认证!")
                //获取客户端证书相关信息
                let identityAndTrust:IdentityAndTrust = self.extractIdentity();
                
                let urlCredential:URLCredential = URLCredential(
                    identity: identityAndTrust.identityRef,
                    certificates: identityAndTrust.certArray as? [AnyObject],
                    persistence: URLCredential.Persistence.forSession);
                
                return (.useCredential, urlCredential);
            }
                // 其它情况(不接受认证)
            else {
                print("其它情况(不接受认证)")
                return (.cancelAuthenticationChallenge, nil)
            }
            
        }
        
        //数据请求
        Alamofire.request("https://192.168.10.18:8000")
            .responseString { response in
                print(response)
        }
        
        
    }
    
    func extractIdentity() -> IdentityAndTrust {
        var identityAndTrust:IdentityAndTrust!
        var securityError:OSStatus = errSecSuccess
        
        let path: String = Bundle.main.path(forResource: "client", ofType: "p12")!
        let PKCS12Data = NSData(contentsOfFile:path)!
        let key : NSString = kSecImportExportPassphrase as NSString
        let options : NSDictionary = [key : "XXX"] //客户端证书密码
        //create variable for holding security information
        //var privateKeyRef: SecKeyRef? = nil
        
        var items : CFArray?
        
        securityError = SecPKCS12Import(PKCS12Data, options, &items)
        
        if securityError == errSecSuccess {
            let certItems:CFArray = items as CFArray!;
            let certItemsArray:Array = certItems as Array
            let dict:AnyObject? = certItemsArray.first;
            if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> {
                // grab the identity
                let identityPointer:AnyObject? = certEntry["identity"];
                let secIdentityRef:SecIdentity = identityPointer as! SecIdentity!
//                print("\(identityPointer)  :::: \(secIdentityRef)")
                // grab the trust
                let trustPointer:AnyObject? = certEntry["trust"]
                let trustRef:SecTrust = trustPointer as! SecTrust
//                print("\(trustPointer)  :::: \(trustRef)")
                // grab the cert
                let chainPointer:AnyObject? = certEntry["chain"]
                identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef,
                                                    trust: trustRef, certArray:  chainPointer!)
            }
        }
        return identityAndTrust;
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
}

//定义一个结构体,存储认证相关信息
struct IdentityAndTrust {
    var identityRef:SecIdentity
    var trust:SecTrust
    var certArray:AnyObject
}

ps:文件中并没有生成.cer后缀的文件,本着偷懒的原则,直接浏览器访问https://192.168.10.18:8000,会出现提示框信任证书,在钥匙串中找到该证书,导出.cer

声明: 本文采用 BY-NC-SA 授权。转载请注明转自: levy