154 lines
3.8 KiB
Go
154 lines
3.8 KiB
Go
package cryptoutil
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
// RSAKeyPair RSA密钥对结构体
|
|
type RSAKeyPair struct {
|
|
n *big.Int // 模数
|
|
e *big.Int // 公钥指数
|
|
}
|
|
|
|
// NewRSAKeyPair 创建RSA密钥对
|
|
// exponent: 公钥指数的十六进制字符串,通常是 "10001" (65537)
|
|
// privateExponent: 私钥指数的十六进制字符串,加密时可以为空
|
|
// modulus: 模数的十六进制字符串
|
|
func NewRSAKeyPair(exponent, privateExponent, modulus string) (*RSAKeyPair, error) {
|
|
// 解析公钥指数
|
|
e := new(big.Int)
|
|
if _, ok := e.SetString(exponent, 16); !ok {
|
|
return nil, fmt.Errorf("invalid exponent: %s", exponent)
|
|
}
|
|
|
|
// 解析模数
|
|
n := new(big.Int)
|
|
if _, ok := n.SetString(modulus, 16); !ok {
|
|
return nil, fmt.Errorf("invalid modulus: %s", modulus)
|
|
}
|
|
|
|
return &RSAKeyPair{
|
|
n: n,
|
|
e: e,
|
|
}, nil
|
|
}
|
|
|
|
// NewRSAKeyPairFromPublicKey 从 X.509 格式的 base64 公钥字符串创建 RSA 密钥对
|
|
// publicKeyBase64: X.509 DER 格式的 base64 编码公钥字符串
|
|
func NewRSAKeyPairFromPublicKey(publicKeyBase64 string) (*RSAKeyPair, error) {
|
|
// 解码 base64
|
|
derBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode base64: %w", err)
|
|
}
|
|
|
|
// 解析 X.509 格式的公钥
|
|
pubKey, err := x509.ParsePKIXPublicKey(derBytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse public key: %w", err)
|
|
}
|
|
|
|
// 转换为 RSA 公钥
|
|
rsaPubKey, ok := pubKey.(*rsa.PublicKey)
|
|
if !ok {
|
|
return nil, fmt.Errorf("not an RSA public key")
|
|
}
|
|
|
|
// 创建密钥对
|
|
return &RSAKeyPair{
|
|
n: rsaPubKey.N,
|
|
e: big.NewInt(int64(rsaPubKey.E)),
|
|
}, nil
|
|
}
|
|
|
|
// Encrypt 使用公钥加密数据,返回十六进制字符串
|
|
func (k *RSAKeyPair) Encrypt(plaintext string) (string, error) {
|
|
ciphertext, err := k.EncryptBytes([]byte(plaintext))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// 返回十六进制编码的密文
|
|
return hex.EncodeToString(ciphertext), nil
|
|
}
|
|
|
|
// EncryptBytes 使用公钥加密字节数据,返回加密后的字节
|
|
// 使用 PKCS1v15 填充方式,兼容 JavaScript RSA 实现
|
|
func (k *RSAKeyPair) EncryptBytes(plaintext []byte) ([]byte, error) {
|
|
// 计算密钥长度(字节)
|
|
keySize := (k.n.BitLen() + 7) / 8
|
|
|
|
// PKCS1v15 填充
|
|
padded, err := pkcs1v15Pad(plaintext, keySize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 将填充后的数据转换为大整数
|
|
m := new(big.Int).SetBytes(padded)
|
|
|
|
// 执行 RSA 加密: c = m^e mod n
|
|
c := new(big.Int).Exp(m, k.e, k.n)
|
|
|
|
// 将结果转换为字节数组,确保长度与密钥长度一致
|
|
ciphertext := c.Bytes()
|
|
if len(ciphertext) < keySize {
|
|
// 如果结果长度不足,在前面填充零
|
|
result := make([]byte, keySize)
|
|
copy(result[keySize-len(ciphertext):], ciphertext)
|
|
return result, nil
|
|
}
|
|
|
|
return ciphertext, nil
|
|
}
|
|
|
|
// pkcs1v15Pad 实现 PKCS#1 v1.5 填充
|
|
func pkcs1v15Pad(data []byte, keySize int) ([]byte, error) {
|
|
if len(data) > keySize-11 {
|
|
return nil, fmt.Errorf("data too long: %d bytes (max %d)", len(data), keySize-11)
|
|
}
|
|
|
|
// PKCS1v15 格式: 0x00 || 0x02 || PS || 0x00 || data
|
|
// 其中 PS 是随机非零字节,长度至少为 8 字节
|
|
padLen := keySize - len(data) - 3
|
|
|
|
padded := make([]byte, keySize)
|
|
padded[0] = 0x00
|
|
padded[1] = 0x02
|
|
|
|
// 生成随机非零字节作为填充
|
|
ps := make([]byte, padLen)
|
|
_, err := rand.Read(ps)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate random padding: %w", err)
|
|
}
|
|
|
|
// 确保所有填充字节都是非零
|
|
for i := 0; i < padLen; i++ {
|
|
for ps[i] == 0 {
|
|
rand.Read(ps[i : i+1])
|
|
}
|
|
padded[2+i] = ps[i]
|
|
}
|
|
|
|
padded[2+padLen] = 0x00
|
|
copy(padded[3+padLen:], data)
|
|
|
|
return padded, nil
|
|
}
|
|
|
|
// GetModulus 获取模数
|
|
func (k *RSAKeyPair) GetModulus() *big.Int {
|
|
return k.n
|
|
}
|
|
|
|
// GetExponent 获取公钥指数
|
|
func (k *RSAKeyPair) GetExponent() *big.Int {
|
|
return k.e
|
|
}
|