发布时间:2018-09-21
 
技术:iOS12 xcode10 golang1.11
 

概述

iOS12 苹果发布了新的网络框架Network,可以更方便地操作底层网络通信了。使用TLS也很方便,但默认是使用系统安装的根证书验证网站证书的,如果使用自签名根证书来验证自架的网站证书,则麻烦一些,这里给大家演示一下。

详细

需求:

并不是每个SSL/TLS站点都能得到一个全球公认的证书,很多时候需要自行生成自签名证书做为根证书。有了自签名根证书还需要手动地用它去验证服务端证书。

概要:

1.生成自签名概证书,服务端证书

2.做一个使用服务端证书的SSL/TLS的服务

3.做一个使用自签名证书访问服务的客户端

结构:

效果:

 

初始界面 发送接收后

我们开始吧!

  • 证书生成:

先构建目录:

1.mkdir certs

2.cd certs

3.unzip store.zip

操作之后,目录如下:

生成自签名根证书,在centos上依次执行以下命令:

1.私钥:openssl genrsa -out ca.key 1024

2.公钥:openssl rsa -in ca.key -pubout -out ca.pem

3.证书:openssl req -new -x509 -days 365 -key ca.key -out ca.crt

执行完成在当前目录下产生以下文件:

其中ca.pem就是生成的自签名根证书。

生成服务端证书,在centos上依次执行如下命令:

1.私钥:openssl genrsa -out server.key 1024

2.公钥:openssl rsa -in server.key -pubout -out server.pem

3.请求:openssl req -new -nodes -key server.key -out server.csr

4.签证:openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config store/openssl.cnf

执行完成后当前目录如下所示:

其中server.pem,server.key分别是服务端的证书和私钥。因为iOS需der格式的证书,我们把根证书ca.pem转换一下。

5.转换:openssl x509 -outform der -in ca.crt -out ca.der

  • 服务端程序:

好了,所需证书都已生成。其中服务端需要server.crt, server.key,需客户端需要ca.der。下面我们先做一个非常简单的服务端,用来配合客户端的连接测试。在centos上创建文件server.go,内容如下:

package main

import (
"log"
"io"
"net"
"crypto/tls"
) func main() {
crt, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
conf := &tls.Config{Certificates: []tls.Certificate{crt}}
listener, err := tls.Listen("tcp", ":8080", conf)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
process(conn)
conn.Close()
}
} func process(conn net.Conn) {
rdata := make([]byte, 2048)
rlen, err := conn.Read(rdata)
if err != nil && err != io.EOF {
log.Println(err)
return
}
_, err = conn.Write(rdata[:rlen])
if err != nil {
log.Println(err)
return
}
}

程序很简单,创建支持tls的服务程序,接收到发送过来的内容,再原样返回出去。

编译:go build server.go

注意:server.crt, server.key与编译出来的server放在同一目录下。然后,执行程序,等待连接到来。

执行:./server

  • 客户端程序:

创建一个iOS工程, 然后把ca.der拖到工程下面:

注意:添加ca.der时,一定要选上Add to targets选项。

Main.storyboard里添加一个Label和一个Button即可,我们毕竟只是演示tls如何工作,没必要搞那么花哨。

在ViewController里添加上如下代码:

