通過Go語言創建CA與簽發證書

来源:https://www.cnblogs.com/Cylon/archive/2022/07/01/16436126.html
-Advertisement-
Play Games

本篇文章中,將描述如何使用go創建CA,並使用CA簽署證書。在使用openssl創建證書時,遵循的步驟是 創建秘鑰 > 創建CA > 生成要頒發證書的秘鑰 > 使用CA簽發證書。這種步驟,那麼我們現在就來嘗試下。 創建證書的頒發機構 首先,會從將從創建 CA 開始。CA 會被用來簽署其他證書 // ...


本篇文章中,將描述如何使用go創建CA,並使用CA簽署證書。在使用openssl創建證書時,遵循的步驟是 創建秘鑰 > 創建CA > 生成要頒發證書的秘鑰 > 使用CA簽發證書。這種步驟,那麼我們現在就來嘗試下。

創建證書的頒發機構

首先,會從將從創建 CA 開始。CA 會被用來簽署其他證書

// 對證書進行簽名
ca := &x509.Certificate{
	SerialNumber: big.NewInt(2019),
	Subject: pkix.Name{
        CommonName:    "domain name",
		Organization:  []string{"Company, INC."},
		Country:       []string{"US"},
		Province:      []string{""},
		Locality:      []string{"San Francisco"},
		StreetAddress: []string{"Golden Gate Bridge"},
		PostalCode:    []string{"94016"},
	},
	NotBefore:             time.Now(),  // 生效時間
	NotAfter:              time.Now().AddDate(10, 0, 0), // 過期時間 年月日
	IsCA:                  true, // 表示用於CA
    // openssl 中的 extendedKeyUsage = clientAuth, serverAuth 欄位
	ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
    // openssl 中的 keyUsage 欄位
	KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
	BasicConstraintsValid: true,
}

接下來需要對證書生成公鑰和私鑰

caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
	return err
}

然後生成證書:

caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
if err != nil {
	return err
}

我們看到的證書內容是PEM編碼後的,現在caBytes我們有了生成的證書,我們將其進行 PEM 編碼以供以後使用:

caPEM := new(bytes.Buffer)
pem.Encode(caPEM, &pem.Block{
	Type:  "CERTIFICATE",
	Bytes: caBytes,
})

caPrivKeyPEM := new(bytes.Buffer)
pem.Encode(caPrivKeyPEM, &pem.Block{
	Type:  "RSA PRIVATE KEY",
	Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
})

創建證書

證書的 x509.Certificate 與CA的 x509.Certificate 屬性有稍微不同,需要進行一些修改

cert := &x509.Certificate{
	SerialNumber: big.NewInt(1658),
	Subject: pkix.Name{
        CommonName:    "domain name",
		Organization:  []string{"Company, INC."},
		Country:       []string{"US"},
		Province:      []string{""},
		Locality:      []string{"San Francisco"},
		StreetAddress: []string{"Golden Gate Bridge"},
		PostalCode:    []string{"94016"},
	},
    IPAddresses:  []net.IP{}, // 這裡就是openssl配置文件中 subjectAltName 里的 IP:/IP=
    DNSNames:     []string{}, // 這裡就是openssl配置文件中 subjectAltName 里的 DNS:/DNS=
	NotBefore:    time.Now(),
	NotAfter:     time.Now().AddDate(10, 0, 0),
	SubjectKeyId: []byte{1, 2, 3, 4, 6},
    // 這裡就是openssl中的extendedKeyUsage 
	ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
	KeyUsage:     x509.KeyUsageDigitalSignature,
}

註:這裡會在證書中特別添加了 DNSIP (這個不是必須的),這個選項的增加代表的我們的證書可以支持多功能變數名稱

為該證書創建私鑰和公鑰:

certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
	return err
}

使用CA簽署證書

有了上述的內容後,可以創建證書並用CA進行簽名

certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey)
if err != nil {
	return err
}

要保存成證書格式需要做PEM編碼

certPEM := new(bytes.Buffer)
pem.Encode(certPEM, &pem.Block{
	Type:  "CERTIFICATE",
	Bytes: certBytes,
})

