写在前面的话

CFSSL是CloudFlare旗下的PKI/TLS工具。可以用于数字签名,签名验证和TLS证书捆绑的命令行工具和HTTP API服务器。

是使用golang语言开发的证书工具。

官方地址:

github地址:https://github.com/cloudflare/cfssl

下载cfssl工具链

https://github.com/cloudflare/cfssl/releases

下载如下文件

cfssl_1.6.0_darwin_amd64 表示cfssl的工具

cfssljson_1.6.0_darwin_amd64 表示使用json展示的工具

cfssl-certinfo_1.6.0_darwin_amd64 表示证书的查看工具

软连接生成cfssl和cfssljson

ln -s ./cfssl_1.6.0_darwin_amd64 cfssl
ln -s ./cfssl-certinfo_1.6.0_darwin_amd64 cfssl-certinfo
ln -s ./cfssljson_1.6.0_darwin_amd64 cfssljson

  

使用cfssl生成证书步骤

1. 编写CA根证书的证书签名请求文件

证书签名请求(Certificate Signing Request)文件,文件格式为ca-csr.json,【文件名含义是CA of Certificate Signing Request】

ca-csr.json文件中包含如下内容简要说明

  • CN: Common Name,表示业务的名称或者对外的域名。

  • C: Country, 表示国家

  • L: Locality,表示地区或城市

  • O: Organization Name,表示组织名称或公司名称

  • OU: Organizational Unit 表示组织单元名称
  • ST: State,表示 州,省OU: Organization Unit Name,组织单位名称或者部门

  • ca.expiry 表示证书的有效期,此处是20年
  • key.algo 表示证书的签名算法 使用rsa
  • hosts 表示要签名的域名,此处是根证书,所以空着,用于签名其他的证书。

ca-csr.json文件内容如下:

➜  certs cat ca-csr.json|python -m json.tool
{
"CN": "voipman",
"ca": {
"expiry": "175200h"
},
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"O": "MyCompany",
"OU": "MyTemp",
"ST": "BeiJing"
}
]
}

2. 使用CA跟证书签名请求文件生成CA根证书。

➜  cfssl gencert -initca  ca-csr.json|cfssljson -bare ca
2021/08/18 10:37:00 [INFO] generating a new CA key and certificate from CSR
2021/08/18 10:37:00 [INFO] generate received request
2021/08/18 10:37:00 [INFO] received CSR
2021/08/18 10:37:00 [INFO] generating key: rsa-2048
2021/08/18 10:37:00 [INFO] encoded CSR
2021/08/18 10:37:00 [INFO] signed certificate with serial number 116851465485290360665710914818380982850969052112

通过执行上面的命令,产生如下文件:

ca.pem 表示CA根证书,可以公开

ca-key.pem 表示CA根证书的密钥,不要公开

ca.csr 表示CA证书签名请求

我们主要分析一下ca.pem证书文件中的内容信息

3. 查看 ca.pem CA根证书

里面包含什么信息呢,可以通过cfssl-certinfo工具查看证书内容

cfssl-certinfo -cert ca.pem
{
"subject": {
"common_name": "voipman",
"country": "CN",
"organization": "MyCompany",
"organizational_unit": "MyTemp",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"MyCompany",
"MyTemp",
"voipman"
]
},
"issuer": {
"common_name": "voipman",
"country": "CN",
"organization": "MyCompany",
"organizational_unit": "MyTemp",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"MyCompany",
"MyTemp",
"voipman"
]
},
"serial_number": "116851465485290360665710914818380982850969052112",
"not_before": "2021-08-18T02:32:00Z",
"not_after": "2041-08-13T02:32:00Z",
"sigalg": "SHA256WithRSA",
"authority_key_id": "",
"subject_key_id": "34:9C:3B:8B:54:02:6F:2F:D3:F4:29:9B:23:23:6C:47:0D:0A:16:2B",
"pem": "-----BEGIN CERTIFICATE-----\n.....\n-----END CERTIFICATE-----\n"
}

从如上的ca.pem中可以看到

签名算法:"sigalg": "SHA256WithRSA"

证书生效时间:"not_before": "2021-08-18T02:32:00Z"

证书失效时间:"not_after": "2041-08-13T02:32:00Z",说明证书签名请求文件中设置的20年的证书有效期。

序列化:"serial_number": "116851465485290360665710914818380982850969052112",在前面生成证书时会打印出来。

证书内容:"pem": "-----BEGIN CERTIFICATE-----...,此处省调证书内容信息。

其他字段在证书签名请求时已经做过介绍,此处忽略。

4. 编写CA签名配置文件ca-config.json

cat ca-config.json |python -m json.tool
{
"signing": {
"default": {
"expiry": "175200h"
},
"profiles": {
"client": {
"expiry": "175200h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
},
"peer": {
"expiry": "175200h",
"usages": [
"signing",
"key encipherment",
"client auth",
"server auth"
]
},
"server": {
"expiry": "175200h",
"usages": [
"signing",
"key encipherment",
"server auth"
]
}
}
}
}

字段说明如下

  • signing, 表示ca.pem证书可用于签名其它证书

  • profile中的peer配置的client auth 和 server auth
  • profile中的client配置的client auth
  • profile中的server配置的server auth
  • server auth:表示 客户端client 可以用 CA证书 对 服务端server的证书进行签名验证。
  • client auth:表示 服务端server 可以用 CA证书 对 客户端client 提供的证书进行签名验证。
  • server auth和client auth都存在时,说明客户端和服务端双向验证。

如下业务域名证书生成选择的profle是peer,表示双向验证。

同样证书的失效日期是20年。

5. 编写业务域名的证书签名请求文件 voipman-csr.json

cat voipman-csr.json |python -m json.tool
{
"CN": "voipman",
"hosts": [
"127.0.0.1",
"*.voipman.com",
"localhost",
"voipman.com",
"*.vipman.com",
"vipman.com"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"O": "voipman",
"OU": "MyTeam",
"ST": "BeiJing"
}
]
}

这个业务域名签名请求文件的内容和ca-csr.json内容含义类似,关键部分是增加了hosts的配置,将需要签名认证的ip地址和域名增加到hosts列表中。

6. 生成业务域名的证书和私钥

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer voipman-csr.json|cfssljson -bare voipman-peer
2021/08/18 11:15:09 [INFO] generate received request
2021/08/18 11:15:09 [INFO] received CSR
2021/08/18 11:15:09 [INFO] generating key: rsa-2048
2021/08/18 11:15:09 [INFO] encoded CSR
2021/08/18 11:15:09 [INFO] signed certificate with serial number 178028283460672116734375677106692089761404461988

此命令的参数说明

生成证书命令:cfssh gencerts

使用ca证书:-ca=ca.pem

使用ca的密钥:-ca-key=ca-key.pem

使用ca签名证书的配置:-config=ca-config.json

选择ca签名证书配置的profile项:-profile=peer

选择业务域名的证书签名请求文件:voipman-csr.json

生成业务域名的私钥和证书文件:cfssljson -bare voipman-peer 会生成voipman-peer.pem证书文件和voipman-peer-key.pem的私钥文件。

执行如上命令后,产生如下文件

voipman-peer.pem 业务域名的证书文件,可以直接公开给请求端使用。
voipman-peer-key.pem 业务域名的私钥,不可公开。
voipman-peer.csr 业务域名的证书签名请求文件。

查看 业务域名的证书文件voipman-peer.pem的内容

cfssl-certinfo -cert voipman-peer.pem
{
"subject": {
"common_name": "voipman",
"country": "CN",
"organization": "voipman",
"organizational_unit": "MyTeam",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"voipman",
"MyTeam",
"voipman"
]
},
"issuer": {
"common_name": "voipman",
"country": "CN",
"organization": "MyCompany",
"organizational_unit": "MyTemp",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"MyCompany",
"MyTemp",
"voipman"
]
},
"serial_number": "178028283460672116734375677106692089761404461988",
"sans": [
"*.voipman.com",
"localhost",
"voipman.com",
"*.vipman.com",
"vipman.com",
"127.0.0.1"
],
"not_before": "2021-08-18T03:10:00Z",
"not_after": "2041-08-13T03:10:00Z",
"sigalg": "SHA256WithRSA",
"authority_key_id": "34:9C:3B:8B:54:02:6F:2F:D3:F4:29:9B:23:23:6C:47:0D:0A:16:2B",
"subject_key_id": "AD:54:74:A0:BF:67:E7:B7:18:50:20:0A:77:57:F7:16:D3:62:80:F6",
"pem": "-----BEGIN CERTIFICATE-----\n......\n-----END CERTIFICATE-----\n"
}

如上可以看到,证书文件的内容中

sans表示证书的支持的域名列表

authority_key_id 表示是本证书是从哪个CA证书签名生成的证书,业务域名正式的authority_key_id等于CA证书的subject_key_id。

其他字段说明见签名对CA证书的字段说明。

使用如下命令查询业务域名的证书签名请求信息

如下内容省略了部分内容,重点说明一下内容中的公钥PublicKey(N模数,E公钥指数)

cfssl certinfo -csr voipman-peer.csr
{
"Raw": "xxx",
"RawTBSCertificateRequest": "xxx,
"RawSubject": "xxx",
"Version": 0,
"Signature": "xxx",
"SignatureAlgorithm": 4,
"PublicKeyAlgorithm": 1,
"PublicKey": {
"N": 252598034519828318357090360116071250272xxxxxxxxxxxxxx....xxxxx7,
"E": 65537
} "Subject": { },
"DNSNames": [
"*.voipman.com",
"localhost",
"voipman.com",
"*.vipman.com",
"vipman.com"
],
"EmailAddresses": null,
"IPAddresses": [
"127.0.0.1"
],
"URIs": null
}