import UIKit
import Network class ViewController: UIViewController {
@IBOutlet weak var messageLabel: UILabel! let queue = DispatchQueue(label: "myqueue")
var conn: NWConnection! override func viewDidLoad() {
super.viewDidLoad()
messageLabel.layer.borderWidth = 1
} @IBAction func start(_ sender: Any) {
let host = NWEndpoint.Host("10.21.16.202")
let port = NWEndpoint.Port(integerLiteral: 8080) let options = NWProtocolTLS.Options()
sec_protocol_options_set_verify_block(options.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in // 为信任证书链设置自签名根证书
let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()
if let url = Bundle.main.url(forResource: "ca", withExtension: "der"),
let data = try? Data(contentsOf: url),
let cert = SecCertificateCreateWithData(nil, data as CFData) {
if SecTrustSetAnchorCertificates(trust, [cert] as CFArray) != errSecSuccess {
sec_protocol_verify_complete(false)
return
}
} // 设置验证策略
let policy = SecPolicyCreateSSL(true, "myserver" as CFString)
SecTrustSetPolicies(trust, policy)
SecTrustSetAnchorCertificatesOnly(trust, true) // 验证证书链
var error: CFError?
if SecTrustEvaluateWithError(trust, &error) {
sec_protocol_verify_complete(true) } else {
sec_protocol_verify_complete(false)
print(error!)
}
}, queue) conn = NWConnection(host: host, port: port, using: NWParameters(tls: options))
conn.start(queue: queue) let messge = "hello"
conn.send(content: messge.data(using: .utf8)!, completion: .contentProcessed({ (error) in
if let error = error {
print(error)
self.conn.cancel()
} else {
print("消息已发送:\(messge)")
}
})) conn.receive(minimumIncompleteLength: 1, maximumLength: 1024) { (data, context, isComplete, error) in
if let error = error {
print(error)
self.conn.cancel()
return
} if let data = data {
DispatchQueue.main.async {
self.messageLabel.text = String(data: data, encoding: .utf8)!
} print("消息已收到:\(String(data: data, encoding: .utf8)!)")
} if isComplete {
self.conn.cancel()
self.conn = nil
}
}
}
}

NWConnection需要一个NWParameters类型的选项,当NWConnection建立连接以及收发数据的时候会使用这些选项调整连接的行为。系统默认一个选项是NWParameters.tls,然后这个选项在tls连接建立时验证服务端证书的时候使用的是iOS系统里预置的要证书,这并不满足我们的需求。

我们必须找到一个地方能定制化双方握手时的证书验证形为。我们可以通过配置NWProtocolTLS.Options.SecurityProtocolOptions添加一个验证回调块来达成这个需求。原型如下:

typedef void (^sec_protocol_verify_t)(sec_protocol_metadata_t metadata, sec_trust_t trust_ref, sec_protocol_verify_complete_t complete);

API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0))
void
sec_protocol_options_set_verify_block(sec_protocol_options_t options, sec_protocol_verify_t verify_block, dispatch_queue_t verify_block_queue);

回调块的参数metadata,可以从中遍历出对端的证书列表。参数trust_ref,可以从中遍历出信任证书列表(对端的证书列表和对端证书链对应的根证书),当然我们自签名根证书不在iOS系统中,系统不会自动为我们添加上,需要我们手动添加。

通过SecCertificateCreateWithData()我们从ca.der生成要证书对象,然后通过SecTrustSetAnchorCeritificates()把它添加信任证书链表中。接着我们通过SecPolicyCreateSSL()生成一个验证策略,其中"myserver"是服务端证书对应的名字,可以查看服务端证书得到,这里也即限制服务端证书的CN必须为myserver,否则验证失败。SecTrustSetPolicies()为信任证书链添加验证策略,SecTrunstSetAnchorCeritificatesOnly()只信任我们自已添加的根证书来验证服务端证书。

SecTrustEvaluateWithError()来最终验证服务端证书,如若有错,通过打印error知道具体的错误原因。验证的成功否是失败都要通过参数complete回调来告知NWConnection以继续后续的握手操作。

连接建立之后就可以自由的收发消息了。

最后项目结构介绍:

源码目录如下:

其中server.go是服务端的代码, learn.zip是客户端的代码 store.zip是生成证书的时一些配置文件。

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

