使用ECC算法自签Ca证书

Photo by Miha Arh on Unsplash

使用ECC算法自签Ca证书

ECC 被认为是 RSA 的继任者,新一代的非对称加密算法,加密速度快,效率更高,对服务器资源消耗低,尤其在区块链中广泛使用。这里介绍一下如何使用 ECC 算法生成 HTTPS 证书。

概览

前一篇博客 CA & OpenSSL自签名证书 讲了如何使用 OpenSSL 生成自签名证书,用的是 RSA 算法,RSA 算法是比较常见的算法,应用较早,较为普及,兼容性较好,但是 RSA 算法密钥长度较长,性能消耗较高。后来于 1985 年提出 椭圆曲线密码学,简称 ECC,并于 2004 年开始被广泛应用。 ECC 被认为是 RSA 的继任者,新一代的非对称加密算法,尤其在区块链中广泛使用。

关于 RSA 和 ECC 算法的原理这里不做介绍,我们只需要知道这是两种非对称加密算法就可以了,而 ECC 加密速度快,效率更高,对服务器资源消耗低,而且重要的是更安全,抗攻击型更强。所以这里介绍一下如何使用 ECC 替换 RSA 生成 HTTPS 证书。

想详细了解 ECC 椭圆算法的可以看文字后面的参考[1][2]

OpenSSL ecparam 命令

OpenSSL 使用 ecparam 命令来进行 ECC 加密,我们看一下支持的参数:

  • -inform PEM|DER: 输入文件格式,DER或者PEM格式。DER格式采用ASN1的DER标准格式。一般用的多的都是PEM格式,就是base64编码格式。
  • -outform DER|PEM: 输出文件格式,DER或者PEM格式。
  • -in filename: 输入的椭圆曲线密钥文件,默认为标准输入。
  • -out filename: 椭圆曲线密钥输出文件,默认为标准输出。
  • -noout: 不打印参数编码的版本信息。
  • -text:打印椭圆曲线密钥参数信息值。
  • -C:以C语言风格打印信息。
  • -check:检查椭圆曲线密钥参数。
  • -name arg:采用短名字。
  • -list_curve:打印所有可用的短名字。
  • -param_enc:指定参数编码方法,可以是 named_curveexplicit,默认为named_curve
  • -conv_form arg:指定信息存放方式,可以是 compresseduncompressed 或者hybrid,默认为compressed
  • -no_seed:如果 -param_enc 指定编码方式为 explicit,不采用随机数种子。
  • -rand file(s):随机数产生种子。
  • -genkey:生成椭圆曲线密钥参数。
  • -engine id:指定硬件引擎。

注意:

PEM格式的EC参数用的头部和底部为:

-----BEGIN EC PARAMETERS-----
...
-----END EC PARAMETERS-----

示例

  • 查看内置的椭圆曲线:
openssl ecparam -list_curves

我们可以用内置的椭圆曲线创创建 EC 参数。

  • 用曲线 prime192v1 产生 EC 参数:
openssl ecparam -name prime192v1 -out ec_param.pem
  • 用明确的参数来产生 EC 参数:
openssl ecparam -name prime192v1 -param_enc explicit -out ec_param.pem
  • 验证给出的 EC 参数:
openssl ecparam -in ec_param.pem -check
  • 创建 EC 参数和私钥文件:
openssl ecparam -genkey -name prime192v1 -out ec_key.pem
  • 改变编码为 compressed
openssl ecparam -in ec_param.pem -conv_form compressed -out ec_out.pem
  • 查看 EC 私钥:
openssl ecparam -in ec_param.pem -noout -text

使用 ECC 自建CA

使用 ECC 和使用签名使用 RAS 的基本一致,不过是把生成私钥的命令从 genrsa 换成 ecparam 了而已,完整命令如下:

#!/bin/bash
set -ex

mkdir root

# 创建 Root Key
openssl ecparam -out root/ca.key.pem -name prime256v1 -genkey

curl -o ./root/openssl.cnf https://gist.githubusercontent.com/foreverzmy/0b07d3a731d70519e35ced070118e3fe/raw/71210c2643b8997ea6e2cf333e26aa33e712a7e2/simple-root-ca.cnf

# 创建 Root Cert
openssl req -config root/openssl.cnf \
      -key root/ca.key.pem \
      -passin pass:123456 \
      -new \
      -x509 \
      -days 7300 \
      -sha256 \
      -extensions v3_ca \
      -out root/ca.cert.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=JediRootCA/emailAddress=921255465@qq.com

chmod 400 root/ca.cert.pem

# 验证证书
# openssl x509 -noout -text -in root/ca.cert.pem

# 创建 Intermediate Pair

# 创建 intermediate 文件夹存放 Intermediate Pair
mkdir intermediate

# 创建 Intermediate Key
openssl ecparam -out intermediate/intermediate.key.pem -name prime256v1 -genkey

chmod 400 intermediate/intermediate.key.pem

curl -o ./intermediate/openssl.cnf https://gist.githubusercontent.com/foreverzmy/936b1bb55adf52c5d1accc9b2216a0e3/raw/28b9f97df967d93febf09219863190aea3b36895/simple-intermediate-ca-cnf

