我使用 openssl 和 sha 256 创建了一个自签名证书,如下所示:
openssl req -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -keyout server.key -out server.crt
我已经通过配置 httpd-ssl.conf 文件在我的 AMPPS 服务器中正确安装了它。如果我尝试在网络浏览器上执行:
https://localhost
我正确地看到该连接使用 TLS 1.2。当我在 iOS 9 模拟器下运行我的应用程序时,我猜会引发错误 (9813),因为我的证书是自签名的。有没有办法为我的测试目的启用它?我在这里读到我应该向 AppDelegate.swift 类添加一些函数,但它似乎不起作用。
这是我收到的完整错误消息:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
Optional(Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “localhost” which could put your confidential information at risk." UserInfo= {NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x7fc492d54cf0>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9813, NSErrorPeerCertificateChainKey=<CFArray 0x7fc492cc4bc0 [0x103cb67b0]>{type = immutable, count = 1, values = (
0 : <cert(0x7fc492d520c0) s: Lorenzo Vinci i: Lorenzo Vinci>
)}, NSUnderlyingError=0x7fc492cc1370 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x7fc492d54cf0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9813, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9813, kCFStreamPropertySSLPeerCertificates=<CFArray 0x7fc492cc4bc0 [0x103cb67b0]>{type = immutable, count = 1, values = (
0 : <cert(0x7fc492d520c0) s: Lorenzo Vinci i: Lorenzo Vinci>
)}}}, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “localhost” which could put your confidential information at risk., NSErrorFailingURLKey=https://localhost/protected/excogitoweb/mobile/loginM.php? username=lorenzo&password=lorenzo, NSErrorFailingURLStringKey=https://localhost/protected/excogitoweb/mobile/loginM.p hp?username=lorenzo&password=lorenzo, NSErrorClientCertificateStateKey=0})
更新:对于 Mac OS X Yosemite
1) 通过 brew 将 openssl 更新到版本 >= 1.0.2d
2) TLS 1.2仅适用于apache 2.4,实际上 MAMP 仍然适用于 apache 2.2。解决方案:下载并安装运行 apache 2.4.x 版本的 AMPPS 的最新版本
3)使用 sha256(根据 iOS 9 的要求)创建一个自签名证书
openssl req -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -keyout server.key -out server.crt
并将 server.key 和 server.crt 保存到 /Applications/AMPPS/apache/conf
4) 确保 ssl 模块加载到 AMPPS 的 httpd.conf 文件中
5) 编辑文件 /Applications/AMPPS/apache/conf/extra/httpd -ssl.conf 添加:
<VirtualHost localhost:443>
DocumentRoot "/Applications/AMPPS/www"
ServerName localhost
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCertificateFile "/Applications/AMPPS/apache/conf/server.crt"
SSLCertificateKeyFile "/Applications/AMPPS/apache/conf/server.key"
</VirtualHost>
里面
<IfModule ssl_module> ... </IfModule>
6) 每次使用 NSURLSession 时,iOS 9 模拟器都会惹恼你,并引发错误 9813 说证书无效(因为是自签名的)。因此,在您将要使用 NSURLSession 的每个类中,例如,执行以下操作:
class LoginService: NSObject, NSURLSessionDelegate {
func URLSession(session: NSURLSession,
task: NSURLSessionTask,
didReceiveChallenge challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?)
-> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
...
func requestLoginWithURL (requestURL: NSURL, completionHandler: (success: Bool?) -> Void) {
let configuration =
NSURLSessionConfiguration.defaultSessionConfiguration()
let urlRequest: NSURLRequest = NSURLRequest(URL: requestURL)
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
/*
dataTaskWithRequest: creates an HTTP request for the specified URL request object, and calls a handler upon completion.
*/
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
...
}
其中函数 URLSession 是一个委托,它将防止应用程序由于自签名证书而崩溃,并且无论如何它都会接受。
最后将此添加到您的 info.plist 中:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<!--Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow HTTP requests-->
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<!--
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/> -->
</dict>
</dict>
</dict>