iOS12 Network框架 自签名证书认证的更多相关文章

  1. Https自签名证书认证及数据请求的封装

    在WWDC 2016开发者大会上,苹果宣布了一个最后期限:到2017年1月1日 App Store中的所有应用都必须启用 App Transport Security安全功能.App Transpor ...

  2. Let's Encrypt 免费通配 https 签名证书 安装方法2 ,安卓签名无法认证!

    Let's Encrypt 免费通配 https 签名证书 安装方法 按照上文 配置完毕后你会发现 在pc浏览器中正常访问,在手机浏览器中无法认证 你只需要安装一个或多个中级证书 1.查看Nginx ...

  3. SpringBoot服务间使用自签名证书实现https双向认证

    SpringBoot服务间使用自签名证书实现https双向认证 以服务server-one和server-two之间使用RestTemplate以https调用为例 一.生成密钥 需要生成server ...

  4. HTTPS协议、TLS协议、证书认证过程解析

    一.HTTPS 协议 HTTPS协议其实就是HTTP over TSL,TSL(Transport Layer Security) 传输层安全协议是https协议的核心. TSL可以理解为SSL (S ...

  5. tomcat 配置客户端证书认证

    在完成配置客户端证书认证后,浏览器以https访问服务器的时候,会提示选择证书,之后,服务器端会验证证书.也就意味着只有拥有有效证书的客户端才能打开该网站. 以下是具体的配置过程. 1. 在服务器端生 ...

  6. cmd命令生成android签名证书

    cmd命令生成android签名证书,有空在写一篇eclipse导出带签名的apk,这里面包括生成新的签名.现在还是讲讲在cmd怎么操作生成签名证书. 1.dos下进入JDK的bin目录 运行如下命令 ...

  7. iOS开发HTTPS实现之信任SSL证书和自签名证书

    iOS开发HTTPS实现之信任SSL证书和自签名证书 转自:http://www.jianshu.com/p/6b9c8bd5005a/comments/5539345 (收录一下供自己学习用的) 字 ...

  8. AFNetWorking3.0使用 自签名证书的https请求

    前几日,项目组出于安全角度的考虑,要求项目中的请求使用https请求,因为是企业内部使用的app,因此使用了自签名的证书,而自签名的证书是不受信任的,所以我们就需要自己来做证书的验证,包括服务器验证客 ...

  9. mobileconfig文件的签名和认证(signed、verified)

    专题:基于IOS上MDM技术相关资料整理及汇总mobileconfig文件的签名和认证(signed.verified) 一.功能描述: 鉴于我们的设备和MDM server之间已经可以通信,并能完成 ...

随机推荐

  1. 如何查看Isilon的节点的CPU的信息?

    使用Isilon自带的命令 isi_hw_status 使用如下的命令 dmidecode --type processor 笔者只是想了解一下F800的CPU有多少个core.上面的命令都可以获得这 ...

  2. layer.msg();怎么关闭

    var index = layer.msg(); $.ajax("","",function(){ layer.close(index) })

  3. Nginx限制某个IP同一时间段的访问次数和请求数示例代码

    nginx可以通过ngx_http_limit_conn_module和ngx_http_limit_req_module配置来限制ip在同一时间段的访问次数. ngx_http_limit_conn ...

  4. Graph 卷积神经网络:概述、样例及最新进展

    http://www.52ml.net/20031.html [新智元导读]Graph Convolutional Network(GCN)是直接作用于图的卷积神经网络,GCN 允许对结构化数据进行端 ...

  5. VIM的buffers

    原文:http://ju.outofmemory.cn/entry/13522 重新在不同的 tab 中打开多个关闭的buffer 文件, https://stackoverflow.com/ques ...

  6. linux bash关闭标准输出1(exec 1<&-)后重新打开

    linux bash shell的再次学习. 文件描述符: stdin,stdout 和 stderr 的文件描述符分别是 0,1 和 2(一个文件描述符说白了就是文件系统为了跟踪这个打开的文件而分配 ...

  7. Spark Structured Streaming:将数据落地按照数据字段进行分区方案

    方案一(使用ForeachWriter Sink方式): val query = wordCounts.writeStream.trigger(ProcessingTime(5.seconds)) . ...

  8. 如何把本地代码提交到git(码云)、github代码管理项目上

    前提必须安装了git工具 以下命令操作都是通过git bash操作. 将本地代码提交到码云空项目操作: D:\>mkdir inek_winprop D:\>cd inek_winprop ...

  9. 转:利用Eclipse CDT 阅读C/C++代码

    转自: https://www.oschina.net/question/234345_41419 排版与说明都很不错,推荐一看.

  10. Ant编译utf-8非法字符:/65279 解决方法

    原文链接:http://blog.csdn.net/xiyuan1999/article/details/5989336   Ant编译utf-8非法字符:/65279 解决方法   使用ant编译j ...