certPrivKeyPEM := new(bytes.Buffer)
pem.Encode(certPrivKeyPEM, &pem.Block{
	Type:  "RSA PRIVATE KEY",
	Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
})

把上面內容融合為一起

創建一個 ca.go 裡面是創建ca和頒發證書的邏輯

package main

import (
	"bytes"
	cr "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"math/rand"
	"net"
	"os"
	"time"
)

type CERT struct {
	CERT       []byte
	CERTKEY    *rsa.PrivateKey
	CERTPEM    *bytes.Buffer
	CERTKEYPEM *bytes.Buffer
	CSR        *x509.Certificate
}

func CreateCA(sub *pkix.Name, expire int) (*CERT, error) {
	var (
		ca  = new(CERT)
		err error
	)

	if expire < 1 {
		expire = 1
	}
	// 為ca生成私鑰
	ca.CERTKEY, err = rsa.GenerateKey(cr.Reader, 4096)
	if err != nil {
		return nil, err
	}

	// 對證書進行簽名
	ca.CSR = &x509.Certificate{
		SerialNumber: big.NewInt(rand.Int63n(2000)),
		Subject:      *sub,
		NotBefore:    time.Now(),                       // 生效時間
		NotAfter:     time.Now().AddDate(expire, 0, 0), // 過期時間
		IsCA:         true,                             // 表示用於CA
		// openssl 中的 extendedKeyUsage = clientAuth, serverAuth 欄位
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		// openssl 中的 keyUsage 欄位
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
	}
	// 創建證書
	// caBytes 就是生成的證書
	ca.CERT, err = x509.CreateCertificate(cr.Reader, ca.CSR, ca.CSR, &ca.CERTKEY.PublicKey, ca.CERTKEY)
	if err != nil {
		return nil, err
	}
	ca.CERTPEM = new(bytes.Buffer)
	pem.Encode(ca.CERTPEM, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: ca.CERT,
	})
	ca.CERTKEYPEM = new(bytes.Buffer)
	pem.Encode(ca.CERTKEYPEM, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(ca.CERTKEY),
	})

	// 進行PEM編碼,編碼就是直接cat證書裡面內容顯示的東西
	return ca, nil
}

func Req(ca *x509.Certificate, sub *pkix.Name, expire int, dns []string, ip []net.IP) (*CERT, error) {
	var (
		cert = &CERT{}
		err  error
	)
	cert.CERTKEY, err = rsa.GenerateKey(cr.Reader, 4096)
	if err != nil {
		return nil, err
	}
	if expire < 1 {
		expire = 1
	}
	cert.CSR = &x509.Certificate{
		SerialNumber: big.NewInt(rand.Int63n(2000)),
		Subject:      *sub,
		IPAddresses:  ip,
		DNSNames:     dns,
		NotBefore:    time.Now(),
		NotAfter:     time.Now().AddDate(expire, 0, 0),
		SubjectKeyId: []byte{1, 2, 3, 4, 6},
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:     x509.KeyUsageDigitalSignature,
	}

	cert.CERT, err = x509.CreateCertificate(cr.Reader, cert.CSR, ca, &cert.CERTKEY.PublicKey, cert.CERTKEY)
	if err != nil {
		return nil, err
	}

	cert.CERTPEM = new(bytes.Buffer)
	pem.Encode(cert.CERTPEM, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert.CERT,
	})
	cert.CERTKEYPEM = new(bytes.Buffer)
	pem.Encode(cert.CERTKEYPEM, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(cert.CERTKEY),
	})
	return cert, nil
}

func Write(cert *CERT, file string) error {
	keyFileName := file + ".key"
	certFIleName := file + ".crt"
	kf, err := os.Create(keyFileName)
	if err != nil {
		return err
	}
	defer kf.Close()

	if _, err := kf.Write(cert.CERTKEYPEM.Bytes()); err != nil {
		return err
	}

	cf, err := os.Create(certFIleName)
	if err != nil {
		return err
	}
	if _, err := cf.Write(cert.CERTPEM.Bytes()); err != nil {
		return err
	}
	return nil
}

