mirror of https://github.com/allinssl/allinssl
429 lines
11 KiB
Go
429 lines
11 KiB
Go
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
|
||
}
|