322 lines
7.5 KiB
Go
322 lines
7.5 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
OPENSSL_PATH = "openssl"
|
|
// WORK_PATH = "/opt/arrokoth/ca-mini"
|
|
WORK_PATH = "/home/code/git/ca-mini/ca-server"
|
|
CA_CERT = WORK_PATH + "/ca/CaRoot.crt"
|
|
CA_CONFIG = WORK_PATH + "/ca/ca.conf"
|
|
CERT_PATH = WORK_PATH + "/cert"
|
|
CRL = WORK_PATH + "/crl/CaRoot.crl"
|
|
)
|
|
|
|
// GenerateRandomString 生成指定长度的随机字符串
|
|
func GenerateRandomString(length int) (string, error) {
|
|
// 创建一个字节切片,用于存储随机数据
|
|
bytes := make([]byte, length)
|
|
|
|
// 使用crypto/rand包生成随机字节
|
|
_, err := io.ReadFull(rand.Reader, bytes)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate random string: %v", err)
|
|
}
|
|
|
|
// 将随机字节转换为Base64字符串
|
|
// randomString := base64.RawURLEncoding.EncodeToString(bytes)
|
|
|
|
// 将随机字节转换为十六进制字符串
|
|
randomString := fmt.Sprintf("%x", bytes)
|
|
|
|
return randomString, nil
|
|
}
|
|
|
|
// 根据时间戳+随机数生成唯一ID
|
|
func GenerateUniqueID() (string, error) {
|
|
// 获取当前时间戳
|
|
timestamp := time.Now().UnixNano()
|
|
|
|
// 生成随机数
|
|
randomNumber, err := GenerateRandomString(8)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// 拼接时间戳和随机数
|
|
uniqueID := fmt.Sprintf("%d%s", timestamp, randomNumber)
|
|
|
|
// uniqueID := fmt.Sprintf("%d%s", timestamp, uuid.New().String())
|
|
|
|
return uniqueID, nil
|
|
}
|
|
|
|
func GetKeyFilePath(keyId string) string {
|
|
return CERT_PATH + "/" + keyId + ".key"
|
|
}
|
|
|
|
func GetCsrFilePath(keyId string) string {
|
|
return CERT_PATH + "/" + keyId + ".csr"
|
|
}
|
|
|
|
func GetCertFilePath(subject string) string {
|
|
return CERT_PATH + "/" + subject + ".crt"
|
|
}
|
|
|
|
// 生成密钥
|
|
func GenerateKey(alg string, len int) (string, error) {
|
|
// 生成私钥
|
|
// openssl genrsa -out Ca.key 2048
|
|
var keyAlg string
|
|
var keyLen string
|
|
id, err := GenerateUniqueID()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
keyFilePath := GetKeyFilePath(id)
|
|
switch alg {
|
|
case "rsa":
|
|
keyAlg = "rsa"
|
|
case "ec":
|
|
keyAlg = "ec"
|
|
default:
|
|
keyAlg = "rsa"
|
|
}
|
|
|
|
switch len {
|
|
case 1024:
|
|
keyLen = "1024"
|
|
case 2048:
|
|
keyLen = "2048"
|
|
case 4096:
|
|
keyLen = "4096"
|
|
default:
|
|
keyLen = "2048"
|
|
}
|
|
// 创建命令
|
|
xcmd := exec.Command(OPENSSL_PATH, "gen"+keyAlg, "-out", keyFilePath, keyLen)
|
|
|
|
// 捕获标准输出和标准错误
|
|
var stdout, stderr bytes.Buffer
|
|
xcmd.Stdout = &stdout
|
|
xcmd.Stderr = &stderr
|
|
|
|
// 执行命令
|
|
err = xcmd.Run()
|
|
if err != nil {
|
|
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
// 生成证书请求
|
|
func GenerateCsr(id string, subject string, sans []string) (string, error) {
|
|
// 生成证书请求
|
|
// openssl req -new -key Ca.key -out Ca.csr -subj "/C=CN/ST=BeiJing/L=BeiJing/O=xxx/OU=dev/CN=CaRoot/emailAddress=CaRoot@xxx.com"
|
|
keyFilePath := GetKeyFilePath(id)
|
|
csrFilePath := GetCsrFilePath(id)
|
|
// 创建命令
|
|
// xcmd := exec.Command(OPENSSL_PATH, "req", "-new", "-key", keyFilePath, "-out", csrFilePath, "-subj", subject)
|
|
args := []string{"req", "-new", "-key", keyFilePath, "-out", csrFilePath, "-subj", subject}
|
|
// if len(sans) > 0 {
|
|
// sanStr := joinSans(sans)
|
|
// args = append(args, "-addext", "subjectAltName = "+sanStr)
|
|
// }
|
|
xcmd := exec.Command(OPENSSL_PATH, args...)
|
|
// 捕获标准输出和标准错误
|
|
var stdout, stderr bytes.Buffer
|
|
xcmd.Stdout = &stdout
|
|
xcmd.Stderr = &stderr
|
|
// 执行命令
|
|
err := xcmd.Run()
|
|
if err != nil {
|
|
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// 生成证书
|
|
func GenerateCert(id string, days int) (string, error) {
|
|
// 生成证书
|
|
// openssl x509 -req -in Ca.csr -signkey Ca.key -out Ca.crt
|
|
// openssl ca -config ca.conf -notext -batch -in ./subordinate/Ca.csr -out ./subordinate/Ca.crt
|
|
csrFilePath := GetCsrFilePath(id)
|
|
certFilePath := GetCertFilePath(id)
|
|
xargs := []string{
|
|
"ca",
|
|
"-config", CA_CONFIG,
|
|
"-notext",
|
|
"-batch",
|
|
"-in", csrFilePath,
|
|
"-out", certFilePath,
|
|
}
|
|
if days > 0 && days < 3650 {
|
|
xargs = append(xargs, "-days", fmt.Sprintf("%d", days))
|
|
}
|
|
|
|
// 创建命令
|
|
xcmd := exec.Command(OPENSSL_PATH, xargs...)
|
|
|
|
// 捕获标准输出和标准错误
|
|
var stdout, stderr bytes.Buffer
|
|
xcmd.Stdout = &stdout
|
|
xcmd.Stderr = &stderr
|
|
|
|
// 执行命令
|
|
err := xcmd.Run()
|
|
if err != nil {
|
|
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// 生成黑名单
|
|
func GenerateCrl(id string) (string, error) {
|
|
// 生成黑名单
|
|
// openssl ca -gencrl -out crl.pem -config ca.conf
|
|
xargs := []string{
|
|
"ca",
|
|
"-config", CA_CONFIG,
|
|
"-gencrl",
|
|
"-out", CRL,
|
|
}
|
|
|
|
// 创建命令
|
|
xcmd := exec.Command(OPENSSL_PATH, xargs...)
|
|
|
|
// 捕获标准输出和标准错误
|
|
var stdout, stderr bytes.Buffer
|
|
xcmd.Stdout = &stdout
|
|
xcmd.Stderr = &stderr
|
|
|
|
// 执行命令
|
|
err := xcmd.Run()
|
|
if err != nil {
|
|
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// 撤销证书
|
|
func RevokeCert(id string) (string, error) {
|
|
// 撤销证书
|
|
// openssl ca -revoke ./subordinate/Ca.crt -config ca.conf
|
|
certFilePath := GetCertFilePath(id)
|
|
xargs := []string{
|
|
"ca",
|
|
"-config", CA_CONFIG,
|
|
"-revoke", certFilePath,
|
|
}
|
|
|
|
// 创建命令
|
|
xcmd := exec.Command(OPENSSL_PATH, xargs...)
|
|
|
|
// 捕获标准输出和标准错误
|
|
var stdout, stderr bytes.Buffer
|
|
xcmd.Stdout = &stdout
|
|
xcmd.Stderr = &stderr
|
|
|
|
// 执行命令
|
|
err := xcmd.Run()
|
|
if err != nil {
|
|
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// 检查证书是否在黑名单中
|
|
func CheckCertInCrl(certFilePath string) (bool, error) {
|
|
// 检查证书是否在黑名单中
|
|
// openssl verify -CAfile /opt/arrokoth/ca-mini/ca/CaRoot.crt -CRLfile /opt/arrokoth/ca-mini/crl/CaRoot.crl /opt/arrokoth/ca-mini/cert/17455454097526357682Q7pIOUE70I.crt
|
|
// openssl crl -in crl.pem -noout -text
|
|
xargs := []string{
|
|
"crl",
|
|
"-in", certFilePath,
|
|
"-noout",
|
|
"-text",
|
|
}
|
|
|
|
// 创建命令
|
|
xcmd := exec.Command(OPENSSL_PATH, xargs...)
|
|
|
|
// 捕获标准输出和标准错误
|
|
var stdout, stderr bytes.Buffer
|
|
xcmd.Stdout = &stdout
|
|
xcmd.Stderr = &stderr
|
|
|
|
// 执行命令
|
|
err := xcmd.Run()
|
|
if err != nil {
|
|
return false, fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// 读取文件内容
|
|
func ReadFile(filePath string) (string, error) {
|
|
data, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("读取文件失败: %v", err)
|
|
}
|
|
// 将数据转换为字符串
|
|
content := string(data)
|
|
return content, nil
|
|
}
|
|
|
|
// 读取CA证书
|
|
func GetCaCert() (string, error) {
|
|
data, err := ReadFile(CA_CERT)
|
|
if err != nil {
|
|
return "", fmt.Errorf("读取CA证书失败: %v", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// 读取证书
|
|
func GetCert(id string) (string, error) {
|
|
data, err := ReadFile(GetCertFilePath(id))
|
|
if err != nil {
|
|
return "", fmt.Errorf("读取证书失败: %v", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// 读取私钥
|
|
func GetKey(id string) (string, error) {
|
|
data, err := ReadFile(GetKeyFilePath(id))
|
|
if err != nil {
|
|
return "", fmt.Errorf("读取私钥失败: %v", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// 读取证书请求
|
|
func GetCsr(id string) (string, error) {
|
|
data, err := ReadFile(GetCsrFilePath(id))
|
|
if err != nil {
|
|
return "", fmt.Errorf("读取证书请求失败: %v", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// joinSans 拼接 SAN 字段,例如 DNS:example.com, IP:192.168.0.1
|
|
func joinSans(sans []string) string {
|
|
result := ""
|
|
for i, san := range sans {
|
|
if i > 0 {
|
|
result += ", "
|
|
}
|
|
result += san
|
|
}
|
|
return result
|
|
}
|