6

如何制作和使用自签名证书

 3 years ago
source link: https://soulteary.com/2021/02/06/how-to-make-and-use-a-self-signed-certificate.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在计算机加密和安全领域中,我们会时常遇到:自签名安全证书。

因为自签名证书签发相对于商业证书流程简单,费用低廉(除了电费几乎不花钱),更新容易。所以在开发领域、甚至一些小众场景下特别常见,比如 K8S / MySQL 集群中的 TLS 认证,一些大的集团、公司的内网服务、网站安全证书、企业路由器设备的管理后台、用于管理企业员工的“安全准入客户端”等不乏使用这个方案。

本篇文章就来聊聊如何快速生成证书,以及如何安装部署到不同的环境中。

写在前面

经常有人说,使用自签名证书不安全,会导致中间人攻击。这里需要为自签名证书“正名”,如果你制作生成的证书被妥善保管(即不泄漏并被二次利用),并将其加入你的有限的设备(自用、团队使用)的证书信任列表中,在明确你的设备访问地址(不涉及DNS攻击),你是不会遇到中间人攻击的。

比如当你遇到类似下面的场景,不一定会遇到不安全的事情,有可能只是管理员忘记换掉过期证书、或者你自己生成证书后,使用了一台没有信任证书的设备进行访问、也可能是管理员压根没有想在公网签发证书,想做一个私有的网站:

muE7vyy.png!mobile

多数时候我们看到的不安全的证书是因为应用错误配置、有心人基于 DNS 地址攻击、证书过期造成、甚至是我们未曾正确配置证书信任白名单造成的。

一旦我们正确生成证书,在妥善保存证书后,进行了有限设备的白名单设置后,我们的证书和商业证书的使用是几乎没有差别的(除了无法使用 OCSP、EV 证书使用上存在一定额外工作外)。

UjMFJzM.png!mobile

那么来聊聊如何快速生成证书。

使用命令行脚本生成自签名证书

最常见和通用的做法便是安装配置一个带有 openssl 环境的系统,然后使用命令行执行类似下面这样的命令:

openssl req -x509 -newkey rsa:2048 -keyout ssl/${fileName}.key -out ssl/${fileName}.crt -days 3600 -nodes ...

这里如果你选择不使用配置文件的话,得参考 openssl 文档,附带一堆参数,或需要交互式的输入一堆选项,并祈祷在中间每一步没有输入出错,例如下面这样:

enerating a RSA private key
....................................................................................................................................................................................................................................................................++++
.............................................................++++
writing new private key to 'example.key'
-----
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) [AU]:CN
State or Province Name (full name) [Some-State]:XX
Locality Name (eg, city) []:XXXX
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example, Inc
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:example.com
Email Address []:[email protected]

相比之下,使用类似下面的配置生成证书会稍微容易那么一些:

#!/bin/sh
 
OUTPUT_FILENAME="lab.com"
 
printf "[req]
prompt                  = no
default_bits            = 4096
default_md              = sha256
encrypt_key             = no
string_mask             = utf8only
 
distinguished_name      = cert_distinguished_name
req_extensions          = req_x509v3_extensions
x509_extensions         = req_x509v3_extensions
 
[ cert_distinguished_name ]
C  = CN
ST = BJ
L  = BJ
O  = HomeLab
OU = HomeLab
CN = lab.com
 
[req_x509v3_extensions]
basicConstraints        = critical,CA:true
subjectKeyIdentifier    = hash
keyUsage                = critical,digitalSignature,keyCertSign,cRLSign #,keyEncipherment
extendedKeyUsage        = critical,serverAuth #, clientAuth
subjectAltName          = @alt_names
 
[alt_names]
DNS.1 = lab.com
DNS.2 = *.lab.com
DNS.3 = *.page.lab.com
 
">ssl/${OUTPUT_FILENAME}.conf
 
openssl req -x509 -newkey rsa:2048 -keyout ssl/$OUTPUT_FILENAME.key -out ssl/$OUTPUT_FILENAME.crt -days 3600 -nodes -config ssl/${OUTPUT_FILENAME}.conf

类似的脚本,我曾在 Traefik 示例脚本中提到过: https://github.com/soulteary/traefik-example/blob/main/scripts/generate-certs.sh

还有更简单的方案吗?尤其是在有不断修改 DNS、希望使用脚本自动化签订证书的场景下?

快速生成证书

为此我写了一个脚本,并使用容器进行封装,以达到可以使用极其简的命令行来生成证书的目的,并借助容器简化掉了本地需要安装 openssl 依赖的问题,“开箱即用”。相关代码我已经开源,项目地址: https://github.com/soulteary/certs-maker

比如你想生成一个稍微复杂一些的站点证书,只需要执行下面这行命令就足够了:

docker run --rm -it -e CERT_DNS="domain.com;*.domain.com;*.a.domain.com" -v `pwd`/certs:/ssl soulteary/certs-maker