# 创建 Intermediate Cert
openssl req -config intermediate/openssl.cnf -new -sha256 \
      -key intermediate/intermediate.key.pem \
      -passin pass:123456 \
      -out intermediate/intermediate.csr.pem \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=JediIntermediateCA/emailAddress=921255465@qq.com

chmod 400 intermediate/intermediate.key.pem

touch root/index.txt
echo 1000 > root/serial
# 使用 v3_intermediate_ca 扩展签名,密码为 123456
# Intermediate Pair 的有效时间一定要为 Root Pair 的子集
openssl ca -config root/openssl.cnf \
      -extensions v3_intermediate_ca \
      -days 3650 \
      -notext \
      -md sha256 \
      -passin pass:123456 \
      -in intermediate/intermediate.csr.pem \
      -out intermediate/intermediate.cert.pem

chmod 444 intermediate/intermediate.cert.pem

# 验证 Intermediate Pair
# openssl x509 -noout -text -in intermediate/intermediate.cert.pem
# openssl verify -CAfile root/ca.cert.pem intermediate/intermediate.cert.pem

# 浏览器在验证中级证书的时候,同时也会去验证它的上一级证书是否靠谱
# 创建证书链,将 Root Cert 和 Intermediate Cert 合并到一起,可以让浏览器一并验证
cat intermediate/intermediate.cert.pem root/ca.cert.pem > intermediate/ca-chain.cert.pem
chmod 444 intermediate/ca-chain.cert.pem

# 创建服务器/客户端证书

# 创建 foreverz.cn 存放域名相关证书文件
mkdir foreverz.cn

# 生成域名私钥
openssl ecparam -out foreverz.cn/foreverz.cn.key.pem -name prime256v1 -genkey

# 创建网站证书请求
openssl req -config intermediate/openssl.cnf \
      -key foreverz.cn/foreverz.cn.key.pem \
      -new -sha256 \
      -out foreverz.cn/foreverz.cn.req \
      -subj /C=CN/ST=Shanghai/L=Shanghai/O=JediLtd/OU=JediProxy/CN=foreverz.cn/emailAddress=921255465@qq.com

# 签发网站证书
openssl x509 -req -in foreverz.cn/foreverz.cn.req \
      -days 375 \
      -sha256 \
      -CA intermediate/intermediate.cert.pem \
      -CAkey intermediate/intermediate.key.pem \
      -CAcreateserial \
      -out foreverz.cn/foreverz.cn.cert.pem

# 验证证书
# openssl x509 -noout -text -in foreverz.cn/foreverz.cn.cert.pem
openssl verify -CAfile intermediate/ca-chain.cert.pem foreverz.cn/foreverz.cn.cert.pem

这样就生成了一系列证书,下面是我们需要的文件:

  • ca.key.pem: 根CA私钥文件,签发中级CA证书时需要。
  • ca.cert.pem: 根CA证书,可以安装到电脑上,用来认证中级CA。
  • intermediate.key.pem:中级CA私钥,签到域名证书是需要。
  • intermediate.key.pem:中级CA证书,可以安装到电脑上,用来认证域名证书。
  • ca-chain.cert.pem: CA证书链,包含根证书和中级证书,可以一次性安装两个证书。
  • foreverz.cn.key.pem: 网站私钥,启用 HTTPS 服务时需要
  • foreverz.cn.cert.pem: 网站公钥,启用 HTTPS 服务时需要

测试

下面我们启动一个 HTTPS 服务测试一下证书。由于我比较喜欢 go 语言,所以使用 go 语言的 fasthttp 框架来测试。

foreverz.cn 目录创建 main.go 内输入如下内容,并使用 go mod initgo mod tidy 安装依赖:

package main

import (
    "fmt"

    "github.com/valyala/fasthttp"
)

func main() {
    fasthttp.ListenAndServeTLS(":6789", "foreverz.cn.cert.pem", "foreverz.cn.key.pem", requestHandler)
}

func requestHandler(ctx *fasthttp.RequestCtx) {
    fmt.Printf("%s %s %s %s\n", ctx.URI().Scheme(), ctx.Method(), ctx.Host(), ctx.Path())
    ctx.SetBodyString(fmt.Sprintf("hello from %s!", ctx.URI().Scheme()))
}

运行 go run main.go 启动服务。

使用 cURL 测试服务:

$ curl https://foreverz.cn:6789/123 --resolve "foreverz.cn:6789:127.0.0.1"

如果此时没有安装根证书到系统,会报错,证书不合法:

curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

可以使用如下命令安装证书到系统(MacOS):

$ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./intermediate/ca-chain.cert.pem

此时再运行 cURL 命令,控制台显示 hello from https! 说明成功了。

PS:可以只安装中级CA证书,也可以安装证书链,只安装根CA证书不可以,会认证失败!

为了安全,在使用完成后尽量删除证书避免安全问题:

$ sudo security remove-trusted-cert -d intermediate/intermediate.cert.pem

完结,撒花🎉🎉🎉

参考

  1. 椭圆曲线密码学在OpenSSL中的实现
  2. 写给开发人员的实用密码学(七)—— 非对称密钥加密算法 RSA/ECC
  3. OpenSSL命令---ecparam
  4. Openssl ecparam命令
  5. Adding trusted root certificates to the server

Did you find this article valuable?

Support MervynZhang by becoming a sponsor. Any amount is appreciated!