Golang 服务器端对客户端的证书进行校验(双向证书校验)

来源:互联网 发布:三洋微波炉怎么样知乎 编辑:程序博客网 时间:2024/05/20 14:22

    服务器端对客户端的证书进行校验(双向证书校验),客户端访问服务器端,必须校验,如果跳过验证,客户端无法成功地建立与服务器之间的连接,也就是您在浏览器地址访问服务器api接口时,会同样象单向校验那样出现“Your connection is not secure”,即使您此时在浏览器页面上选择添加安全异常,也无法建立与服务器连接。

[root@contoso ~]# echo "192.168.10.100   zigoo.com" >> /etc/hosts    ##配置IP到域名的映射,如果存在映射关系,跳过此条指令
[root@contoso ~]# more /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.10.100   zigoo.com
[root@contoso ~]#

[root@contoso ~]# tree $GOPATH/src/contoso.org -L 3  ##查看项目目录结构

/root/code/go/src/contoso.org
├── client
│   └── client.go
└── server
    └── server.go

2 directories, 2 files
[root@contoso ~]#

服务端可以要求对客户端的证书进行校验,以更严格识别客户端的身份,限制客户端的访问。
要对客户端数字证书进行校验,首先客户端需要先有自己的证书。接下来我们来建立我们自己的CA,需要生成一个CA私钥和一个CA的数字证书。

[root@contoso ~]# cd $GOPATH/src/contoso.org/client
[root@contoso client]# openssl genrsa -out ca.key 2048    ## 1). 生成一个CA私钥
Generating RSA private key, 2048 bit long modulus
..........+++
...............................+++
e is 65537 (0x10001)
[root@contoso client]# openssl req -x509 -new -nodes -key ca.key -days 365 -out ca.crt   ## 2).使用ca私钥生成客户端的数字证书

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:GuangDong
Locality Name (eg, city) [Default City]:ShenZhen
Organization Name (eg, company) [Default Company Ltd]:ZiGoo
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:zigoo.com
Email Address []:24759362@qq.com
[root@contoso client]#
客户端:
私钥文件   ca.key
数字证书   ca.crt

[root@contoso client]#tree $GOPATH/src/contoso.org -L 3  ##查看项目目录结构
/root/code/go/src/contoso.org
├── client
│   ├── ca.crt
│   ├── ca.key
│   └── client.go
└── server
    └── server.go

2 directories, 4 files
[root@contoso client]#

