自签pfx证书,带单个域名
-
创建自签的根证书
openssl genrsa -out myCA.key openssl req -new -x509 -days 3650 -key myCA.key -out myCA.crt
-
创建自签的证书请求
openssl genrsa -out mykey.key openssl req -new -key mykey.key -out mycert.csr
-
通过根证书签发下级证书
3.1 不带x509v3,即不绑定域名或者IPopenssl x509 -req -CA myCA.crt -CAkey myCA.key -CAcreateserial -days 3560 -in mycert.csr -out mycert.crt
3.2 带上x509v3就可以绑定域名或IP
vim v3.ext # 在当前目录下新创建此文件,内容如下
添加配置内容
[default] # Extensions to add to a certificate request #basicConstraints = CA:TRUE #keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName=@alt_names [alt_names] #DNS.1 = flymote.com #DNS.2 = *.flymote.com #DNS.3 = www.flymot.com #DNS.4 = *.flymot.com IP.1 = 1.2.3.4 IP.2 = 5.6.7.8 # 此处就可以配置IP,可以多个,域名也如下所示
生成证书请求文件
openssl x509 -req -CA myCA.crt -CAkey myCA.key -CAcreateserial -days 3560 -in mycert.csr -out mycert-domain.crt -extfile v3.ext
-
把上面生成的证书转换为 pfx 文件
openssl pkcs12 –export –in [用户证书文件] –certfile [CA根证书文件] -inkey [用户私钥文件] –passout pass:[P12文件的加密密码] –out [P12文件] -name [用户证书别名] –caname [CA证书别名] #可能需要手动输入密码 openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt -password pass:xxxxxxxx
导出成pfx的时候,如果加了 -name 或者 -cname 参数,在生成的时候又没有,就可能报错: ANS1 结构错误
-
通过以上操作,得到的文件如下图
Server端进行SSL的监听
使用Golang启动一个SSL的Server,代码如下
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
// 监听一个HTTPS端口的服务器
func main() {
// 直接在生成 server 的时候加载 SSL 的配置
server := &http.Server{
Addr: ":8443",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
TLSConfig: tlsConfig(),
}
# 请求处理
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
msg := fmt.Sprintf("Protocol: %s IP:%s ServerName:%s Scheme:%s Host:%s",
r.Proto, r.RemoteAddr, r.TLS.ServerName, r.URL.Path, r.Host)
_, _ = w.Write([]byte(msg))
})
// 如果上面不指定SSL配置,可以在此指定两个SSL的文件
if err := server.ListenAndServeTLS("", ""); err != nil {
log.Fatal(err)
}
}
// SSL配置生成
func tlsConfig() *tls.Config {
# 加载证书
crt, err := ioutil.ReadFile("../domain_ssl/mycert.crt")
if err != nil {
log.Fatal(err)
}
# 加载证书请求
key, err := ioutil.ReadFile("../domain_ssl/mykey.key")
if err != nil {
log.Fatal(err)
}
# 合并成证书
cert, err := tls.X509KeyPair(crt, key)
if err != nil {
log.Fatal(err)
}
// 加载根证书 签发证书时生成的,client 请求时带上同样的,就可以过验证了
rootCa, err := ioutil.ReadFile("../domain_ssl/myCA.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(rootCa)
if !ok {
panic("failed to parse root certificate")
}
# 生成TLS配置
return &tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: "localhost", // 这个为生成证书是指定的域名或IP地址
RootCAs: caCertPool,
}
}
Client端 pfx 证书请求接口
客户端需要读取pfx文件来请求接口,也可以直接读取 crt 或者 key 证书,如下代码为读取pfx文件
package main
import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"github.com/astaxie/beego/logs"
"golang.org/x/crypto/pkcs12"
"io/ioutil"
"net/http"
"os"
"strings"
)
// 从 pfx 证书文件中进行SSL请求
func main() {
dir, _ := os.Getwd()
pfxFile := dir + "./domain_ssl/mycert-domain.pfx"
pfxPwd := "pass:123456" // 证书签发时的密码
err := sslClientFromPfx(pfxFile, pfxPwd)
if err != nil {
logs.Error("出错了:%v", err)
return
}
}
// pfx 的SSL请求
func sslClientFromPfx(pfxFile, pfxPwd string) (err error) {
f, err := os.Open(pfxFile)
if err != nil {
return err
}
// 读取pfx文件内容
bytes, err := ioutil.ReadAll(f)
if err != nil {
return err
}
// 因为pfx证书公钥和密钥是成对的,所以要先转成 pem.Block
blocks, err := pkcs12.ToPEM(bytes, pfxPwd)
if err != nil {
return err
}
if len(blocks) != 2 {
return errors.New("密钥长度错误")
}
var pemData []byte
for _, b := range blocks {
pemData = append(pemData, pem.EncodeToMemory(b)...)
}
// then use PEM data for tls to construct tls certificate:
cert, err := tls.X509KeyPair(pemData, pemData)
if err != nil {
return err
}
// 根证书 要使用签发时候生成的
rootCa, err := ioutil.ReadFile("./domain_ssl/myCA.crt")
if err != nil {
return err
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(rootCa)
if !ok {
panic("failed to parse root certificate")
}
// Setup HTTPS client
tlsCfg := &tls.Config{
Certificates: []tls.Certificate{cert},
// 域名,签发证书的时候绑定的,如果改为:localhost22 就不一致了,
// 报错: x509: certificate is valid for localhost, not localhost22
// 如果改为: 192.168.100.100 ,就会报错:
// x509: cannot validate certificate for 192.168.100.100 because it doesn't contain any IP SANs
// 因为在签发证书的时候,没有配置IP
ServerName: "localhost", // 此处理的域名,可以和请求的URL地址中的域名不一致!
RootCAs: caCertPool,
InsecureSkipVerify: false, // 跳过验证,默认为 false
}
httpTs := &http.Transport{
TLSClientConfig: tlsCfg,
DisableCompression: true,
}
client := &http.Client{Transport: httpTs}
url := "http://192.168.100.100:8443"
arg := `{"phone":"15712033162","value":"n/Gp3q190XwdIcBQIwaGYmYUIJR1QakCreUYwr09KmQ=","type":"pass","channel":65535}`
res, err := client.Post(url, "Content-Type:application/json", strings.NewReader(arg))
if err != nil {
return err
}
defer res.Body.Close()
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return
}
logs.Info("请求响应:%s", all)
return
}
问题总结
-
x509: certificate signed by unknown authority
使用的是自签的CA证书,在服务启动和客户机请求的时候最好带上,如下图
参考
证书crt、pem、pfx、cer、key 作用及区别
Openssl用公钥crt+私钥key生成pfx
openssl 自签证书(带ip或者域名)
基于OpenSSL的CA建立及证书签发(签发单域名/IP)
使用 Go 实现 TLS socket server
golang使用ssl/tls加密tcp(不是https)
golang解析pfx证书文件获得证书私钥
使用 golang 如何生成 rsa 证书,然后将私钥导出到 pfx