如果需要使用的話,可以引用這些函數

package main

import (
	"crypto/x509/pkix"
	"log"
	"net"
)

func main() {
	subj := &pkix.Name{
		CommonName:    "chinamobile.com",
		Organization:  []string{"Company, INC."},
		Country:       []string{"US"},
		Province:      []string{""},
		Locality:      []string{"San Francisco"},
		StreetAddress: []string{"Golden Gate Bridge"},
		PostalCode:    []string{"94016"},
	}
	ca, err := CreateCA(subj, 10)
	if err != nil {
		log.Panic(err)
	}

	Write(ca, "./ca")

	crt, err := Req(ca.CSR, subj, 10, []string{"test.default.svc", "test"}, []net.IP{})

	if err != nil {
		log.Panic(err)
	}

	Write(crt, "./tls")
}

遇到的問題

panic: x509: unsupported public key type: rsa.PublicKey

這裡是因為 x509.CreateCertificate 的參數 privatekey 需要傳入引用變數,而傳入的是一個普通變數

註:x509: only RSA and ECDSA public keys supported

一些參數的意思

extendedKeyUsage :增強型密鑰用法(參見"new_oids"欄位):伺服器身份驗證、客戶端身份驗證、時間戳。

extendedKeyUsage = critical,serverAuth, clientAuth, timeStamping

keyUsage : 密鑰用法,防否認(nonRepudiation)、數字簽名(digitalSignature)、密鑰加密(keyEncipherment)。

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

Reference

golang ca and signed cert go

package x509

作者:鋼閘門
出處:http://lc161616.cnblogs.com/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 阿裡雲優惠:點擊力享低價 墨墨學英語:幫忙點一下
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 6月30日,在2022(第五屆)GIS軟體技術大會GIS基礎軟體新技術板塊論壇上,華為聯合超圖推出了基於HMS Core 3D Engine開發的高保真三維GIS插件,通過3D渲染技術助力三維GIS實現更高清、更具真實感的數字世界場景。 本次大會以“地理智慧 多維築基”為主題,旨在分享GIS軟體的最 ...
  • 如果能開發一個線上K歌應用,就能與王心凌線上合唱實現追星自由。 如何快速實現合唱功能?即構科技提供了實時合唱一站式解決方案和技術實現流程。 ...
  • 前言 我以往在實現點擊按鈕切換DOM元素樣式的時候,使用的是在全局範圍內定義一個flag變數,然後用true和false來對應不同的狀態。 const btn = document.querySelector('#btn'); //獲取按鈕元素 let flag = false; //flag是全局 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 var length = 10; function fn () { return this.length + 1; } var obj = { length: 5, test1: function () { return fn(); } ...
  • 效果展示 思路分析 層級思路 從展示的效果可以猜到這個效果由3層文字組成。 錶面一層黑色文字 中間一層白色文字 最低的一層是斑馬條紋文字 要實現多層文字,我首先想到的是文字投影 text-shadow ,但通常 text-shadow 只能投個純色出來。 於是思路要改一下,黑色和白色層是投影,斑馬條 ...
  • bootstrap-select,selectpicker 用法詳細:通過官方文檔翻譯 用過selectpicker的都說好~但是網上中文的教程又找不到比較完整的用法,於是去官網看了下 順便弄過來翻譯一下: 選項可以通過數據屬性或JavaScript傳遞。對於數據屬性,附加選項名稱data-,如 d ...
  • Fundebug 前端異常監控服務 Fundebug提供專業的前端異常監控服務,我們的插件可以提供全方位的異常監控,可以幫助開發者第一時間定位各種前端異常,包括但不限於 JavaScript 執行錯誤以及 HTTP 請求錯誤。 並且,Fundebug 支持 Source Map 還原,記錄用戶行為以 ...
  • 前段時間研究了下基於`Prometheus`構建監控系統相關的概念,並以此為基準設計了一個企業級通用的監控告警平臺的方案。這裡分享一下架構的分析過程以及問題的解決思路。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...