[root@contoso client]#openssl genrsa -out client.key 2048    ## 3).生成客户端的私钥
Generating RSA private key, 2048 bit long modulus
..............................+++
............+++
e is 65537 (0x10001)
[root@contoso client]# openssl req -new -key client.key -out client.csr    ## 4).使用客户端私钥生成数字证书请求
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:GuangDong
Locality Name (eg, city) [Default City]:ShenZhen
Organization Name (eg, company) [Default Company Ltd]:ZiGoo
Organizational Unit Name (eg, section) []:zigoo.com
Common Name (eg, your name or your server's hostname) []:zigoo.com            
Email Address []:24759362@qq.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:12345678
An optional company name []:    ## 直接按回车键跳过
[root@contoso client]# echo "extendedKeyUsage=clientAuth" >> client.ext    ## 5). 创建客户端扩展配置信息
[root@contoso client]# openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile client.ext -out client.crt -days 365   ## 6). 使用客户端ca私钥和客户端扩展配置信息签发客户端的数字证书
Signature ok
subject=/C=CN/ST=GuangDong/L=ShenZhen/O=ZiGoo/OU=zigoo.com/CN=zigoo.com/emailAddress=24759362@qq.com
Getting CA Private Key
[root@contoso client]# tree $GOPATH/src/contoso.org -L 3    ##查看项目目录结构
/root/code/go/src/contoso.org
├── client
│   ├── ca.crt
│   ├── ca.key
│   ├── ca.srl
│   ├── client.crt
│   ├── client.csr
│   ├── client.ext
│   ├── client.go
│   └── client.key
└── server
    └── server.go

2 directories, 9 files
[root@contoso client]#

重要提示:
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365  // 因为没有ExtKeyUsage信息,无法使用client.crt
证书中的ExtKeyUsage信息应该包含clientAuth,创建文件client.ext
内容:

extendedKeyUsage=clientAuth

[root@contoso client]#cp ca.crt ca.key $GOPATH/src/contoso.org/server && cd $GOPATH/src/contoso.org/server
[root@contoso server]# openssl genrsa -out server.key 2048   ## 7). 生成服务器端的私钥
Generating RSA private key, 2048 bit long modulus
................+++
.........+++
e is 65537 (0x10001)
[root@contoso server]# openssl req -new -key server.key -out server.csr   ## 8). 使用服务器端的私钥生成一个数字证书请求
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:GuangDong
Locality Name (eg, city) [Default City]:ShenZhen
Organization Name (eg, company) [Default Company Ltd]:ZiGoo
Organizational Unit Name (eg, section) []:zigoo.com
Common Name (eg, your name or your server's hostname) []:zigoo.com
Email Address []:24759362@qq.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:12345678
An optional company name []:
[root@contoso server]# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365     ##使用客户端ca私钥签发服务器端的数字证书
Signature ok
subject=/C=CN/ST=GuangDong/L=ShenZhen/O=ZiGoo/OU=zigoo.com/CN=zigoo.com/emailAddress=24759362@qq.com
Getting CA Private Key
[root@contoso server]#
[root@contoso server]# tree $GOPATH/src/contoso.org -L 3    ##查看项目目录结构
/root/code/go/src/contoso.org
├── client
│   ├── ca.crt
│   ├── ca.key
│   ├── ca.srl
│   ├── client.crt
│   ├── client.csr
│   ├── client.ext
│   ├── client.go
│   └── client.key
└── server
    ├── ca.crt
    ├── ca.key
    ├── ca.srl
    ├── server.crt
    ├── server.csr
    ├── server.go
    └── server.key

2 directories, 15 files
[root@contoso server]#

服务器端:
私钥文件   server.key
数字证书   server.crt



## 注意 X509v3 Extended Key Usage, X509v3 extensions 和 TLS Web Client Authentication
## 这说明client.crt的Extended Key Usage已经添加成功

[root@contoso server]#cd $GOPATH/src/contoso.org/client
[root@contoso client]# openssl x509 -text -in client.crt -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 13079588882123393112 (0xb58408ca271d0858)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=CN, ST=GuangDong, L=ShenZhen, O=ZiGoo, CN=zigoo.com/emailAddress=24759362@qq.com
        Validity
            Not Before: Jun 25 11:04:44 2017 GMT
            Not After : Jun 25 11:04:44 2018 GMT
        Subject: C=CN, ST=GuangDong, L=ShenZhen, O=ZiGoo, OU=zigoo.com, CN=zigoo.com/emailAddress=24759362@qq.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:e4:40:41:bc:fa:31:e1:0a:ce:5f:d1:38:ab:88:
                    e9:24:d6:63:9f:e5:6e:5f:79:e9:31:c8:0d:8c:5f:
                    6d:22:c5:a5:71:92:f1:f8:67:d9:59:49:81:85:69:
                    24:83:c4:69:ea:f8:40:d3:c8:8e:ca:a8:ea:4a:af:
                    95:fa:8a:8d:20:d7:a4:11:78:bd:ae:cc:da:d0:bf:
                    c3:a6:e5:2a:a4:f3:99:8b:b9:37:e9:39:63:58:b1:
                    29:37:e4:c1:77:45:29:76:7a:b1:79:1c:d9:c6:07:
                    bc:e0:f9:25:55:f4:65:56:49:0f:d1:00:a2:c0:c9:
                    ae:b0:c2:02:c1:56:cb:a1:2c:e9:8b:5d:29:4d:74:
                    aa:e5:62:80:e0:b2:12:ba:9d:e5:ec:be:26:e9:b1:
                    15:72:c3:7f:93:39:62:51:5b:f3:14:90:9b:2e:64:
                    4c:3f:30:4a:dc:90:6b:1a:d8:50:a8:70:52:8c:d1:
                    f3:a7:e3:13:4d:29:29:d4:b3:5a:84:07:d6:8f:9f:
                    38:e6:1a:41:df:68:76:e8:06:61:26:fa:c5:25:be:
                    4f:9a:27:19:14:ad:2c:77:fb:24:c4:a3:46:a8:b0:
                    08:14:98:9a:29:44:42:a8:73:20:80:55:1a:9a:ec:
                    51:79:14:99:a5:eb:bb:77:81:f1:74:d6:fa:45:d7:
                    fc:f9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Extended Key Usage:
                TLS Web Client Authentication

    Signature Algorithm: sha1WithRSAEncryption
         69:ba:09:84:82:2b:2b:69:6a:cd:00:4d:1a:5c:08:e5:55:39:
         da:6a:72:c9:ba:70:a8:15:63:2b:f3:34:e5:a7:06:4a:5d:79:
         28:84:68:9d:5c:9d:d7:88:b6:9d:48:7c:e9:47:39:4e:a5:69:
         dc:ce:ff:58:52:69:ee:af:57:d9:10:c9:1b:34:d1:22:e0:99:
         36:e6:32:3f:fa:fa:6d:01:13:1c:07:00:68:51:33:43:f7:a4:
         d5:7a:dc:86:5b:4a:b9:87:7d:a5:c9:cd:1b:4f:19:4a:5c:cd:
         54:8a:2e:9c:83:d0:c6:fc:f6:ff:e9:d5:2d:db:2b:8d:b7:95:
         b0:5d:ae:7e:cb:15:77:81:b4:f8:0c:81:84:b9:bb:45:fe:ae:
         27:c0:42:a9:4d:ac:c2:a3:17:0a:08:88:4f:02:68:4b:de:19:
         c1:28:8a:96:30:b6:6a:b5:5e:a0:ed:44:27:1b:4a:45:8f:ff:
         d7:d5:27:1c:d9:1e:e8:7a:59:2b:9f:9e:8f:52:73:72:7c:9e:
         75:08:71:96:40:15:dc:7a:19:3c:45:9a:35:48:81:07:a8:dd:
         d8:b7:f8:7f:4e:e0:59:07:a3:d2:11:a4:6c:94:d3:db:d0:ff:
         79:85:9a:c0:a3:31:06:3c:95:85:dd:34:e1:19:d4:f0:b6:34:
         2f:82:08:99
[root@contoso client]#


使用Go创建一个HTTPS Web Server

/root/code/go/src/contoso.org/server/server.go :

package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "net/http"
)

type MyMux struct {
}

func (p *MyMux) ServeHTTP(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(res, "Hi, This is an example of https service in golang!\n")

    fmt.Fprintf(res,
        `[{"Name":"jason","Age":35,"Weight":60.3,"Speciality":"computer science","Hobby":["tennis","swimming","reading"],"Score":725.5,"Secret":"SRRMb3ZlFFlvdSE="}]`)
}

func main() {
    pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)

    s := &http.Server{
        Addr:    ":8081",
        Handler: &MyMux{},
        TLSConfig: &tls.Config{
            ClientCAs:  pool,
            ClientAuth: tls.RequireAndVerifyClientCert,
        },
    }

    err = s.ListenAndServeTLS("server.crt", "server.key")
    if err != nil {
        fmt.Println("ListenAndServeTLS err:", err)
    }
}