执行完毕你将会看到类似下面的日志:

User Input: { CERT_DNS: 'domain.com;*.domain.com;*.a.domain.com' }
Generating a RSA private key
................................................................................................................................................+++++
.........................................................+++++
writing new private key to 'ssl/domain.com.key'
-----

以及能够在 ssl 目录中看到我们生成的证书文件。

至于其他的使用方式,比如生成包含多个域名的混合证书、生成单个证书,只需要调整 CERT_DNS 参数的值即可。如果想进一步定制前文提到的证书细节,比如证书签发国家、省份等信息,可以参考开源项目仓库的使用方式,添加其他的参数,这里就不过多赘述了。

使用 docker-compose 生成

如果你希望将命令保存下来,作为代码存储在仓库里,也可以考虑编写一个 compose 文件:

version: '2'
 
services:
 
  certs-maker:
    image: soulteary/certs-maker
    environment:
      - CERT_DNS=a.com;b.com;c.com;*.d.com;
    volumes:
      - ./certs:/ssl

将上面的内容保存为 docker-compose.yml ,然后执行 docker-compose up ,你会在 certs 目录看到生成的证书文件。

使用证书

生成证书之后,来聊聊如何使用证书。

在各种系统上导入证书

导入证书可以参考下面的文档,过程都很简单,引导证书,然后重启需要使用证书的应用即可。

在 Java 应用中信任自签名证书

如果你使用的是 Java 应用访问自签名的网站,应用访问过程会出现因为证书错误而拒绝连接的错误。

解决这个问题并不复杂,只需要额外做一点点工作,将证书添加到 keystore 中,重启 Java 应用即可:

sudo keytool -import -alias charles -file /Path-To-Certs/key.crt -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit

这个操作对于证书过期的情况,也同样有效,早先有一篇文章有描述: 《使用 Docker 和 Traefik v2 搭建 Confluence 7.3 》

在 Debian / Ubuntu / Alpine 系统中信任证书

对于 Debian / Ubuntu 系统,信任证书相当简单,只需要将证书拷贝到“待安装目录”,然后执行证书更新命令即可:

cp *.crt /usr/local/share/ca-certificates/
update-ca-certificates

Alpine 也是一样,考虑我们经常在容器场景中使用它,所以这里直接给出一个完整的Dockerfile 示例:

FROM alpine
 
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
 
ADD ./ssl/*.crt /usr/local/share/ca-certificates/
 
RUN update-ca-certificates --fresh

搭建配合安装证书使用的 Web 服务

上文中如果想在客户端(尤其是手机)上安装证书,一定会遇到跨系统传输文件的问题。为了方便分发和安装,这里可以使用 Nginx 启动一个用于分享证书安装的 Web 服务。

jquqAfu.png!mobile

搭建通用服务

我们可以使用 Nginx 的 ngx_http_sub_module 和 ** ngx_http_autoindex_module** 模块构建一个能够自动列举证书目录的服务:

server {
    listen 80;
    server_name localhost;
    location = /favicon.ico {
        empty_gif;
    }
    location / {
        root /public;
        autoindex on;
        sub_filter '<h1>Index of /</h1>'  '<h1>Get Certs</h1>';
        sub_filter_once on;
    }
}

将上面的内容保存为 default.conf 后,再创建一个名为 docker-compose.yml 的配置文件:

version: '2'
 
services:
 
  nginx:
    image: nginx:1.19.6-alpine
    ports:
      - 8080:80
    volumes: 
      - ./default.conf:/etc/nginx/templates/default.conf.template:ro
      - ./public:/public:ro

然后使用 docker-compose up 启动服务后,使用手机访问页面,就能够看到类似下面的页面,然后使用手机访问证书文件进行安装和信任就可以啦。

更简单的配置方式

上面的模式我们将配置和服务编排文件分拆成了两个文件,考虑到这个 Nginx 配置十分简单,那么我们是否有办法将其简化呢?

答案是有的,通过对 command 命令进行调整,我们可以将 Nginx 配置的创建和服务启动同时写在 docker-compose.yml 编排文件中:

version: '2'
 
services:
 
  nginx:
    image: nginx:1.19.6-alpine
    ports:
      - 8080:80
    volumes: 
      - ./public:/public:ro
    command: >
      /bin/sh -c "
      echo \"daemon off;\" >> /etc/nginx/nginx.conf;
      echo \"server {
            listen 80;
            server_name localhost;
            location = /favicon.ico {
                empty_gif;
            }
            location / {
                root /public;
                autoindex on;
                sub_filter '<h1>Index of /</h1>'  '<h1>Get Certs</h1>';
                sub_filter_once on;
            }
        }\" > /etc/nginx/conf.d/default.conf; nginx;
      "

最后

最近入手了三根雷电数据线,快过年了,或许可以折腾一下雷电数据线组网开发。

–EOF


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK