【加解密】使用CFSSL生成证书并使用gRPC验证证书
写在前面的话
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验证证书的更多相关文章
- python实现RSA加解密
# coding=utf-8 """ @author:Eleven created on:2018年10月30日 """ import bi ...
- 两种JavaScript的AES加密方式(可与Java相互加解密)
由于JavaScript属于弱类型脚本语言,因此当其与强类型的后台语言进行数据交互时会产生各种问题,特别是加解密的操作.本人由于工作中遇到用js与Java进行相互加解密的问题,在网上查了很多资料及代码 ...
- RSA加密通信小结(三)--生成加解密所需的SSL命令与流程
在iOS中使用RSA加密解密,需要用到.der和.p12后缀格式的文件,其中.der格式的文件存放的是公钥(Public key)用于加密,.p12格式的文件存放的是私钥(Private key)用于 ...
- C#创建数字证书并导出为pfx,并使用pfx进行非对称加解密
本文源程序下载:http://download.csdn.net/source/2444494 我的项目当中,考虑到安全性,需要为每个客户端分发一个数字证书,同时使用数字证书中的公私钥来进行数据的加解 ...
- RSA - 原理、特点(加解密及签名验签)及公钥和私钥的生成
Wiki - RSA加密演算法 Wiki - 欧拉函数 Wiki - 模反元素 ASN.1 格式标准 RSA算法原理(二) 注意: RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BAS ...
- .NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书
简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...
- rsa互通密钥对生成及互通加解密(c#,java,php)
摘要 在数据安全上rsa起着非常大的作用,特别是数据网络通讯的安全上.当异构系统在数据网络通讯上对安全性有所要求时,rsa将作为其中的一种选择,此时rsa的互通性就显得尤为重要了. 本文参考网络资料, ...
- Python rsa公私钥生成 rsa公钥加解密(分段加解密)-私钥加签验签实战
一般现在的SAAS服务提供现在的sdk或api对接服务都涉及到一个身份验证和数据加密的问题.一般现在普遍的做法就是配置使用非对称加密的方式来解决这个问题,你持有SAAS公司的公钥,SAAS公司持有你的 ...
- Java使用数字证书加密通信(加解密/加签验签)
本文中使用的Base64Utils.java可参考:http://www.cnblogs.com/shindo/p/6346618.html 证书制作方法可参考:http://www.cnblogs. ...
随机推荐
- 增强采样软件PLUMED的安装与使用
技术背景 增强采样(Enhanced Sampling)是一种在分子动力学模拟中常用的技术,其作用是帮助我们更加快速的在时间轴上找到尽可能多的体系结构及其对应的能量.比如一个氢气的燃烧反应,在中间过程 ...
- 开始前端三大基础的js之途
初识 js ...
- 你真的懂 export default 吗?
export default A 和 export { A as default } 乍一看是一样的,但是里面有一些细微的区别比较容易留坑.本文介绍两种写法的不同之处. import 语句导入的是引用 ...
- mongodb数据的导出导入
1.[导出]mongoexport -h (主机名) -d (库) -c (集合名) -o (路径) -u (账号) -p (密码)示例:mongoexport -h localhost -d jav ...
- Result Maps collection already contains value for cn.itcast.ssm.mapper.CompetesMapperCustom.baseMap
在使用ssm时出现的错误: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: java.lang ...
- diff -u:内核开发的新项目
译至:http://www.linuxjournal.com/content/diff-u-whats-new-kernel-development-1 Linux的一个问题是它的系统调用实现 . 正 ...
- linux学习之路第九天(磁盘分区,挂载详解)
磁盘分区,挂载 -----分区基础知识 分区的方式 1)mbr分区: 1.最多支持四个主分区 2.系统只能安装在主分区 3.扩展分区要占一个主分区 4.mbr最大只支持2TB,但拥有最好的兼容性 -- ...
- Leetcode No.108 Convert Sorted Array to Binary Search Tree(c++实现)
1. 题目 1.1 英文题目 Given an integer array nums where the elements are sorted in ascending order, convert ...
- mongodb的基本命令与常规操作
1. 查看当前数据库的版本号:db.version()2. 查看当前所在数据库:db 默认是test数据库3. 查看当前数据库的连接地址:db.getMongo()4. 查看所有数据库:show da ...
- 「CF555E」 Case of Computer Network
「CF555E」 Case of Computer Network 传送门 又是给边定向的题目(马上想到欧拉回路) 然而这个题没有对度数的限制,你想歪了. 然后又开始想一个类似于匈牙利的算法:我先跑, ...