allinssl/backend/internal/private_ca/private_ca.go

429 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package private_ca
import (
"ALLinSSL/backend/public"
"encoding/json"
"fmt"
"time"
)
func CreateRootCA(name, commonName, organization, organizationalUnit, country, province, locality, keyType string, keyBits, validDays int64) error {
var err error
var data *CAConfig
if keyType == "sm2" {
// 国密SM2根证书 - 生成签名和加密双证书, 使用不同私钥
data, err = GenerateRootCASM2(name, commonName, organization, organizationalUnit, country, province, locality, int(validDays))
} else {
// 标准根证书 - 生成单一证书
data, err = GenerateRootCAStandard(name, commonName, organization, organizationalUnit, country, province, locality, KeyType(keyType), int(keyBits), int(validDays))
}
if err != nil {
return err
}
// 保存到数据库
s, err := GetSqlite()
if err != nil {
return err
}
defer s.Close()
_, err = s.Insert(public.StructToMap(data, true))
if err != nil {
return err
}
return nil
}
func CreateIntermediateCA(name, commonName, organization, organizationalUnit, country, province, locality string, rootId, keyBits, validDays int64) error {
s, err := GetSqlite()
if err != nil {
return err
}
defer s.Close()
issuers, err := s.Where("id=?", []interface{}{rootId}).Select()
if err != nil {
return err
}
if len(issuers) == 0 {
return fmt.Errorf("issuer with id %d not found", rootId)
}
issuer := issuers[0]
keyType := issuer["algorithm"].(string)
cert := issuer["cert"].(string)
key := issuer["key"].(string)
var data *CAConfig
if keyType == "sm2" {
// 国密SM2中级证书 - 生成签名和加密双证书, 使用不同私钥
enCert := issuer["en_cert"].(string)
enKey := issuer["en_key"].(string)
data, err = GenerateIntermediateCASM2(name, commonName, organization, organizationalUnit, country, province, locality, cert, key, enCert, enKey, int(validDays))
} else {
// 标准中级证书 - 生成单一证书
data, err = GenerateIntermediateCAStandard(name, commonName, organization, organizationalUnit, country, province, locality, cert, key, keyType, int(keyBits), int(validDays))
}
if err != nil {
return err
}
// 保存到数据库
insertData := public.StructToMap(data, true)
insertData["root_id"] = rootId
_, err = s.Insert(insertData)
if err != nil {
return err
}
return nil
}
func DeleteCA(id int64) error {
s, err := GetSqlite()
if err != nil {
return err
}
defer s.Close()
// 检查是否有子证书
children, err := s.Where("root_id=?", []interface{}{id}).Select()
if err != nil {
return err
}
if len(children) > 0 {
return fmt.Errorf("cannot delete CA with id %d: it has child CAs", id)
}
_, err = s.Where("id=?", []interface{}{id}).Delete()
if err != nil {
return err
}
return nil
}
func ListCAs(search, level string, p, limit int64) ([]map[string]interface{}, int, error) {
s, err := GetSqlite()
if err != nil {
return nil, 0, err
}
defer s.Close()
var data []map[string]any
var count int64
var limits []int64
if p >= 0 && limit >= 0 {
limits = []int64{0, limit}
if p > 1 {
limits[0] = (p - 1) * limit
limits[1] = limit
}
}
whereStr := "1=1"
var params []interface{}
if search != "" {
whereStr += " and (name like ? or cn like ?)"
params = append(params, "%"+search+"%", "%"+search+"%")
}
if level == "root" {
whereStr += " and root_id is null"
}
if level == "intermediate" {
whereStr += " and root_id is not null"
}
data, err = s.Where(whereStr, params).Limit(limits).Order("create_time", "desc").Select()
count, err = s.Where(whereStr, params).Limit(limits).Count()
if err != nil {
return data, int(count), err
}
return data, int(count), nil
}
func CreateLeafCert(caId, usage, keyBits, validDays int64, cn, san string) (*LeafCertConfig, error) {
if caId <= 0 {
return nil, fmt.Errorf("CA ID不能为空")
}
if san == "" {
return nil, fmt.Errorf("备用名称不能为空")
}
var sans SAN
err := json.Unmarshal([]byte(san), &sans)
if err != nil {
return nil, fmt.Errorf("备用名称格式错误: %v", err)
}
if cn == "" {
if len(sans.DNSNames) > 0 {
cn = sans.DNSNames[0]
} else if len(sans.IPAddresses) > 0 {
cn = string(sans.IPAddresses[0])
} else if len(sans.EmailAddresses) > 0 {
cn = sans.EmailAddresses[0]
} else {
return nil, fmt.Errorf("CN和SAN不能为空")
}
}
s, err := GetSqlite()
if err != nil {
return nil, err
}
defer s.Close()
issuers, err := s.Where("id=?", []interface{}{caId}).Select()
if err != nil {
return nil, err
}
if len(issuers) == 0 {
return nil, fmt.Errorf("issuer with id %d not found", caId)
}
issuer := issuers[0]
if issuer["root_id"] == "" || issuer["root_id"] == nil {
return nil, fmt.Errorf("不允许使用根证书直接签发叶子证书,请先创建中间证书")
}
keyType := issuer["algorithm"].(string)
cert := issuer["cert"].(string)
key := issuer["key"].(string)
var issuerObj *Certificate
if keyType == "sm2" {
enCert := issuer["en_cert"].(string)
enKey := issuer["en_key"].(string)
issuerObj, err = NewCertificateFromPEMSM2([]byte(cert), []byte(key), []byte(enCert), []byte(enKey))
} else {
issuerObj, err = NewCertificateFromPEMStandard([]byte(cert), []byte(key), KeyType(keyType))
}
leafObj, err := GenerateLeafCertificate(cn, sans, issuerObj, KeyType(keyType), int(usage), int(keyBits), int(validDays))
if err != nil {
return nil, err
}
s.TableName = "leaf"
// 保存到数据库
leafObj.SAN = san
leafObj.CaId = caId
insertData := public.StructToMap(leafObj, true)
_, err = s.Insert(insertData)
if err != nil {
return nil, err
}
return leafObj, nil
}
func ListLeafCerts(caId int64, search string, p, limit int64) ([]map[string]interface{}, int, error) {
s, err := GetSqlite()
if err != nil {
return nil, 0, err
}
defer s.Close()
s.TableName = "leaf"
var data []map[string]any
var count int64
var limits []int64
sql := `
select leaf.*, ca.name as ca_name, ca.cn as ca_cn
from leaf
left join ca on leaf.ca_id = ca.id
where 1=1
`
// 拼接查询条件
var params []interface{}
if caId > 0 {
sql += " and leaf.ca_id = ?"
params = append(params, caId)
}
if search != "" {
sql += " and (leaf.cn like ? or leaf.san like ?)"
params = append(params, "%"+search+"%", "%"+search+"%")
}
sql += " order by leaf.create_time desc"
sqlCount := "select count(id) as count from (" + sql + ")"
if p > 0 && limit > 0 {
limits = []int64{0, limit}
if p > 1 {
limits[0] = (p - 1) * limit
limits[1] = limit
}
sql += fmt.Sprintf(" limit %d offset %d", limits[1], limits[0])
}
data, err = s.Query(sql, params...)
if err != nil {
return data, 0, err
}
countResult, err := s.Query(sqlCount, params...)
if err != nil {
return data, 0, err
}
if len(countResult) > 0 {
count = countResult[0]["count"].(int64)
}
return data, int(count), nil
}
func DeleteLeafCert(id int64) error {
s, err := GetSqlite()
if err != nil {
return err
}
defer s.Close()
s.TableName = "leaf"
_, err = s.Where("id=?", []interface{}{id}).Delete()
if err != nil {
return err
}
return nil
}
func GetCert(id int64, certType string) (map[string]any, error) {
s, err := GetSqlite()
if err != nil {
return nil, err
}
defer s.Close()
s.TableName = certType
leafs, err := s.Where("id=?", []interface{}{id}).Select()
if err != nil {
return nil, err
}
if len(leafs) == 0 {
return nil, fmt.Errorf("leaf cert with id %d not found", id)
}
return leafs[0], nil
}
func WorkflowCreateLeafCert(params map[string]any, logger *public.Logger) (map[string]any, error) {
caId, ok := params["ca_id"].(float64)
if !ok || caId <= 0 {
return nil, fmt.Errorf("ca_id参数错误")
}
keyBits, ok := params["key_length"].(float64)
if !ok {
return nil, fmt.Errorf("key_length参数错误")
}
validDays, ok := params["valid_days"].(float64)
if !ok {
return nil, fmt.Errorf("valid_days参数错误")
}
endDay, ok := params["end_day"].(float64)
if !ok {
endDay = 0
}
cn, ok := params["cn"].(string)
if !ok {
cn = ""
}
san, ok := params["san"].(string)
if !ok || san == "" {
return nil, fmt.Errorf("san参数错误")
}
// 先获取ca信息确认是中间证书且不能是国密
s, err := GetSqlite()
if err != nil {
return nil, err
}
defer s.Close()
issuers, err := s.Where("id=?", []interface{}{caId}).Select()
if err != nil {
return nil, err
}
if len(issuers) == 0 {
return nil, fmt.Errorf("issuer with id %d not found", caId)
}
issuer := issuers[0]
if issuer["root_id"] == "" || issuer["root_id"] == nil {
return nil, fmt.Errorf("不允许使用根证书直接签发叶子证书,请先创建中间证书")
}
keyType := issuer["algorithm"].(string)
if keyType == "sm2" {
return nil, fmt.Errorf("暂不兼容国密证书,请使用标准证书")
}
// 判断中间证书不能为已过期,过期时间不能超过中间证书
caNotAfter, err := time.Parse("2006-01-02 15:04:05", issuer["not_after"].(string))
if err != nil {
return nil, fmt.Errorf("解析中间证书过期时间失败: %v", err)
}
maxValidDays := caNotAfter.Sub(time.Now()).Hours() / 24
if maxValidDays <= 0 {
return nil, fmt.Errorf("中间证书已过期,不能签发叶子证书")
}
if validDays > maxValidDays {
return nil, fmt.Errorf("叶子证书的有效期不能超过中间证书的有效期,中间证书将在 %s 过期,距离今天还有 %d 天", caNotAfter.Format("2006-01-02"), int(maxValidDays))
}
// 解析san判断数据库中是否已存在相同的cn和san
var sans SAN
err = json.Unmarshal([]byte(san), &sans)
if err != nil {
return nil, fmt.Errorf("备用名称格式错误: %v", err)
}
if cn == "" {
if len(sans.DNSNames) > 0 {
cn = sans.DNSNames[0]
} else {
cn = string(sans.IPAddresses[0])
}
}
s.TableName = "leaf"
// 获取所有未过期的证书判断是否有相同的cn和san
leafs, err := s.Where("ca_id=? and not_after>? and usage=1", []interface{}{caId, time.Now().Format("2006-01-02 15:04:05")}).Select()
if err != nil {
return nil, err
}
var certificate map[string]any
for _, v := range leafs {
// 判断剩余天数是否满足要求
if endDay > 0 {
notAfter, err := time.Parse("2006-01-02 15:04:05", v["not_after"].(string))
if err != nil {
continue
}
remainDays := notAfter.Sub(time.Now()).Hours() / 24
if remainDays < endDay {
continue
}
}
// 判断cn和san是否相同
if v["cn"] == cn {
var existingSAN SAN
err = json.Unmarshal([]byte(v["san"].(string)), &existingSAN)
if err != nil {
continue
}
if public.ContainsAllIgnoreBRepeats(existingSAN.DNSNames, sans.DNSNames) {
var existingIPs, newIPs []string
for _, ip := range existingSAN.IPAddresses {
existingIPs = append(existingIPs, ip.String())
}
for _, ip := range sans.IPAddresses {
newIPs = append(newIPs, ip.String())
}
if public.ContainsAllIgnoreBRepeats(existingIPs, newIPs) {
// 找到相同的证书,标记为跳过
logger.Debug("找到相同的证书,跳过创建", "id", v["id"])
certificate = map[string]any{
"cert": v["cert"],
"key": v["key"],
"skip": true,
}
break
}
}
}
}
if certificate == nil {
leaf, err := CreateLeafCert(int64(caId), 1, int64(keyBits), int64(validDays), cn, san)
if err != nil {
return nil, err
}
certificate = map[string]any{
"cert": leaf.Cert,
"key": leaf.Key,
}
}
return certificate, nil
}