crypto.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package utils
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "crypto/rand"
  6. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "os"
  11. )
  12. // 获取加密密钥(优先从环境变量读取,否则使用默认值)
  13. // 重要说明:
  14. // 1. 这个密钥是固定的,用于加密/解密所有 API Key
  15. // 2. API Key 的长度可以不同,但都用同一个加密密钥加密
  16. // 3. 如果改变这个密钥,之前加密的 API Key 将无法解密
  17. // 4. 生产环境必须从环境变量 ENCRYPTION_KEY 读取
  18. // AES-256 需要 32 字节的密钥
  19. func getEncryptionKey() []byte {
  20. key := os.Getenv("ENCRYPTION_KEY")
  21. if key != "" && len(key) == 32 {
  22. return []byte(key)
  23. }
  24. // 默认密钥(仅用于开发环境,生产环境必须设置 ENCRYPTION_KEY)
  25. return []byte("abcdefghijklmnopqrstuvwxyz123456") // 32 bytes for AES-256
  26. }
  27. // EncryptAPIKey 加密 API Key
  28. // 参数:
  29. // - plaintext: 用户输入的 API Key(长度可变,不同服务商不同)
  30. //
  31. // 返回:
  32. // - 加密后的字符串(base64 编码)
  33. //
  34. // 说明:
  35. // - 无论 API Key 多长,都用同一个固定的 encryptionKey 加密
  36. // - 加密密钥(encryptionKey)是固定的,不会因为 API Key 长度变化而改变
  37. func EncryptAPIKey(plaintext string) (string, error) {
  38. // 验证输入
  39. if plaintext == "" {
  40. return "", errors.New("API Key 不能为空")
  41. }
  42. // 获取加密密钥(优先从环境变量读取)
  43. key := getEncryptionKey()
  44. if len(key) != 32 {
  45. return "", errors.New("加密密钥长度必须为 32 字节")
  46. }
  47. // 创建 AES cipher
  48. block, err := aes.NewCipher(key)
  49. if err != nil {
  50. return "", fmt.Errorf("创建加密器失败: %v", err)
  51. }
  52. // 创建 GCM(Galois/Counter Mode)
  53. gcm, err := cipher.NewGCM(block)
  54. if err != nil {
  55. return "", fmt.Errorf("创建 GCM 失败: %v", err)
  56. }
  57. // 生成随机 nonce
  58. nonce := make([]byte, gcm.NonceSize())
  59. if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
  60. return "", fmt.Errorf("生成随机数失败: %v", err)
  61. }
  62. // 加密数据
  63. ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
  64. // 返回 base64 编码的密文
  65. return base64.StdEncoding.EncodeToString(ciphertext), nil
  66. }
  67. // DecryptAPIKey 解密 API Key
  68. func DecryptAPIKey(ciphertext string) (string, error) {
  69. // 获取加密密钥(优先从环境变量读取)
  70. key := getEncryptionKey()
  71. if len(key) != 32 {
  72. return "", errors.New("加密密钥长度必须为 32 字节")
  73. }
  74. // 解码 base64
  75. data, err := base64.StdEncoding.DecodeString(ciphertext)
  76. if err != nil {
  77. return "", err
  78. }
  79. // 创建 AES cipher
  80. block, err := aes.NewCipher(key)
  81. if err != nil {
  82. return "", err
  83. }
  84. // 创建 GCM
  85. gcm, err := cipher.NewGCM(block)
  86. if err != nil {
  87. return "", err
  88. }
  89. // 检查数据长度
  90. if len(data) < gcm.NonceSize() {
  91. return "", errors.New("ciphertext too short")
  92. }
  93. // 提取 nonce 和密文
  94. nonce, ciphertextBytes := data[:gcm.NonceSize()], data[gcm.NonceSize():]
  95. // 解密数据
  96. plaintext, err := gcm.Open(nil, nonce, ciphertextBytes, nil)
  97. if err != nil {
  98. return "", err
  99. }
  100. return string(plaintext), nil
  101. }