7. 使用golang的grpc验证业务域名的证书和私钥

编写proto接口文件,文件命名为test.proto,定义一个EchoService接口,服务端实现时,将请求数据转成大写返回。

syntax = "proto3";
package test; service EchoService {
rpc Echo (Request) returns (Response) {}
} message Request {
string data = 1;
} message Response {
string data = 1;
}

生成go的grpc代码

mkdir -p ../src/test  && protoc --go_out=plugins=grpc:../src/test/ ./test.proto

会生成 src/test/test.pb.go代码文件。

编写gRPC的服务端验证代码,配置证书voipman-peer.pem和私钥voipman-peer-key.pem

代码如下所示

package main
import (
"cert-verify/src/test"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/reflection"
"log"
"net"
"strings"
)
type EchoServer struct{
} func (s *EchoServer) Echo(ctx context.Context, in *test.Request) (*test.Response, error) {
fmt.Println("RequestData: " + in.Data)
return &test.Response{Data: strings.ToUpper(in.Data)}, nil
} func main() {
listen, err := net.Listen("tcp", "0.0.0.0:9025")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
creds, cerErr := credentials.NewServerTLSFromFile("./certs/voipman-peer.pem", "./certs/voipman-peer-key.pem")
if cerErr != nil {
log.Fatalf("Failed to load cert error: %v", cerErr)
}
var grpcServer *grpc.Server
grpcServer = grpc.NewServer(grpc.Creds(creds))
test.RegisterEchoServiceServer(grpcServer, &EchoServer{})
reflection.Register(grpcServer)
grpcServer.Serve(listen)
}

编写gRPC的客户端代码,使用voipman-peer.pem证书请求如上的gRPC服务端

package main
import (
"cert-verify/src/test"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"log"
) func main() {
hostNameList := []string {
"dev.voipman.com",
"test.voipman.com",
"voipman.com",
"127.0.0.1",
"localhost",
"dev.vipman.com",
"test.vipman.com",
"vipman.com",
"dev.unknown.com",
}
for _, hostName := range hostNameList {
url := "127.0.0.1:9025"
creds, err := credentials.NewClientTLSFromFile("./certs/voipman-peer.pem", hostName)
if err != nil {
log.Printf("new rpc client tls fail %v", err)
}
clientConn, err := grpc.DialContext(context.Background(), url, grpc.WithTransportCredentials(creds))
if err != nil {
log.Printf("dail rpc server fail url:%v, err:%v", url, err)
}
if err != nil {
log.Printf(err.Error())
}
defer clientConn.Close()
cli := test.NewEchoServiceClient(clientConn)
response, err := cli.Echo(context.Background(), &test.Request{Data: hostName})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("HostName:%v, Response: %s", hostName, response.Data)
}
}

如上代码中,我们验证voipman-csr.json中定义的hosts列表,如下测试域名应该可以通过证书的验证。

"dev.voipman.com",
"test.voipman.com",
"voipman.com",
"127.0.0.1",
"localhost",
"dev.vipman.com",
"test.vipman.com",
"vipman.com",

另外增加一条错误的域名地址,使用证书验证应该不成功。

运行结果如下
2021/08/18 13:38:12 HostName:dev.voipman.com, Response: DEV.VOIPMAN.COM
2021/08/18 13:38:12 HostName:test.voipman.com, Response: TEST.VOIPMAN.COM
2021/08/18 13:38:12 HostName:voipman.com, Response: VOIPMAN.COM
2021/08/18 13:38:12 HostName:127.0.0.1, Response: 127.0.0.1
2021/08/18 13:38:12 HostName:localhost, Response: LOCALHOST
2021/08/18 13:38:12 HostName:dev.vipman.com, Response: DEV.VIPMAN.COM
2021/08/18 13:38:12 HostName:test.vipman.com, Response: TEST.VIPMAN.COM
2021/08/18 13:38:12 HostName:vipman.com, Response: VIPMAN.COM
2021/08/18 13:38:12 could not greet: rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: x509: certificate is valid for *.voipman.com, localhost, voipman.com, *.vipman.com, vipman.com, not dev.unknown.com"

从运行的结果来看,最后一条域名,使用证书和服务端建立认证时出现错误

could not greet: rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: x509: certificate is valid for *.voipman.com, localhost, voipman.com, *.vipman.com, vipman.com, not dev.unknown.com"

这就说明了证书voipman.pem中的hosts含义所在。

祝玩的开心~

done.

