數字證書是一個經證書受權中心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件。證書籤發涉及到了非對稱加密方面的知識,這裏介紹使用golang中的x509標準庫進行證書自簽發,還有證書籤發後如何使用golang進行雙向認證.git
根證書是CA認證中心給本身頒發的證書,是信任鏈的起始點.這裏咱們本身作CA使用openssl命令來生成根證書.golang
首先生成私鑰tcp
openssl genrsa -out key.pem 2048
而後根據私鑰提取公鑰ui
openssl rsa -in key.pem -pubout -out key.pub
開始生成X509格式的自簽名證書,會要求輸入區別名DN的各項信息(國家,城市,組織,姓名,email等.編碼
penssl req -x509 -new -days 365 -key rsakey.pem -out cert.crt
到這裏根證書就製做好了,下面開始使用golang根據根證書籤發下一級證書.加密
關於自簽發證書的流程,這裏作個簡單介紹.golang的x509標準庫下有個Certificate結構,這個結構就是證書解析後對應的實體.新證書須要先生成祕鑰對,而後使用根證書的私鑰進行簽名.證書和私鑰以及公鑰這裏使用的是pem編碼方式.code
首先讀取根證書的證書和私鑰ip
//解析根證書 caFile, err := ioutil.ReadFile(rootCa) if err != nil { return } caBlock, _ := pem.Decode(caFile) cert, err := x509.ParseCertificate(caBlock.Bytes) if err != nil { return } //解析私鑰 keyFile, err := ioutil.ReadFile(rootKey) if err != nil { return } keyBlock, _ := pem.Decode(keyFile) praKey, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) if err != nil { return }
而後須要生成新證書的模板,裏面的字段根據本身需求填寫,ssl
cer := &x509.Certificate{ SerialNumber: big.NewInt(rd.Int63()), //證書序列號 Subject: pkix.Name{ Country: []string{"CN"}, Organization: []string{"Easy"}, OrganizationalUnit: []string{"Easy"}, Province: []string{"ShenZhen"}, CommonName: equi.Code, Locality: []string{"ShenZhen"}, }, NotBefore: time.Now(), //證書有效期開始時間 NotAfter: time.Now().AddDate(1, 0, 0), //證書有效期結束時間 BasicConstraintsValid: true, //基本的有效性約束 IsCA: false, //是不是根證書 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, //證書用途(客戶端認證,數據加密) KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment, EmailAddresses: []string{"test@test.com"}, IPAddresses: []net.IP{net.ParseIP("192.168.1.59")}, }
當獲取到這種信息後就能夠簽發證書了,key和ca就是簽發好的證書和私鑰.ci
//生成公鑰私鑰對 priKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return } ca, err = x509.CreateCertificate(rand.Reader, equiCer, rootCa, &priKey.PublicKey, rootKey) if err != nil { return } //編碼證書文件和私鑰文件 caPem := &pem.Block{ Type: "CERTIFICATE", Bytes: ca, } ca = pem.EncodeToMemory(caPem) buf := x509.MarshalPKCS1PrivateKey(priKey) keyPem := &pem.Block{ Type: "PRIVATE KEY", Bytes: buf, } key = pem.EncodeToMemory(keyPem)
客戶端
//加載客戶端證書 //這裏加載的是服務端簽發的 cert, err := tls.LoadX509KeyPair("client_cert.pem", "client_key.pem") if err != nil { log.Fatalln(err) } config := &tls.Config{ //這裏先不驗證服務端證書,是本身簽發的呀 InsecureSkipVerify: true, Certificates: []tls.Certificate{cert}, } raddr, err := net.ResolveTCPAddr("tcp", "192.168.1.59:6001") if err != nil { log.Fatalln(err) } conn, err := net.DialTCP("tcp", nil, raddr) if err != nil { log.Fatalln(err) } tlsConn := tls.Client(conn, config)
tlsConn就和net.Conn同樣了,當調用Wirte時就會進行握手,若是服務端證書不符合要求,就會返回錯誤.
服務端
//這裏讀取的是根證書 buf, err := ioutil.ReadFile(d.conf.Tls.CA) if err != nil { return } pool := x509.NewCertPool() pool.AppendCertsFromPEM(buf) //加載服務端證書 cert, err := tls.LoadX509KeyPair(d.conf.Tls.Cert, d.conf.Tls.Key) if err != nil { return } tlsConfig := &tls.Config Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: pool, } //accept到conn後 tlsConn := tls.Server(conn, tlsConfig)
這個tlsConn和客戶端的同樣,也能夠手動調用Handshake進行握手.
這裏只是進行了簡單介紹,關於證書的知識還不止這些.好比x509標準庫還能夠生成簽發證書請求,解析證書吊銷列表等.多加練習才能對證書方面知識理解更深.