一、非对称加密

1.1 非对称加密

指加密和解密使用不同密钥的加密算法,也称为公私钥加密。

1.2 非对称加密中的证书签名与验证


以上图的虚线为准,左边为签名的实现,右边为签名的验证。

  • 对 Data 部分进行 Hash,结果一般称为 Digest
  • 通过 CA 的私钥,也就是 ca.key ,对 Digest 进行加密
  • 将加密后的结果和签名内容放在一起,变成了一个完整的证书

签名的验证

  • 客户端使用相同的哈希算法,这里使用的是 SHA-256,对证书内容进行 哈希
  • 客户端使用公钥对签名部分进行解密,结果就是上图中红色值为 2 的部分
  • 根据非对称加密算法的特性,红色值 2 应该等于 红色值1
  • 又根据哈希算法的特性,对同样的值进行哈希,结果相同,则有 红色值1Hash Value H1
  • 红色值2 == 红色值1 == Hash Value H1 => 红色值2 == Hash Value H1 证书验证通过

二、创建 CA 证书

2.1 mkcert 生成证书

mkcert --install

2.2 openssl 生成 CA 证书

# 生成一个 CA 密钥
openssl genpkey -algorithm RSA -out ca.key -aes256

# 生成 ca 证书
openssl req -new -x509 -key ca.key -out ca.crt
# 生成带有过期时间的 ca 证书
openssl req -new -x509 -key ca.key -out ca.crt -days 3650

# 证书验证
openssl x509 -noout -text -in ca.crt

2.3 cfssl 生成 CA 证书

{
  "CN": "My CA",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "San Francisco",
      "O": "My Company",
      "OU": "IT Department",
      "ST": "California"
    }
  ],
  "ca": {
    "expiry": "87600h"
  }
}

保存为 ca-csr.json,然后使用下面命令生成 ca 证书

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

2.4 CA 证书中的信息

Certificate:
    Data:
		# X.509 证书版本
        Version: 3 (0x2)
		# 证书的序列号,由颁发机构分配
        Serial Number:
            b7:58:ea:fc:21:0b:0b:1f:8d:af:7d:b1:ee:ed:f5:1c
		# 证书签名算法
        Signature Algorithm: sha256WithRSAEncryption
		# 颁发机构的信息
        Issuer: O = mkcert development CA, OU = root@pve, CN = mkcert root@pve
        # 证书的有效期限,包括起始时间和结束时间
		Validity
            Not Before: Jul  8 12:32:11 2023 GMT
            Not After : Jul  8 12:32:11 2033 GMT
		# 证书所代表的实体的信息
        Subject: O = mkcert development CA, OU = root@pve, CN = mkcert root@pve
        # 证书所包含的公钥信息,包括公钥算法、公钥和模数
		Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (3072 bit)
                Modulus:
                    00:b9:62:e0:85:05:4b:ab:b8:23:02:86:4f:de:0e:
                    ...
                Exponent: 65537 (0x10001)
        # X.509 证书的扩展信息,包括证书用途、基本限制和实体密钥标识符等
		X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
			# 在后面生成的 服务端证书中会包含这个值,可以用来判断是否是由该 CA 签发的证书
            X509v3 Subject Key Identifier: 
                61:41:20:D4:1A:22:F9:8B:A3:FE:6E:FE:C2:B8:8A:8D:26:C5:61:F3
    # 证书的数字签名,由颁发机构使用其私钥签名生成,用于验证证书的完整性和真实性
	Signature Algorithm: sha256WithRSAEncryption
         8c:8b:81:61:b2:5a:7e:05:55:f3:e7:1a:28:df:85:3b:58:6f:
         ...

三、生成服务端证书

3.1 mkcert 生成服务端证书

mkcert abc.com 192.168.0.100
# ls
abc.com+1-key.pem abc.com+1.pem

3.2 openssl 生成服务端证书

# 生成服务端私钥
openssl genpkey -algorithm RSA -out server.key -aes256
# 生成 CSR
openssl req -newkey rsa:2048 -nodes -keyout server.key \
	-out server.csr \
	-subj "/C=US/ST=California/L=San Francisco/O=My Company/OU=IT Department/CN=example.com" \
	-addext "subjectAltName = DNS:abc.com,DNS:www.abc.com,IP:192.168.0.1"
# 通过 CSR 生成服务端证书
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
		-out server.crt -days 3650 \
		-extfile <(printf "subjectAltName=DNS:abc.com,DNS:www.abc.com,IP:192.168.0.100")

3.3 cfssl 生成服务端证书

{
  "CN": "abc.com",
  "hosts": [
    "abc.com",
    "www.abc.com",
    "192.168.0.100"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "San Francisco",
      "O": "My Company",
      "OU": "IT Department",
      "ST": "California"
    }
  ]
}

