最近工作上遇到了一些关于tls证书相关的问题,网上教程许多可是大部分都只是说怎么做,而且大部分教程互相也对不上,各种查资料研讨了两三天,写一个相对清晰,且解释具体的文档来帮助大家避坑哈!

前置常识

名词解释

  • ca:Certification Authority,翻译为证书组织,担任给各个网站颁布证书。(每个电脑都会内置13个根证书组织的证书,并无条件信赖这13个证书组织)
  • csr:Certificate Signing Request,即证书签名请求。用户向ca请求证书的时候,需求提交的信息。一般会包括网站域名、用户邮箱、所在国家区域、组织称号等等信息
  • 请求证书,或许签发证书,即ca首先运用特定的摘要算法核算出csr的摘要,再用自己的私钥给摘要进行加密后得到的签名,终究将以上一切信息打包为一个特定格式的文件(也便是证书)
  • cert:Certificate,即证书。一个特定格式的文件,里边包括了csr中部分信息、摘要生成算法、tls拓宽支撑、ca给csr的指纹信息(也翻译为签名)等。
  • key:一般指openssl生成的密钥文件,其间包括了公钥和私钥
  • 验证证书合法:指客户端首先从cert中读取该证书的签发ca(这儿会涉及到证书链的问题,暂时可简单理解为,一切证书都是根证书组织签发),然后运用该ca的公钥去解密证书的签名(非对称加密,私钥加密,公钥解密),获得摘要信息A;然后运用证书中指定的摘要算法核算证书的摘要B;第三步判摘要A是否和摘要B持平
  • tls单向认证:一般指客户端会验证服务端所提供的证书是否合法(拜访https的网页便是用的这个,用的很广泛)
  • tls双向认证:在单项认证基础上,服务端也会验证客户端的证书是否合法(相对罕见,目前只要docker daemon敞开tls后才会运用)

tls单向认证大致流程

一文搞懂自签名tls证书
和证书相关的只要图中的第2、3步。

在一般的https页面拜访中(也便是tls单项认证中)怎么完成自签名证书

自签名证书都需求什么文件

从前置常识中能够知道服务端需求提供证书,客户端需求验证证书。因而服务端需求有证书文件,又因为请求证书需求csr以及公钥、私钥,因而服务端至少需求3个文件也便是cert、csr、key。那么客户端呢?客户端至少需求ca的公钥,其实ca的公钥一般存放在ca的cert中,因而客户端需求ca的cert,也便是一个ca的证书文件。

tls单项认证自签名证书小结

tls单项认证所需求文件如下

ca相关:

  • key.pem (包括ca的私钥,牢记不行露出)
  • cert.pem (ca的证书,需求给到客户端)

服务端相关的:

  • key.pem (包括服务端的私钥,不行外露,和ca的key.pem不是同一个)
  • cert.pem (服务端证书)
  • csr.pem (签名证书的中间文件能够删去)
  • openssl.cnf (签名证书时的额定装备)

客户端相关的:

  • ca.pem(和ca的cert.pem是同一个)

talk is cheap,show me the code

  1. 生成ca相关文件
mkdir ca
#4096 为私钥比特数,一般取2048或4096即可
#生成key文件
openssl genrsa -out "ca/key.pem" 4096
#生成ca的证书文件cert.pem
# subj '/CN=docker:dind CA' 为证书组织的称号,-days "1800 为证书有限期,可适当调整
# -x509为密码系统标准(不太精确)记住即可
openssl req -new -key "ca/key.pem" 
  -out "ca/cert.pem" 
  -subj '/CN=docker:dind CA' -x509 -days "1800"
  1. 生成服务端文件
mkdir server
# 生成key文件
openssl genrsa -out "server/key.pem" 4096
#生成csr文件
# subj '/CN=docker:dind server' 里边填写的是你的组织、组织单位、公用名的信息。
# 至少需求CN这个字端,其他能够选填,其他字端请自行百度
openssl req -new -key "server/key.pem" 
  -out "server/csr.pem" 
  -subj '/CN=docker:dind server'
# 请求证书时额定的装备
# subjectAltName = DNS:ubuntu-linux,IP:10.211.55.5,这儿要把dns后面的改为你的域名,ip后面改你你服务所在的ip(如果只用域名拜访,ip能够不要)
# subjectAltName 也是X509的一个拓宽,指的是san(多域名)类型的证书,其间DNS:ubuntu-linux能够多次重复,下面给出了bing证书的比如,能够看到,一个证书就支撑了十分多的域名。
cat > "server/openssl.cnf" <<-EOF
  [ x509_exts ]
  subjectAltName = DNS:ubuntu-linux,IP:10.211.55.5
