相关证书生成 参照: 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