diff --git a/README.md b/README.md index f865c15..07f8fad 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,12 @@ Golang 中可以使用的常用辅助功能工具箱。主要配备以下功能 - [x] No Padding - [x] ZerosPadding - [x] Pkcs7Padding - - [ ] RSA 加解密算法 - - [ ] 1024 位长 - - [ ] 2048 位长 - - [ ] KeyPair 生成器 - - [ ] RSA 签名算法 + - [x] RSA 加解密算法 + - [x] 1024 位长 + - [x] 2048 位长 + - [x] KeyPair 生成器 + - [x] Key 导入与导出 + - [x] RSA 签名算法 - 散列及校验和算法。 - [x] Sha512 散列算法 - [x] Sha256 散列算法 diff --git a/encryption/rsa/errors.go b/encryption/rsa/errors.go new file mode 100644 index 0000000..2c485d0 --- /dev/null +++ b/encryption/rsa/errors.go @@ -0,0 +1,67 @@ +package rsa + +import "fmt" + +type UnableToGenerateKeyPairError struct { + err error +} + +func (e *UnableToGenerateKeyPairError) Error() string { + return fmt.Sprintf("未能生成密钥对, %s", e.err) +} + +type AbsentPrivateKeyError struct{} + +func (e *AbsentPrivateKeyError) Error() string { + return "缺少私钥" +} + +type AbsentPublicKeyError struct{} + +func (e *AbsentPublicKeyError) Error() string { + return "缺少公钥" +} + +type EncryptionError struct { + err error +} + +func (e *EncryptionError) Error() string { + return fmt.Sprintf("加密失败, %s", e.err) +} + +type DecryptionError struct { + err error +} + +func (e *DecryptionError) Error() string { + return fmt.Sprintf("解密失败, %s", e.err) +} + +type KeyNotFoundError struct{} + +func (e *KeyNotFoundError) Error() string { + return "找不到密钥" +} + +type InvalidSignMethodError struct{} + +func (e *InvalidSignMethodError) Error() string { + return "无效的签名方法" +} + +type SignError struct { + err error +} + +func (e *SignError) Error() string { + return fmt.Sprintf("签名失败, %s", e.err) +} + +type VerifyError struct { + err error +} + +func (e *VerifyError) Error() string { + return fmt.Sprintf("验证失败, %s", e.err) +} diff --git a/encryption/rsa/key.go b/encryption/rsa/key.go new file mode 100644 index 0000000..22a43d1 --- /dev/null +++ b/encryption/rsa/key.go @@ -0,0 +1,141 @@ +package rsa + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" +) + +type KeyFormat int + +const ( + RSA KeyFormat = iota + COMMON +) + +// 将指定的私钥编码为PEM格式,如果不指定具体格式将使用RSA PRIVATE KEY(PKCS1)格式。 +func EncodePrivateKey(key *rsa.PrivateKey, format ...KeyFormat) ([]byte, error) { + var block *pem.Block + switch append(format, RSA)[0] { + case COMMON: + key, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + return nil, fmt.Errorf("不支持的私钥格式,%w", err) + } + block = &pem.Block{ + Type: "PRIVATE KEY", + Bytes: key, + } + case RSA: + fallthrough + default: + key := x509.MarshalPKCS1PrivateKey(key) + block = &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: key, + } + } + return pem.EncodeToMemory(block), nil +} + +// 将指定的公钥编码为PEM格式,如果不指定具体格式将使用RSA PUBLIC KEY(PKCS1)格式。 +func EncodePublicKey(key *rsa.PublicKey, format ...KeyFormat) ([]byte, error) { + var block *pem.Block + switch append(format, RSA)[0] { + case COMMON: + key, err := x509.MarshalPKIXPublicKey(key) + if err != nil { + return nil, fmt.Errorf("不支持的公钥格式,%w", err) + } + block = &pem.Block{ + Type: "PUBLIC KEY", + Bytes: key, + } + case RSA: + fallthrough + default: + key := x509.MarshalPKCS1PublicKey(key) + block = &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: key, + } + } + return pem.EncodeToMemory(block), nil +} + +// 将给定的PEM证书内容解析为RSA私钥。 +// 仅能读取PEM证书中的第一个私钥编码段。 +func DecodePrivateKey(cert []byte) (*rsa.PrivateKey, error) { + var ( + block *pem.Block + pemRestContent = cert + ) + for { + block, pemRestContent = pem.Decode(pemRestContent) + if block == nil { + return nil, &KeyNotFoundError{} + } + if block.Type == "PRIVATE KEY" || block.Type == "RSA PRIVATE KEY" { + break + } + } + switch block.Type { + case "PRIVATE KEY": + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("不支持的私钥格式,%w", err) + } + pKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("提供的私钥不是RSA专用私钥,%w", err) + } + return pKey, nil + case "RSA PRIVATE KEY": + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("不支持的私钥格式,%w", err) + } + return key, nil + default: + return nil, fmt.Errorf("不支持的私钥格式或者私钥不存在,%s", block.Type) + } +} + +// 将给定的PEM证书内容解析为RSA公钥。 +// 仅能读取PEM证书中的第一个公钥编码段。 +func DecodePublicKey(cert []byte) (*rsa.PublicKey, error) { + var ( + block *pem.Block + pemRestContent = cert + ) + for { + block, pemRestContent = pem.Decode(pemRestContent) + if block == nil { + return nil, &KeyNotFoundError{} + } + if block.Type == "PUBLIC KEY" || block.Type == "RSA PUBLIC KEY" { + break + } + } + switch block.Type { + case "PUBLIC KEY": + key, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("不支持的公钥格式,%w", err) + } + pKey, ok := key.(*rsa.PublicKey) + if !ok { + return nil, fmt.Errorf("提供的公钥不是RSA专用公钥,%w", err) + } + return pKey, nil + case "RSA PUBLIC KEY": + key, err := x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("不支持的公钥格式,%w", err) + } + return key, nil + default: + return nil, fmt.Errorf("不支持的公钥格式或者公钥不存在,%s", block.Type) + } +} diff --git a/encryption/rsa/rsa.go b/encryption/rsa/rsa.go new file mode 100644 index 0000000..2abf368 --- /dev/null +++ b/encryption/rsa/rsa.go @@ -0,0 +1,181 @@ +// 提供RSA不对称加解密功能。 +package rsa + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha512" + "errors" +) + +type KeyLength int +type SignAlgorithm int + +const ( + RSA1024 KeyLength = 1024 + RSA2048 KeyLength = 2048 +) + +const ( + PSSSign SignAlgorithm = iota + PKCS1Sign +) + +type KeyPair struct { + PublicKey *rsa.PublicKey + PrivateKey *rsa.PrivateKey +} + +// 生成一个新的RSA密钥对。 +func NewKeyPair(length KeyLength) (*KeyPair, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, int(length)) + if err != nil { + return nil, &UnableToGenerateKeyPairError{err: err} + } + publicKey := privateKey.Public().(*rsa.PublicKey) + return &KeyPair{ + PublicKey: publicKey, + PrivateKey: privateKey, + }, nil +} + +// 从一个PEM格式的密钥对中读取一套RSA密钥对。 +// 如果获取到的公钥与私钥不配对,那么将自动使用私钥生成一个配对的公钥,以保证密钥对的完整性。 +// 如果提供的证书中只存在公钥,那么将会保持私钥为空。 +func NewFromPEM(cert []byte) (*KeyPair, error) { + publicKey, err := DecodePublicKey(cert) + if err != nil { + if errors.Is(err, &KeyNotFoundError{}) { + publicKey = nil + } else { + return nil, err + } + } + privateKey, err := DecodePrivateKey(cert) + if err != nil { + if errors.Is(err, &KeyNotFoundError{}) { + privateKey = nil + } else { + return nil, err + } + } + if privateKey != nil { + pubKeyFromPriKey := privateKey.Public().(*rsa.PublicKey) + if publicKey == nil || !publicKey.Equal(pubKeyFromPriKey) { + publicKey = pubKeyFromPriKey + } + } + return &KeyPair{ + PublicKey: publicKey, + PrivateKey: privateKey, + }, nil +} + +// 加载一个新的公钥,将会自动清空私钥。 +func (kp *KeyPair) LoadPublicKey(cert []byte) error { + publicKey, err := DecodePublicKey(cert) + if err != nil { + return err + } + kp.PublicKey = publicKey + kp.PrivateKey = nil + return nil +} + +// 加载一个私钥,将会自动生成一个配对的公钥。 +func (kp *KeyPair) LoadPrivateKey(cert []byte) error { + privateKey, err := DecodePrivateKey(cert) + if err != nil { + return err + } + kp.PrivateKey = privateKey + kp.PublicKey = privateKey.Public().(*rsa.PublicKey) + return nil +} + +// 使用密钥对加密给定的数据,如果密钥对中不存在公钥则会返回错误。 +func (kp KeyPair) Encrypt(data []byte, label ...[]byte) ([]byte, error) { + if kp.PublicKey == nil { + return nil, &AbsentPublicKeyError{} + } + var cipherLabel []byte + if len(label) > 0 { + cipherLabel = label[0] + } else { + cipherLabel = nil + } + hasher := sha512.New() + cipherText, err := rsa.EncryptOAEP(hasher, rand.Reader, kp.PublicKey, data, cipherLabel) + if err != nil { + return nil, &EncryptionError{err: err} + } + return cipherText, nil +} + +// 使用密钥对解密给定的数据,如果密钥对中不存在私钥则会返回错误。 +func (kp KeyPair) Decrypt(data []byte, label ...[]byte) ([]byte, error) { + if kp.PrivateKey == nil { + return nil, &AbsentPrivateKeyError{} + } + var cipherLabel []byte + if len(label) > 0 { + cipherLabel = label[0] + } else { + cipherLabel = nil + } + hasher := sha512.New() + plainText, err := rsa.DecryptOAEP(hasher, rand.Reader, kp.PrivateKey, data, cipherLabel) + if err != nil { + return nil, &DecryptionError{err: err} + } + return plainText, nil +} + +// 对给定的数据使用私钥进行签名。 +func (kp KeyPair) Sign(data []byte, algorithm ...SignAlgorithm) ([]byte, error) { + if kp.PrivateKey == nil { + return nil, &AbsentPrivateKeyError{} + } + hashedData := sha512.Sum512(data) + switch append(algorithm, PSSSign)[0] { + case PSSSign: + signature, err := rsa.SignPSS(rand.Reader, kp.PrivateKey, crypto.SHA512, hashedData[:], &rsa.PSSOptions{SaltLength: 64}) + if err != nil { + return nil, &SignError{err: err} + } + return signature, nil + case PKCS1Sign: + signature, err := rsa.SignPKCS1v15(rand.Reader, kp.PrivateKey, crypto.SHA512, hashedData[:]) + if err != nil { + return nil, &SignError{err: err} + } + return signature, nil + default: + return nil, &InvalidSignMethodError{} + } +} + +// 对给定的数据和签名数据使用公钥进行验证。不返回任何错误信息即表示验证成功。 +func (kp KeyPair) Verify(data, signature []byte, algorithm ...SignAlgorithm) error { + if kp.PublicKey == nil { + return &AbsentPublicKeyError{} + } + hashedData := sha512.Sum512(data) + switch append(algorithm, PSSSign)[0] { + case PSSSign: + err := rsa.VerifyPSS(kp.PublicKey, crypto.SHA512, hashedData[:], signature, &rsa.PSSOptions{SaltLength: 64}) + if err != nil { + return &VerifyError{err: err} + } + return nil + case PKCS1Sign: + err := rsa.VerifyPKCS1v15(kp.PublicKey, crypto.SHA512, hashedData[:], signature) + if err != nil { + return &VerifyError{err: err} + } + return nil + default: + return &InvalidSignMethodError{} + } +}