allinssl/backend/internal/private_ca/ca.go

478 lines
14 KiB
Go

package private_ca
import (
"ALLinSSL/backend/public"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math/big"
"time"
"github.com/tjfoc/gmsm/sm2"
gmx509 "github.com/tjfoc/gmsm/x509"
)
func GetSqlite() (*public.Sqlite, error) {
s, err := public.NewSqlite("data/private_ca.db", "")
if err != nil {
return nil, err
}
s.TableName = "ca"
return s, nil
}
// ----------- 标准算法 Root CA -----------
func GenerateRootCAStandard(name, commonName, organization, organizationalUnit, country, province, locality string, keyType KeyType, keyBits, validDays int) (*CAConfig, error) {
if keyType == KeySM2 {
return nil, errors.New("use GenerateRootCASM2 for SM2")
}
priv, err := generatePrivateKey(keyType, keyBits)
if err != nil {
return nil, err
}
now := time.Now()
expire := now.AddDate(0, 0, validDays)
if validDays <= 0 {
expire = now.AddDate(10, 0, 0)
}
subject := pkix.Name{
// 通用名称
CommonName: commonName,
// 国家代码
Country: []string{country},
}
if organization != "" {
subject.Organization = []string{organization}
}
if organizationalUnit != "" {
subject.OrganizationalUnit = []string{organizationalUnit}
}
if province != "" {
subject.Province = []string{province}
}
if locality != "" {
subject.Locality = []string{locality}
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()),
Subject: subject,
NotBefore: now,
NotAfter: expire,
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
MaxPathLen: 2,
}
cert, err := signCert(tmpl, tmpl, priv, priv, keyType)
if err != nil {
return nil, err
}
cert.KeyType = keyType
return &CAConfig{
Name: name,
CN: commonName,
O: organization,
C: country,
Cert: string(cert.CertPEM),
Key: string(cert.KeyPEM),
Algorithm: string(keyType),
KeyLength: int64(keyBits),
NotBefore: now.Format("2006-01-02 15:04:05"),
NotAfter: expire.Format("2006-01-02 15:04:05"),
CreateTime: now.Format("2006-01-02 15:04:05"),
}, nil
}
// ----------- SM2 Root CA (双证书体系) -----------
func GenerateRootCASM2(name, commonName, organization, organizationalUnit, country, province, locality string, validDays int) (*CAConfig, error) {
// 1. 为签名和加密功能分别生成私钥
signPriv, err := generatePrivateKey(KeySM2, 0)
if err != nil {
return nil, fmt.Errorf("failed to generate SM2 root signing key: %w", err)
}
encryptPriv, err := generatePrivateKey(KeySM2, 0)
if err != nil {
return nil, fmt.Errorf("failed to generate SM2 root encryption key: %w", err)
}
now := time.Now()
expire := now.AddDate(0, 0, validDays)
if validDays <= 0 {
expire = now.AddDate(10, 0, 0)
}
subject := pkix.Name{
// 通用名称
CommonName: commonName,
// 国家代码
Country: []string{country},
}
if organization != "" {
subject.Organization = []string{organization}
}
if organizationalUnit != "" {
subject.OrganizationalUnit = []string{organizationalUnit}
}
if province != "" {
subject.Province = []string{province}
}
if locality != "" {
subject.Locality = []string{locality}
}
// 2. 创建根签名证书模板
signTmpl := &gmx509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()),
Subject: subject,
NotBefore: now,
NotAfter: expire,
IsCA: true,
KeyUsage: gmx509.KeyUsageCertSign | gmx509.KeyUsageCRLSign,
BasicConstraintsValid: true,
MaxPathLen: 2,
}
// 3. 创建根加密证书模板
encryptTmpl := &gmx509.Certificate{
SerialNumber: big.NewInt(now.UnixNano() + 1),
Subject: subject,
NotBefore: now,
NotAfter: expire,
IsCA: true,
KeyUsage: gmx509.KeyUsageKeyEncipherment | gmx509.KeyUsageDataEncipherment,
BasicConstraintsValid: true,
MaxPathLen: 2,
}
// 4. 自签名根签名证书 (使用自己的签名私钥)
signCert, err := signSM2Cert(signTmpl, signTmpl, signPriv.(*sm2.PrivateKey), signPriv.(*sm2.PrivateKey))
if err != nil {
return nil, fmt.Errorf("failed to self-sign SM2 root signing cert: %w", err)
}
// 5. 使用根签名证书签发根加密证书
encryptCert, err := signSM2Cert(encryptTmpl, signTmpl, encryptPriv.(*sm2.PrivateKey), signPriv.(*sm2.PrivateKey))
if err != nil {
return nil, fmt.Errorf("failed to sign SM2 root encryption cert: %w", err)
}
// 6. 组装返回结果
return &CAConfig{
Name: name,
CN: commonName,
O: organization,
C: country,
Cert: string(signCert.CertPEM),
Key: string(signCert.KeyPEM),
EnCert: string(encryptCert.CertPEM),
EnKey: string(encryptCert.KeyPEM),
Algorithm: "sm2",
KeyLength: 256,
NotBefore: now.Format("2006-01-02 15:04:05"),
NotAfter: expire.Format("2006-01-02 15:04:05"),
CreateTime: now.Format("2006-01-02 15:04:05"),
}, nil
}
// ----------- 标准算法 Intermediate CA -----------
func GenerateIntermediateCAStandard(name, commonName, organization, organizationalUnit, country, province, locality, cert, key, algorithm string, keyBits, validDays int) (*CAConfig, error) {
keyType := KeyType(algorithm)
issuer, err := NewCertificateFromPEMStandard([]byte(cert), []byte(key), keyType)
if err != nil {
return nil, fmt.Errorf("failed to parse issuer certificate: %w", err)
}
if issuer == nil {
return nil, errors.New("issuer is nil")
}
if issuer.KeyType != keyType {
return nil, fmt.Errorf("issuer and intermediate key type mismatch: %s != %s", issuer.KeyType, keyType)
}
if keyType == KeySM2 {
return nil, errors.New("use GenerateIntermediateCASM2 for SM2")
}
priv, err := generatePrivateKey(keyType, keyBits)
if err != nil {
return nil, err
}
now := time.Now()
expire := now.AddDate(0, 0, validDays)
if validDays <= 0 {
expire = now.AddDate(5, 0, 0)
}
subject := pkix.Name{
// 通用名称
CommonName: commonName,
// 国家代码
Country: []string{country},
}
if organization != "" {
subject.Organization = []string{organization}
}
if organizationalUnit != "" {
subject.OrganizationalUnit = []string{organizationalUnit}
}
if province != "" {
subject.Province = []string{province}
}
if locality != "" {
subject.Locality = []string{locality}
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()),
Subject: subject,
NotBefore: now,
NotAfter: expire,
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
MaxPathLen: 1,
}
certObj, err := signCert(tmpl, issuer.Cert, priv, issuer.Key, keyType)
if err != nil {
return nil, err
}
certObj.KeyType = keyType
return &CAConfig{
Name: name,
CN: commonName,
O: organization,
C: country,
Cert: string(certObj.CertPEM),
Key: string(certObj.KeyPEM),
Algorithm: string(keyType),
KeyLength: int64(keyBits),
NotBefore: now.Format("2006-01-02 15:04:05"),
NotAfter: expire.Format("2006-01-02 15:04:05"),
CreateTime: now.Format("2006-01-02 15:04:05"),
}, nil
}
// ----------- SM2 Intermediate CA (双证书体系) -----------
func GenerateIntermediateCASM2(name, commonName, organization, organizationalUnit, country, province, locality, cert, key, enCert, enKey string, validDays int) (*CAConfig, error) {
issuer, err := NewCertificateFromPEMSM2([]byte(cert), []byte(key), []byte(enCert), []byte(enKey))
if err != nil {
return nil, fmt.Errorf("failed to parse issuer certificate: %w", err)
}
if issuer == nil {
return nil, errors.New("issuer is nil")
}
if issuer.KeyType != KeySM2 || issuer.SignGmCert == nil {
return nil, errors.New("issuer must be a valid SM2 dual-certificate CA")
}
// 1. 为签名和加密功能分别生成私钥
signPriv, err := generatePrivateKey(KeySM2, 0)
if err != nil {
return nil, fmt.Errorf("failed to generate SM2 intermediate signing key: %w", err)
}
encryptPriv, err := generatePrivateKey(KeySM2, 0)
if err != nil {
return nil, fmt.Errorf("failed to generate SM2 intermediate encryption key: %w", err)
}
now := time.Now()
expire := now.AddDate(0, 0, validDays)
if validDays <= 0 {
expire = now.AddDate(5, 0, 0)
}
subject := pkix.Name{
// 通用名称
CommonName: commonName,
// 国家代码
Country: []string{country},
}
if organization != "" {
subject.Organization = []string{organization}
}
if organizationalUnit != "" {
subject.OrganizationalUnit = []string{organizationalUnit}
}
if province != "" {
subject.Province = []string{province}
}
if locality != "" {
subject.Locality = []string{locality}
}
// 2. 创建中间签名证书模板
signTmpl := &gmx509.Certificate{
SerialNumber: big.NewInt(now.UnixNano()),
Subject: subject,
NotBefore: now,
NotAfter: expire,
IsCA: true,
KeyUsage: gmx509.KeyUsageCertSign | gmx509.KeyUsageCRLSign,
BasicConstraintsValid: true,
MaxPathLen: 1,
}
// 3. 创建中间加密证书模板
encryptTmpl := &gmx509.Certificate{
SerialNumber: big.NewInt(now.UnixNano() + 1),
Subject: subject,
NotBefore: now,
NotAfter: expire,
IsCA: true,
KeyUsage: gmx509.KeyUsageKeyEncipherment | gmx509.KeyUsageDataEncipherment,
BasicConstraintsValid: true,
MaxPathLen: 1,
}
// 4. 使用颁发者的签名证书和私钥,签发中间签名证书
signCert, err := signSM2Cert(signTmpl, issuer.SignGmCert, signPriv.(*sm2.PrivateKey), issuer.Key.(*sm2.PrivateKey))
if err != nil {
return nil, fmt.Errorf("failed to sign SM2 intermediate signing cert: %w", err)
}
// 5. 使用颁发者的签名证书和私钥,签发中间加密证书
encryptCert, err := signSM2Cert(encryptTmpl, issuer.SignGmCert, encryptPriv.(*sm2.PrivateKey), issuer.Key.(*sm2.PrivateKey))
if err != nil {
return nil, fmt.Errorf("failed to sign SM2 intermediate encryption cert: %w", err)
}
// 6. 组装返回结果
return &CAConfig{
Name: name,
CN: commonName,
O: organization,
C: country,
Cert: string(signCert.CertPEM),
Key: string(signCert.KeyPEM),
EnCert: string(encryptCert.CertPEM),
EnKey: string(encryptCert.KeyPEM),
Algorithm: "sm2",
KeyLength: 256,
NotBefore: now.Format("2006-01-02 15:04:05"),
NotAfter: expire.Format("2006-01-02 15:04:05"),
CreateTime: now.Format("2006-01-02 15:04:05"),
}, nil
}
// NewCertificateFromPEMStandard 从PEM格式的标准算法证书和私钥创建Certificate对象
func NewCertificateFromPEMStandard(certPEM, keyPEM []byte, keyType KeyType) (*Certificate, error) {
if keyType == KeySM2 {
return nil, errors.New("use NewCertificateFromPEMSM2 for SM2 certificates")
}
certBlock, _ := pem.Decode(certPEM)
if certBlock == nil || certBlock.Type != "CERTIFICATE" {
return nil, errors.New("failed to decode certificate PEM")
}
keyBlock, _ := pem.Decode(keyPEM)
if keyBlock == nil {
return nil, errors.New("failed to decode key PEM")
}
cert := &Certificate{
CertPEM: certPEM,
KeyPEM: keyPEM,
KeyType: keyType,
}
var err error
cert.Cert, err = x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse standard certificate: %w", err)
}
switch keyType {
case KeyRSA:
cert.Key, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
pkcs8Key, err2 := x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
if err2 != nil {
return nil, fmt.Errorf("failed to parse rsa private key (PKCS1 and PKCS8): %w", err)
}
cert.Key = pkcs8Key
err = nil
}
case KeyECDSA:
cert.Key, err = x509.ParseECPrivateKey(keyBlock.Bytes)
if err != nil {
pkcs8Key, err2 := x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
if err2 != nil {
return nil, fmt.Errorf("failed to parse ecdsa private key (EC and PKCS8): %w", err)
}
cert.Key = pkcs8Key
err = nil
}
default:
return nil, fmt.Errorf("unsupported key type: %s", keyType)
}
return cert, nil
}
// NewCertificateFromPEMSM2 从PEM格式的国密双证书和双私钥创建Certificate对象
func NewCertificateFromPEMSM2(signCertPEM, signKeyPEM, encryptCertPEM, encryptKeyPEM []byte) (*Certificate, error) {
// 解析签名证书
signCertBlock, _ := pem.Decode(signCertPEM)
if signCertBlock == nil || signCertBlock.Type != "CERTIFICATE" {
return nil, errors.New("failed to decode signing certificate PEM")
}
signGmCert, err := gmx509.ParseCertificate(signCertBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse sm2 signing certificate: %w", err)
}
// 解析签名私钥
signKeyBlock, _ := pem.Decode(signKeyPEM)
if signKeyBlock == nil {
return nil, errors.New("failed to decode signing key PEM")
}
signKey, err := gmx509.ParsePKCS8PrivateKey(signKeyBlock.Bytes, nil)
if err != nil {
return nil, fmt.Errorf("failed to parse sm2 signing private key: %w", err)
}
// 解析加密证书
encryptCertBlock, _ := pem.Decode(encryptCertPEM)
if encryptCertBlock == nil || encryptCertBlock.Type != "CERTIFICATE" {
return nil, errors.New("failed to decode encryption certificate PEM")
}
encryptGmCert, err := gmx509.ParseCertificate(encryptCertBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse sm2 encryption certificate: %w", err)
}
// 解析加密私钥
encryptKeyBlock, _ := pem.Decode(encryptKeyPEM)
if encryptKeyBlock == nil {
return nil, errors.New("failed to decode encryption key PEM")
}
encryptKey, err := gmx509.ParsePKCS8PrivateKey(encryptKeyBlock.Bytes, nil)
if err != nil {
return nil, fmt.Errorf("failed to parse sm2 encryption private key: %w", err)
}
return &Certificate{
SignCertPEM: signCertPEM,
SignKeyPEM: signKeyPEM,
EncryptCertPEM: encryptCertPEM,
EncryptKeyPEM: encryptKeyPEM,
Key: signKey,
EncryptKey: encryptKey,
SignGmCert: signGmCert,
EncryptGmCert: encryptGmCert,
KeyType: KeySM2,
}, nil
}