使用Go创建一个HTTPS Web Client

/root/code/go/src/contoso.org/client/client.go :

package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)

    cliCrt, err := tls.LoadX509KeyPair("client.crt", "client.key")
    if err != nil {
        fmt.Println("Loadx509keypair err:", err)
        return
    }

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs:      pool,
            Certificates: []tls.Certificate{cliCrt},
        },
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://zigoo.com:8081")
    if err != nil {
        fmt.Println("Get error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}


[root@contoso ~]#cd $GOPATH/src/contoso.org/server  ##服务器端路径
[root@contoso server]# go run server.go    ##临时性非全局执行程序,注意,要先启动服务器端


[root@contoso ~]# cd $GOPATH/src/contoso.org/client  ##客户端路径
[root@contoso client]# go run client.go    ##临时性非全局执行程序,注意,要先启动服务器端
Hi, This is an example of https service in golang!
[{"Name":"jason","Age":35,"Weight":60.3,"Speciality":"computer science","Hobby":["tennis","swimming","reading"],"Score":725.5,"Secret":"SRRMb3ZlFFlvdSE="}]



[root@contoso ~]# cd $GOPATH/src/contoso.org/client &&
for i in `seq 1 100000`
do
echo $i " : " && go run client.go
done

以上指令可以多打开几个终端窗口,可以看看多个客户端连续请求HTTPS Web Server返回数据的效果

[root@contoso ~]#ulimit -n  ##默认1024,客户端连接太多会报错:http: Accept error: accept tcp [::]:8081: accept4: too many open files; retrying in 1s
[root@contoso ~]#echo "*  -    nofile  409600" >> /etc/security/limits.conf  ##修改文件描述符的大小,调整服务器的最大并发处理能力
[root@contoso ~]#echo "ulimit -n 409600"  >> /etc/profile && source /etc/profile    ##立即永久生效
[root@contoso ~]#more /etc/security/limits.conf

[root@contoso ~]#more /etc/profile

[root@contoso ~]# ulimit -a     ##查看当前所有系统限制
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63359
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63359
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@contoso ~]# ulimit -n     ##单独查看文件描述符
1024
[root@contoso ~]# cat /proc/sys/fs/file-max    ##查看系统最大文件描述符
1607166
[root@contoso ~]# cat /proc/sys/fs/file-nr     ##查看系统当前使用的描述符
7520    0       1607166

HTTPS Web Server 收到如下信息:
http: TLS handshake error from 192.168.10.100:50092: EOF
表示客户端强制退出与服务器端的连接

HTTPS Web Client 收到如下信息:

Get error: Get https://zigoo.com:8081: dial tcp 192.168.10.100:8081: getsockopt: connection refused

表示服务器端已关闭



[root@contoso ~]# cat /etc/profile
# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}


if [ -x /usr/bin/id ]; then
    if [ -z "$EUID" ]; then
        # ksh workaround
        EUID=`/usr/bin/id -u`
        UID=`/usr/bin/id -ru`
    fi
    USER="`/usr/bin/id -un`"
    LOGNAME=$USER
    MAIL="/var/spool/mail/$USER"
fi

# Path manipulation
if [ "$EUID" = "0" ]; then
    pathmunge /usr/sbin
    pathmunge /usr/local/sbin
else
    pathmunge /usr/local/sbin after
    pathmunge /usr/sbin after
fi

HOSTNAME=`/usr/bin/hostname 2>/dev/null`
HISTSIZE=1000
if [ "$HISTCONTROL" = "ignorespace" ] ; then
    export HISTCONTROL=ignoreboth
else
    export HISTCONTROL=ignoredups
fi

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file
if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
    umask 002
else
    umask 022
fi

for i in /etc/profile.d/*.sh ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null
        fi
    fi
done

unset i
unset -f pathmunge

export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin

export GOPATH=~/code/go
export PATH=$PATH:$GOPATH/bin

unset MAILCHECK
ulimit -n 409600

[root@contoso ~]#


备注:以上红字标识前4行与Visual Studio Code开发配置环境相关

阅读全文
0 0
原创粉丝点击