【加解密】使用CFSSL生成证书并使用gRPC验证证书的更多相关文章

  1. python实现RSA加解密

    # coding=utf-8 """ @author:Eleven created on:2018年10月30日 """ import bi ...

  2. 两种JavaScript的AES加密方式(可与Java相互加解密)

    由于JavaScript属于弱类型脚本语言,因此当其与强类型的后台语言进行数据交互时会产生各种问题,特别是加解密的操作.本人由于工作中遇到用js与Java进行相互加解密的问题,在网上查了很多资料及代码 ...

  3. RSA加密通信小结(三)--生成加解密所需的SSL命令与流程

    在iOS中使用RSA加密解密,需要用到.der和.p12后缀格式的文件,其中.der格式的文件存放的是公钥(Public key)用于加密,.p12格式的文件存放的是私钥(Private key)用于 ...

  4. C#创建数字证书并导出为pfx,并使用pfx进行非对称加解密

    本文源程序下载:http://download.csdn.net/source/2444494 我的项目当中,考虑到安全性,需要为每个客户端分发一个数字证书,同时使用数字证书中的公私钥来进行数据的加解 ...

  5. RSA - 原理、特点(加解密及签名验签)及公钥和私钥的生成

    Wiki - RSA加密演算法 Wiki - 欧拉函数 Wiki - 模反元素 ASN.1 格式标准 RSA算法原理(二) 注意: RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BAS ...

  6. .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书

    简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...

  7. rsa互通密钥对生成及互通加解密(c#,java,php)

    摘要 在数据安全上rsa起着非常大的作用,特别是数据网络通讯的安全上.当异构系统在数据网络通讯上对安全性有所要求时,rsa将作为其中的一种选择,此时rsa的互通性就显得尤为重要了. 本文参考网络资料, ...

  8. Python rsa公私钥生成 rsa公钥加解密(分段加解密)-私钥加签验签实战

    一般现在的SAAS服务提供现在的sdk或api对接服务都涉及到一个身份验证和数据加密的问题.一般现在普遍的做法就是配置使用非对称加密的方式来解决这个问题,你持有SAAS公司的公钥,SAAS公司持有你的 ...

  9. Java使用数字证书加密通信(加解密/加签验签)

    本文中使用的Base64Utils.java可参考:http://www.cnblogs.com/shindo/p/6346618.html 证书制作方法可参考:http://www.cnblogs. ...

随机推荐

  1. JDK1.8 ArrayList 源码解析

    源码的解读逻辑按照程序运行的轨迹展开 Arraylist的继承&实现关系 打开ArrayList源码,会看到有如下的属性定义, ArrayList中定义的属性 /** * Default in ...

  2. AcWing 243. 一个简单的整数问题2

    给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1."C l r d",表示把 A[l],A[l+1],-,A[r] 都加上 d. 2."Q l r ...

  3. SpringBoot集成websocket发送后台日志到前台页面

    业务需求 后台为一个采集系统,需要将采集过程中产生的日志实时发送到前台页面展示,以便了解采集过程. 技能点 SpringBoot 2.x websocket logback thymeleaf Rab ...

  4. 暑假自学java第二天

    今天学习了一些java规则 一个java源文件的公开类只能有一个,而且必学和源文件名相同. 了解到java的标识符规范,这对以后的团队协作有很大作用. 标识符规则和c++还是很相似的 java中的字面 ...

  5. buu 达芬奇 && ROT

    一.达芬奇 百度了下电影简介,发现了斐波那契数列,同时发现密文是由斐波那契数列移动而来的,有点像base64变种 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 ...

  6. Kubernetes全栈架构师(二进制高可用安装k8s集群扩展篇)--学习笔记

    目录 二进制Metrics&Dashboard安装 二进制高可用集群可用性验证 生产环境k8s集群关键性配置 Bootstrapping: Kubelet启动过程 Bootstrapping: ...

  7. Java基础00-多态19

    1. 多态 多态 1.1 多态概述 代码示例: 动物类: public class Animal { public void eat(){ System.out.println("动物吃东西 ...

  8. File类与常用IO流第四章——IO字节流

    一切文件在存储时,都是以二进制数字的形式保存的,都是一个一个字节.无论使用什么样的流对象,底层传输的始终是二进制数据. 字节输出流 OutputStream java.io.OutputStream ...

  9. Leetcode6. Z 字形变换

    > 简洁易懂讲清原理,讲不清你来打我~ 输入字符串,按下右上下右上排列后输出字符串![在这里插入图片描述](https://img-blog.csdnimg.cn/4578280a7c1848c ...

  10. 单细胞分析实录(19): 基于CellPhoneDB的细胞通讯分析及可视化 (下篇)

    在上一篇帖子中,我介绍了CellPhoneDB的原理.实际操作,以及一些值得注意的地方.这一篇继续细胞通讯分析的可视化. 公众号后台回复20210723获取本次演示的测试数据,以及主要的可视化代码. ...