mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 07:41:10 +08:00
Add files via upload
This commit is contained in:
139
backend/public/cert.go
Normal file
139
backend/public/cert.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// **解析 PEM 格式的证书**
|
||||
func ParseCertificate(certPEM []byte) (*x509.Certificate, error) {
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("无法解析证书 PEM")
|
||||
}
|
||||
return x509.ParseCertificate(block.Bytes)
|
||||
}
|
||||
|
||||
// **解析 PEM 格式的私钥**
|
||||
func ParsePrivateKey(keyPEM []byte) (crypto.PrivateKey, error) {
|
||||
block, _ := pem.Decode(keyPEM)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("无法解析私钥 PEM")
|
||||
}
|
||||
|
||||
// **尝试解析不同类型的私钥**
|
||||
if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
return nil, fmt.Errorf("无法识别的私钥格式")
|
||||
}
|
||||
|
||||
// **检查证书是否过期**
|
||||
func CheckCertificateExpiration(cert *x509.Certificate) error {
|
||||
now := time.Now()
|
||||
if now.Before(cert.NotBefore) {
|
||||
return fmt.Errorf("证书尚未生效,有效期开始于: %v", cert.NotBefore)
|
||||
}
|
||||
if now.After(cert.NotAfter) {
|
||||
return fmt.Errorf("证书已过期,有效期截止到: %v", cert.NotAfter)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// **检查证书是否与私钥匹配**
|
||||
func VerifyCertificateAndKey(cert *x509.Certificate, privateKey crypto.PrivateKey) error {
|
||||
messageARR := sha256.Sum256([]byte("test message"))
|
||||
message := messageARR[:]
|
||||
var signature []byte
|
||||
var err error
|
||||
|
||||
// **用私钥签名数据**
|
||||
switch key := privateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
signature, err = rsa.SignPKCS1v15(nil, key, crypto.SHA256, message)
|
||||
case *ecdsa.PrivateKey:
|
||||
signature, err = key.Sign(nil, message, crypto.SHA256)
|
||||
case ed25519.PrivateKey:
|
||||
signature = ed25519.Sign(key, message)
|
||||
default:
|
||||
err = errors.New("不支持的私钥类型")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("签名失败: %v", err)
|
||||
}
|
||||
|
||||
// **使用公钥验证签名**
|
||||
switch pub := cert.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, message, signature)
|
||||
case *ecdsa.PublicKey:
|
||||
ok := ecdsa.VerifyASN1(pub, message, signature)
|
||||
if !ok {
|
||||
err = fmt.Errorf("ECDSA 签名验证失败")
|
||||
}
|
||||
case ed25519.PublicKey:
|
||||
if !ed25519.Verify(pub, message, signature) {
|
||||
err = fmt.Errorf("Ed25519 签名验证失败")
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("不支持的公钥类型: %T", pub)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// **主验证函数**
|
||||
func ValidateSSLCertificate(certStr, keyStr string) error {
|
||||
certPEM, keyPEM := []byte(certStr), []byte(keyStr)
|
||||
// **解析证书和私钥**
|
||||
cert, err := ParseCertificate(certPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析证书失败: %v", err)
|
||||
}
|
||||
privateKey, err := ParsePrivateKey(keyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析私钥失败: %v", err)
|
||||
}
|
||||
|
||||
// **检查证书有效期**
|
||||
if err := CheckCertificateExpiration(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// **检查证书和私钥是否匹配**
|
||||
if err := VerifyCertificateAndKey(cert, privateKey); err != nil {
|
||||
return fmt.Errorf("证书与私钥不匹配: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取sha256
|
||||
func GetSHA256(certStr string) (string, error) {
|
||||
certPEM := []byte(certStr)
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return "", fmt.Errorf("无法解析证书 PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解析证书失败: %v", err)
|
||||
}
|
||||
|
||||
sha256Hash := sha256.Sum256(cert.Raw)
|
||||
return hex.EncodeToString(sha256Hash[:]), nil
|
||||
}
|
||||
44
backend/public/config.go
Normal file
44
backend/public/config.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package public
|
||||
|
||||
import "strconv"
|
||||
|
||||
var Port = GetSettingIgnoreError("port")
|
||||
var Secure = GetSettingIgnoreError("secure")
|
||||
var SessionKey = GetSettingIgnoreError("session_key")
|
||||
var LogPath = GetSettingIgnoreError("log_path")
|
||||
var TimeOut = func() int {
|
||||
settingStr := GetSettingIgnoreError("timeout")
|
||||
setting, err := strconv.Atoi(settingStr)
|
||||
if err != nil {
|
||||
return 300
|
||||
}
|
||||
return setting
|
||||
}()
|
||||
var ShutdownFunc func()
|
||||
|
||||
func ReloadConfig() {
|
||||
Port = GetSettingIgnoreError("port")
|
||||
Secure = GetSettingIgnoreError("secure")
|
||||
SessionKey = GetSettingIgnoreError("session_key")
|
||||
LogPath = GetSettingIgnoreError("log_path")
|
||||
|
||||
settingStr := GetSettingIgnoreError("timeout")
|
||||
setting, err := strconv.Atoi(settingStr)
|
||||
if err != nil {
|
||||
TimeOut = 300
|
||||
} else {
|
||||
TimeOut = setting
|
||||
}
|
||||
ShutdownFunc = nil
|
||||
|
||||
}
|
||||
|
||||
// OpLog 操作日志
|
||||
type OpLog struct {
|
||||
OpType string `db:"op_type"`
|
||||
OpUser string `db:"op_user"`
|
||||
OpTime string `db:"op_time"`
|
||||
OpDetail string `db:"op_detail"`
|
||||
OpResult string `db:"op_result"`
|
||||
IP string `db:"ip"`
|
||||
}
|
||||
112
backend/public/logger.go
Normal file
112
backend/public/logger.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
infoLogger *log.Logger
|
||||
errorLogger *log.Logger
|
||||
warnLogger *log.Logger
|
||||
logFile *os.File
|
||||
)
|
||||
|
||||
// InitLogger 初始化日志器(仅写入文件)
|
||||
func InitLogger(logPath string) {
|
||||
// 确保日志目录存在
|
||||
dir := filepath.Dir(logPath)
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
panic("创建日志目录失败: " + err.Error())
|
||||
}
|
||||
|
||||
var err error
|
||||
logFile, err = os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
panic("无法打开日志文件: " + err.Error())
|
||||
}
|
||||
|
||||
infoLogger = log.New(logFile, "[INFO] ", log.LstdFlags|log.Lshortfile)
|
||||
errorLogger = log.New(logFile, "[ERROR] ", log.LstdFlags|log.Lshortfile)
|
||||
warnLogger = log.New(logFile, "[WARN] ", log.LstdFlags|log.Lshortfile)
|
||||
}
|
||||
|
||||
// Info 输出 Info 级别日志
|
||||
func Info(msg string) {
|
||||
infoLogger.Println(msg)
|
||||
}
|
||||
|
||||
// Error 输出 Error 级别日志
|
||||
func Error(msg string) {
|
||||
errorLogger.Println(msg)
|
||||
}
|
||||
|
||||
// Warn 输出 Warn 级别日志
|
||||
func Warn(msg string) {
|
||||
warnLogger.Println(msg)
|
||||
}
|
||||
|
||||
// CloseLogger 关闭日志文件(建议在程序退出前调用)
|
||||
func CloseLogger() {
|
||||
if logFile != nil {
|
||||
logFile.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
filePath string
|
||||
logger *log.Logger
|
||||
file *os.File
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewLogger(filePath string) (*Logger, error) {
|
||||
// 确保日志目录存在
|
||||
dir := filepath.Dir(filePath)
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
panic("创建日志目录失败: " + err.Error())
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := log.New(file, "", 0) // 不设置前缀,我们手动格式化
|
||||
return &Logger{
|
||||
filePath: filePath,
|
||||
logger: l,
|
||||
file: file,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close 关闭日志文件
|
||||
func (l *Logger) Close() {
|
||||
l.file.Close()
|
||||
}
|
||||
|
||||
// write 写日志,内部使用锁保证线程安全
|
||||
func (l *Logger) write(level string, msg string) {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||
logLine := "[" + level + "] " + timestamp + " - " + msg
|
||||
l.logger.Println(logLine)
|
||||
}
|
||||
|
||||
// Info 输出 info 级别日志
|
||||
func (l *Logger) Info(msg string) {
|
||||
l.write("INFO", msg)
|
||||
}
|
||||
|
||||
// Error 输出 error 级别日志
|
||||
func (l *Logger) Error(msg string) {
|
||||
l.write("ERROR", msg)
|
||||
}
|
||||
func (l *Logger) Debug(msg string) {
|
||||
l.write("Debug", msg)
|
||||
}
|
||||
44
backend/public/response.go
Normal file
44
backend/public/response.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package public
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Count int `json:"count"`
|
||||
Data any `json:"data"`
|
||||
Message string `json:"message"`
|
||||
Status bool `json:"status"`
|
||||
}
|
||||
|
||||
func SuccessMsg(c *gin.Context, msg string) {
|
||||
c.JSON(200, Response{
|
||||
Code: 200,
|
||||
Count: 0,
|
||||
Data: nil,
|
||||
Message: msg,
|
||||
Status: true,
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func SuccessData(c *gin.Context, data interface{}, count int) {
|
||||
c.JSON(200, Response{
|
||||
Code: 200,
|
||||
Count: count,
|
||||
Data: data,
|
||||
Message: "success",
|
||||
Status: true,
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func FailMsg(c *gin.Context, msg string) {
|
||||
c.JSON(500, Response{
|
||||
Code: 500,
|
||||
Count: 0,
|
||||
Data: nil,
|
||||
Message: msg,
|
||||
Status: false,
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
1094
backend/public/sqlite.go
Normal file
1094
backend/public/sqlite.go
Normal file
File diff suppressed because it is too large
Load Diff
181
backend/public/utils.go
Normal file
181
backend/public/utils.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// GetSettingIgnoreError 获取系统配置-忽略错误
|
||||
func GetSettingIgnoreError(key string) string {
|
||||
s, err := NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
res, err := s.Where("key=?", []interface{}{key}).Select()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return ""
|
||||
}
|
||||
setting, ok := res[0]["value"].(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return setting
|
||||
}
|
||||
|
||||
func UpdateSetting(key, val string) error {
|
||||
s, err := NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
_, err = s.Where("key=?", []interface{}{key}).Update(map[string]any{"value": val})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSettingsFromType(typ string) ([]map[string]any, error) {
|
||||
db := "data/data.db"
|
||||
s, err := NewSqlite(db, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Connect()
|
||||
defer s.Close()
|
||||
s.TableName = "settings"
|
||||
res, err := s.Where("type=?", []interface{}{typ}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// GetFreePort 获取一个可用的随机端口
|
||||
func GetFreePort() (int, error) {
|
||||
// 端口为 0,表示让系统自动分配一个可用端口
|
||||
ln, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
addr := ln.Addr().String()
|
||||
// 提取端口号
|
||||
parts := strings.Split(addr, ":")
|
||||
if len(parts) < 2 {
|
||||
return 0, fmt.Errorf("invalid address: %s", addr)
|
||||
}
|
||||
|
||||
var port int
|
||||
fmt.Sscanf(parts[len(parts)-1], "%d", &port)
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// RandomString 生成指定长度的随机字符串
|
||||
func RandomString(length int) string {
|
||||
if str, err := RandomStringWithCharset(length, defaultCharset); err != nil {
|
||||
return "allinssl"
|
||||
} else {
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
// RandomStringWithCharset 使用指定字符集生成随机字符串
|
||||
func RandomStringWithCharset(length int, charset string) (string, error) {
|
||||
result := make([]byte, length)
|
||||
charsetLen := big.NewInt(int64(len(charset)))
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
num, err := rand.Int(rand.Reader, charsetLen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
// GenerateUUID 生成 UUID
|
||||
func GenerateUUID() string {
|
||||
// 生成一个新的 UUID
|
||||
uuidStr := strings.ReplaceAll(uuid.New().String(), "-", "")
|
||||
|
||||
// 返回 UUID 的字符串表示
|
||||
return uuidStr
|
||||
}
|
||||
|
||||
func GetLocalIP() (string, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // 接口未启用
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // 忽略回环地址
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
|
||||
// 只返回 IPv4 内网地址
|
||||
if ip != nil && ip.To4() != nil && !ip.IsLoopback() {
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("没有找到内网 IP")
|
||||
}
|
||||
|
||||
func GetPublicIP() (string, error) {
|
||||
resp, err := http.Get("https://www.bt.cn/Api/getIpAddress")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("HTTP状态错误: %v", resp.Status)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
35
backend/public/validate_code.go
Normal file
35
backend/public/validate_code.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
var codeDefaultDriver = base64Captcha.NewDriverString(
|
||||
1000,
|
||||
1200,
|
||||
0,
|
||||
base64Captcha.OptionShowSlimeLine,
|
||||
4,
|
||||
"23456789abcdefghjkmnpqrstuvwxyz",
|
||||
&color.RGBA{
|
||||
R: 225,
|
||||
G: 225,
|
||||
B: 200,
|
||||
A: 255,
|
||||
},
|
||||
nil,
|
||||
[]string{"wqy-microhei.ttc", "RitaSmith.ttf"},
|
||||
)
|
||||
|
||||
// GenerateCode 生成图形化字符串验证码
|
||||
func GenerateCode() (string, string, string, error) {
|
||||
// 生成默认数字的driver
|
||||
codeId, content, _ := codeDefaultDriver.GenerateIdQuestionAnswer() // 生成验证码和随机id
|
||||
item, err := codeDefaultDriver.DrawCaptcha(content) // 生成验证码图片
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
b64s := item.EncodeB64string()
|
||||
return codeId, b64s, content, nil
|
||||
}
|
||||
Reference in New Issue
Block a user