保存为 config.json,然后使用下面命令生成

cfssl gencert -ca ca.pem -ca-key ca-key.pem -config config.json \
	-profile server server-csr.json | cfssljson -bare server

3.4 证书示例与说明

Certificate:
    Data:
		# 证书版本号
        Version: 3 (0x2)
		# 证书序列号,用于唯一标识证书
        Serial Number:
            9e:a1:c9:65:a9:a1:6a:43:93:dc:2b:24:96:15:d7:ab
        # 签名算法
		Signature Algorithm: sha256WithRSAEncryption
		# 证书颁发者信息,包括组织名(O)、组织单位(OU)和通用名称(CN)
        # 其实就是 CA 的信息
        Issuer: O = mkcert development CA, OU = root@pve, CN = mkcert root@pve
		# 证书有效期,包括证书生效时间(Not Before)和证书失效时间(Not After)
        Validity
            Not Before: Jul 10 14:40:52 2023 GMT
            Not After : Jul 10 14:40:52 2033 GMT
		# 证书主体信息,包括组织名(O)和组织单位(OU)
        Subject: O = mkcert development certificate, OU = root@pve
		# 证书公钥信息,包括公钥算法(Public Key Algorithm)、公钥(RSA Public-Key)和指数(Exponent)
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:c9:05:0e:d7:59:02:5b:30:55:8f:df:e3:54:90:
                    ...
                Exponent: 65537 (0x10001)
		# 证书扩展信息
        X509v3 extensions:
			# 证书密钥用途,这里是数字签名和密钥加密
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            # 证书扩展密钥用途,这里是TLS Web Server Authentication
			X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            # 基本限制扩展,这里标记为非CA证书
			X509v3 Basic Constraints: critical
                CA:FALSE
            # 颁发者密钥标识符,用于标识颁发者的公钥
			# 这里对应上面 CA 中的 X509v3 Subject Key Identifier
			X509v3 Authority Key Identifier: 
                keyid:61:41:20:D4:1A:22:F9:8B:A3:FE:6E:FE:C2:B8:8A:8D:26:C5:61:F3
			# 主体备用名称,这里就是  SANs 的值
            X509v3 Subject Alternative Name: 
                DNS:abc.com, IP Address:192.168.0.100
    # 证书签名值,由颁发者使用私钥对证书信息进行签名得到
	Signature Algorithm: sha256WithRSAEncryption
         5c:ea:7b:5c:6b:aa:ef:03:2b:a2:9f:3a:3d:45:06:75:bc:56:
         ...

四、安装 CA 根证书

在 SSL 通信过程中,客户端会对服务端发送过来的证书进行验证,这个验证就需要 ca.crt

openssl verify -CAfile ca.crt server.crt

在非对称算法中,私钥用于加密,公钥用于解密。我们使用公钥(ca.crt) 对 server.crt 进行解密,如果能解密成功,说明这个证书是通过 CA 签发的,然后再对比证书 hash 来进行验证。

4.1 Windows 安装证书

将 ca.crt 拷贝到 windows 上,然后双击进行安装。在证书存储时选择受信任的根证书颁发机构,安装完成后 windows 自带浏览器和一些内置的程序,就可以使用该 CA 签发的证书进行网络通信了。

如果要使用 Chrome 或 Firefox 之类的浏览器,还是需要单独对浏览器配置 受信任的根证书颁发机构

4.2 Linux 安装证书

func init() {
	switch {
	case binaryExists("apt"):
		CertutilInstallHelp = "apt install libnss3-tools"
	case binaryExists("yum"):
		CertutilInstallHelp = "yum install nss-tools"
	case binaryExists("zypper"):
		CertutilInstallHelp = "zypper install mozilla-nss-tools"
	}
	if pathExists("/etc/pki/ca-trust/source/anchors/") {
		SystemTrustFilename = "/etc/pki/ca-trust/source/anchors/%s.pem"
		SystemTrustCommand = []string{"update-ca-trust", "extract"}
	} else if pathExists("/usr/local/share/ca-certificates/") {
		SystemTrustFilename = "/usr/local/share/ca-certificates/%s.crt"
		SystemTrustCommand = []string{"update-ca-certificates"}
	} else if pathExists("/etc/ca-certificates/trust-source/anchors/") {
		SystemTrustFilename = "/etc/ca-certificates/trust-source/anchors/%s.crt"
		SystemTrustCommand = []string{"trust", "extract-compat"}
	} else if pathExists("/usr/share/pki/trust/anchors") {
		SystemTrustFilename = "/usr/share/pki/trust/anchors/%s.pem"
		SystemTrustCommand = []string{"update-ca-certificates"}
	}
}

我们可以参考 mkcert 的代码,来将 ca.crt 放到对应的位置并安装到系统。

五、SSL 通信过程