EOF
# 模拟ca签发服务端证书
openssl x509 -req 
   -in "server/csr.pem" 
   -CA "ca/cert.pem" 
   -CAkey "ca/key.pem" 
   -CAcreateserial 
   -out "server/cert.pem" 
   -days "1800" 
   -extfile "server/openssl.cnf" 
   -extensions x509_exts
# 检查证书内容
openssl x509 -in server/cert.pem -noout -text
# 验证证书
# 运用ca证书验证你的证书,
openssl verify -CAfile "ca/cert.pem" "server/cert.pem"

一文搞懂自签名tls证书

  1. nginx演示
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    server {
        listen 443 ssl;
        server_name ubuntu-linux
        # SSL 协议版别
        ssl_protocols TLSv1.2;
        # 服务端证书目录
        ssl_certificate /root/ca/server/cert.pem;
        # 服务端私钥目录
        ssl_certificate_key /root/ca/server/key.pem;
        # ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
        ssl_ciphers AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256;
        # 与False Start不要紧,默认此项敞开,此处削减抓包的搅扰而封闭
        ssl_session_tickets off;
        default_type text/html;
        return 200 "https ok n";
    }
    include /etc/nginx/conf.d/*.conf;
}

运用docker发动进行演示

docker run --rm -v $PWD/nginx.conf:/etc/nginx/nginx.conf -p 9999:443 -v /root/ca:/root/ca nginx

此刻拜访浏览器会报如下过错:

一文搞懂自签名tls证书
呈现的原因是我们的ca不是权威的ca,所以浏览器不信赖这个ca 4. 给客户端添加额定的ca证书,将方才生成的ca的cert.pem拷贝到客户端电脑上,每个人指令不一样,我的指令为scp ubuntu-linux:/root/ca/ca/cert.pem .

mac上翻开钥匙串拜访,操作如下(windows请自行百度):

一文搞懂自签名tls证书

导入后,双击证书,并信赖

一文搞懂自签名tls证书

此刻从头刷新浏览器页面发现现已没有报错了,检查证书的信息也都正常,可检查一下颁布给、颁布者的信息,和前面填写的信息都能对得上

一文搞懂自签名tls证书

选看内容

创立双向认证证书

所谓双向认证便是服务端也会向客户端索要证书,因而只需求在单项认证的基础上,再生成一套客户端证书即可

生成客户端证书

mkdir client
# 生成key文件
openssl genrsa -out "client/key.pem" 4096
#生成csr文件
openssl req -new -key "client/key.pem" 
  -out "client/csr.pem" 
  -subj '/CN=docker:dind client'
# 请求证书时额定的装备
# client和server这儿不一样
# extendedKeyUsage = clientAuth 这是x509的一个扩展用来支撑客户端认证的
cat > "client/openssl.cnf" <<-'EOF'
  [ x509_exts ]
  extendedKeyUsage = clientAuth
EOF
# 模拟ca签发服务端证书
openssl x509 -req 
   -in "client/csr.pem" 
   -CA "ca/cert.pem" 
   -CAkey "ca/key.pem" 
   -CAcreateserial 
   -out "client/cert.pem" 
   -days "1800" 
   -extfile "client/openssl.cnf" 
   -extensions x509_exts
# 检查证书内容
openssl x509 -in client/cert.pem -noout -text
# 验证证书
# 运用ca证书验证你的证书,
openssl verify -CAfile "ca/cert.pem" "client/cert.pem"

终究目录结构如下:

一文搞懂自签名tls证书

nginx装备并验证

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    server {
        listen 443 ssl;
        server_name ubuntu-linux
        # SSL 协议版别
        ssl_protocols TLSv1.2;
        # 服务端证书目录
        ssl_certificate /root/ca/server/cert.pem;
        # 服务端私钥目录
        ssl_certificate_key /root/ca/server/key.pem;
        # ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
        ssl_ciphers AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256;
        #客户端ca的证书
        ssl_client_certificate /root/ca/ca/cert.pem;
        ssl_verify_client on; #敞开客户端证书验证
        # 与False Start不要紧,默认此项敞开,此处削减抓包的搅扰而封闭
        ssl_session_tickets off;
        default_type text/html;
        return 200 "https ok n";
    }
    include /etc/nginx/conf.d/*.conf;
}

发动服务端nginx docker run --rm -v $PWD/nginx.conf:/etc/nginx/nginx.conf -p 9999:443 -v /root/ca:/root/ca nginx

客户端运用curl验证

–cert client/cert.pem –key client/key.pem指定了客户端的证书和key文文件,-k参数不校验服务端的证书(因为自签名的证书,curl也不认)

curl --cert client/cert.pem --key client/key.pem https://ubuntu-linux:9999 -v -k 验证成果如图,能够看到返回了200,而且客户端也发送了证书给服务端TLSv1.3 (OUT), TLS handshake, Certificate (11):

一文搞懂自签名tls证书

docker daemon敞开tls拜访

docker daemon tls形式默认便是运用tls双向认证来进行安全通讯的,只需求按双向认证步骤生成相应的证书文件即可,下面是docker官方生成双向证书的脚本。具体怎么装备,可自行百度,这儿不再赘述

#!/bin/sh
set -eu
_tls_ensure_private() {
  local f="$1"; shift
  [ -s "$f" ] || openssl genrsa -out "$f" 4096
}
_tls_san() {
  {
   ip -oneline address | awk '{ gsub(//.+$/, "", $4); print "IP:" $4 }'
   {
    cat /etc/hostname
    echo 'docker'
    echo 'localhost'
    echo 'ubuntu-linux'
    hostname -f
    hostname -s
   } | sed 's/^/DNS:/'
   [ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
  } | sort -u | xargs printf '%s,' | sed "s/,$//"
}
_tls_generate_certs() {
  local dir="$1"; shift
  # if server/{ca,key,cert}.pem && !ca/key.pem, do NOTHING except verify (user likely managing CA themselves)
  # if ca/key.pem || !ca/cert.pem, generate CA public if necessary
  # if ca/key.pem, generate server public
  # if ca/key.pem, generate client public
  # (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
  if [ -s "$dir/server/ca.pem" ] && [ -s "$dir/server/cert.pem" ] && [ -s "$dir/server/key.pem" ] && [ ! -s "$dir/ca/key.pem" ]; then
   openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
   return 0
  fi
  # https://github.com/FiloSottile/mkcert/issues/174
  local certValidDays='825'
  if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
   # if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
   mkdir -p "$dir/ca"
   _tls_ensure_private "$dir/ca/key.pem"
   openssl req -new -key "$dir/ca/key.pem" 
    -out "$dir/ca/cert.pem" 
    -subj '/CN=docker:dind CA' -x509 -days "$certValidDays"
  fi
  if [ -s "$dir/ca/key.pem" ]; then
   # if we have a CA private key, we should create/manage a server key
   mkdir -p "$dir/server"
   _tls_ensure_private "$dir/server/key.pem"
   openssl req -new -key "$dir/server/key.pem" 
    -out "$dir/server/csr.pem" 
    -subj '/CN=docker:dind server'
   cat > "$dir/server/openssl.cnf" <<-EOF
    [ x509_exts ]
    subjectAltName = $(_tls_san)
   EOF
   openssl x509 -req 
     -in "$dir/server/csr.pem" 
     -CA "$dir/ca/cert.pem" 
     -CAkey "$dir/ca/key.pem" 
     -CAcreateserial 
     -out "$dir/server/cert.pem" 
     -days "$certValidDays" 
     -extfile "$dir/server/openssl.cnf" 
     -extensions x509_exts
   cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
   openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
  fi
  if [ -s "$dir/ca/key.pem" ]; then
   # if we have a CA private key, we should create/manage a client key
   mkdir -p "$dir/client"
   _tls_ensure_private "$dir/client/key.pem"
   chmod 0644 "$dir/client/key.pem" # openssl defaults to 0600 for the private key, but this one needs to be shared with arbitrary client contexts
   openssl req -new 
     -key "$dir/client/key.pem" 
     -out "$dir/client/csr.pem" 
     -subj '/CN=docker:dind client'
   cat > "$dir/client/openssl.cnf" <<-'EOF'
    [ x509_exts ]
    extendedKeyUsage = clientAuth
   EOF
   openssl x509 -req 
     -in "$dir/client/csr.pem" 
     -CA "$dir/ca/cert.pem" 
     -CAkey "$dir/ca/key.pem" 
     -CAcreateserial 
     -out "$dir/client/cert.pem" 
     -days "$certValidDays" 
     -extfile "$dir/client/openssl.cnf" 
     -extensions x509_exts
   cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
   openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
  fi
}
_tls_generate_certs "$(pwd)"

总结

自签名tls证书总共分为三步

  1. 生成ca相关文件:
  • key.pem (包括ca的私钥,牢记不行露出)
  • cert.pem (ca的证书,需求给到客户端)
  1. 生成服务端相关文件:
  • key.pem (包括服务端的私钥,不行外露,和ca的key.pem不是同一个)
  • cert.pem (服务端证书)
  • csr.pem (签名证书的中间文件能够删去)
  • openssl.cnf (签名证书时的额定装备)
  1. 装备客户端使其信赖自签名的ca证书