feat: 新增XTS/CCM流式与KDF能力,补充安全测试并更新README/CHANGELOG

This commit is contained in:
2026-03-18 13:43:18 +08:00
parent e89350b56a
commit 4fa79744e8
44 changed files with 4636 additions and 77 deletions
+6
View File
@@ -10,6 +10,7 @@ import (
"encoding/pem"
"errors"
"github.com/emmansun/gmsm/pkcs8"
"github.com/emmansun/gmsm/sm2"
"github.com/emmansun/gmsm/smx509"
"golang.org/x/crypto/ssh"
@@ -86,6 +87,11 @@ func DecodePrivateKey(private []byte, password string) (crypto.PrivateKey, error
return key, nil
}
return smx509.ParsePKCS8PrivateKey(bytes)
case "ENCRYPTED PRIVATE KEY":
if password == "" {
return nil, errors.New("private key is encrypted but password is empty")
}
return pkcs8.ParsePKCS8PrivateKey(blk.Bytes, []byte(password))
case "OPENSSH PRIVATE KEY":
if password == "" {
return ssh.ParseRawPrivateKey(private)
+137 -3
View File
@@ -9,6 +9,7 @@ import (
"errors"
"math/big"
"github.com/emmansun/gmsm/pkcs8"
"golang.org/x/crypto/ssh"
)
@@ -21,6 +22,37 @@ func GenerateRsaKey(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
}
func EncodeRsaPrivateKey(private *rsa.PrivateKey, secret string) ([]byte, error) {
return EncodeRsaPrivateKeyWithLegacy(private, secret, true)
}
func EncodeRsaPrivateKeyWithLegacy(private *rsa.PrivateKey, secret string, legacy bool) ([]byte, error) {
if legacy {
return encodeRsaPrivateKeyLegacy(private, secret)
}
return EncodeRsaPrivateKeyPKCS8(private, secret)
}
func EncodeRsaPrivateKeyPKCS8(private *rsa.PrivateKey, secret string) ([]byte, error) {
password := []byte(secret)
var (
der []byte
blockType = "PRIVATE KEY"
err error
)
if secret == "" {
der, err = pkcs8.MarshalPrivateKey(private, nil, nil)
} else {
der, err = pkcs8.MarshalPrivateKey(private, password, pkcs8.DefaultOpts)
blockType = "ENCRYPTED PRIVATE KEY"
}
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: blockType, Bytes: der}), nil
}
func encodeRsaPrivateKeyLegacy(private *rsa.PrivateKey, secret string) ([]byte, error) {
der := x509.MarshalPKCS1PrivateKey(private)
if secret == "" {
return pem.EncodeToMemory(&pem.Block{
@@ -52,6 +84,46 @@ func DecodeRsaPrivateKey(private []byte, password string) (*rsa.PrivateKey, erro
return nil, errors.New("private key error")
}
switch blk.Type {
case "PRIVATE KEY", "ENCRYPTED PRIVATE KEY":
return DecodeRsaPrivateKeyPKCS8(private, password)
default:
return decodeRsaPrivateKeyLegacy(private, password)
}
}
func DecodeRsaPrivateKeyWithLegacy(private []byte, password string, legacy bool) (*rsa.PrivateKey, error) {
if legacy {
return decodeRsaPrivateKeyLegacy(private, password)
}
return DecodeRsaPrivateKeyPKCS8(private, password)
}
func DecodeRsaPrivateKeyPKCS8(private []byte, password string) (*rsa.PrivateKey, error) {
blk, _ := pem.Decode(private)
if blk == nil {
return nil, errors.New("private key error")
}
switch blk.Type {
case "PRIVATE KEY":
return pkcs8.ParsePKCS8PrivateKeyRSA(blk.Bytes)
case "ENCRYPTED PRIVATE KEY":
if password == "" {
return nil, errors.New("private key is encrypted but password is empty")
}
return pkcs8.ParsePKCS8PrivateKeyRSA(blk.Bytes, []byte(password))
default:
return nil, errors.New("private key is not PKCS#8")
}
}
func decodeRsaPrivateKeyLegacy(private []byte, password string) (*rsa.PrivateKey, error) {
blk, _ := pem.Decode(private)
if blk == nil {
return nil, errors.New("private key error")
}
bytes, err := decodePEMBlockBytes(blk, password)
if err != nil {
return nil, err
@@ -60,11 +132,11 @@ func DecodeRsaPrivateKey(private []byte, password string) (*rsa.PrivateKey, erro
if prikey, err := x509.ParsePKCS1PrivateKey(bytes); err == nil {
return prikey, nil
}
pkcs8, err := x509.ParsePKCS8PrivateKey(bytes)
pkcs8key, err := x509.ParsePKCS8PrivateKey(bytes)
if err != nil {
return nil, err
}
prikey, ok := pkcs8.(*rsa.PrivateKey)
prikey, ok := pkcs8key.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("private key is not RSA")
}
@@ -96,6 +168,10 @@ func EncodeRsaSSHPublicKey(public *rsa.PublicKey) ([]byte, error) {
}
func GenerateRsaSSHKeyPair(bits int, secret string) (string, string, error) {
return GenerateRsaSSHKeyPairWithLegacy(bits, secret, true)
}
func GenerateRsaSSHKeyPairWithLegacy(bits int, secret string, legacy bool) (string, string, error) {
pkey, pubkey, err := GenerateRsaKey(bits)
if err != nil {
return "", "", err
@@ -104,7 +180,7 @@ func GenerateRsaSSHKeyPair(bits int, secret string) (string, string, error) {
if err != nil {
return "", "", err
}
priv, err := EncodeRsaPrivateKey(pkey, secret)
priv, err := EncodeRsaPrivateKeyWithLegacy(pkey, secret, legacy)
if err != nil {
return "", "", err
}
@@ -119,6 +195,22 @@ func RSADecrypt(prikey *rsa.PrivateKey, data []byte) ([]byte, error) {
return rsa.DecryptPKCS1v15(rand.Reader, prikey, data)
}
func RSAEncryptOAEP(pub *rsa.PublicKey, data, label []byte, hashType crypto.Hash) ([]byte, error) {
hashType, err := normalizeModernRSAHash(hashType)
if err != nil {
return nil, err
}
return rsa.EncryptOAEP(hashType.New(), rand.Reader, pub, data, label)
}
func RSADecryptOAEP(prikey *rsa.PrivateKey, data, label []byte, hashType crypto.Hash) ([]byte, error) {
hashType, err := normalizeModernRSAHash(hashType)
if err != nil {
return nil, err
}
return rsa.DecryptOAEP(hashType.New(), rand.Reader, prikey, data, label)
}
func RSASign(msg, priKey []byte, password string, hashType crypto.Hash) ([]byte, error) {
prikey, err := DecodeRsaPrivateKey(priKey, password)
if err != nil {
@@ -143,6 +235,38 @@ func RSAVerify(sig, msg, pubKey []byte, hashType crypto.Hash) error {
return rsa.VerifyPKCS1v15(pubkey, hashType, hashed, sig)
}
func RSASignPSS(msg, priKey []byte, password string, hashType crypto.Hash, opts *rsa.PSSOptions) ([]byte, error) {
prikey, err := DecodeRsaPrivateKey(priKey, password)
if err != nil {
return nil, err
}
hashType, err = normalizeModernRSAHash(hashType)
if err != nil {
return nil, err
}
hashed, err := hashMessage(msg, hashType)
if err != nil {
return nil, err
}
return rsa.SignPSS(rand.Reader, prikey, hashType, hashed, opts)
}
func RSAVerifyPSS(sig, msg, pubKey []byte, hashType crypto.Hash, opts *rsa.PSSOptions) error {
pubkey, err := DecodeRsaPublicKey(pubKey)
if err != nil {
return err
}
hashType, err = normalizeModernRSAHash(hashType)
if err != nil {
return err
}
hashed, err := hashMessage(msg, hashType)
if err != nil {
return err
}
return rsa.VerifyPSS(pubkey, hashType, hashed, sig, opts)
}
func RSAEncryptByPrivkey(priv *rsa.PrivateKey, data []byte) ([]byte, error) {
return rsa.SignPKCS1v15(nil, priv, crypto.Hash(0), data)
}
@@ -154,6 +278,16 @@ func RSADecryptByPubkey(pub *rsa.PublicKey, data []byte) ([]byte, error) {
return unLeftPad(em)
}
func normalizeModernRSAHash(hashType crypto.Hash) (crypto.Hash, error) {
if hashType == 0 {
hashType = crypto.SHA256
}
if !hashType.Available() {
return 0, errors.New("hash function is not available")
}
return hashType, nil
}
func hashMessage(msg []byte, hashType crypto.Hash) ([]byte, error) {
if hashType == 0 {
return msg, nil
+134
View File
@@ -0,0 +1,134 @@
package asymm
import (
"bytes"
"crypto/rsa"
"testing"
)
func TestRsaPrivateKeyEncodeDecodeWithLegacyFlag(t *testing.T) {
priv, pub, err := GenerateRsaKey(1024)
if err != nil {
t.Fatalf("GenerateRsaKey failed: %v", err)
}
modernPEM, err := EncodeRsaPrivateKeyWithLegacy(priv, "pwd", false)
if err != nil {
t.Fatalf("EncodeRsaPrivateKeyWithLegacy modern failed: %v", err)
}
if !bytes.Contains(modernPEM, []byte("ENCRYPTED PRIVATE KEY")) {
t.Fatalf("modern PEM should be ENCRYPTED PRIVATE KEY")
}
modernPriv, err := DecodeRsaPrivateKeyWithLegacy(modernPEM, "pwd", false)
if err != nil {
t.Fatalf("DecodeRsaPrivateKeyWithLegacy modern failed: %v", err)
}
if modernPriv.D.Cmp(priv.D) != 0 {
t.Fatalf("modern decoded private key mismatch")
}
autoPriv, err := DecodeRsaPrivateKey(modernPEM, "pwd")
if err != nil {
t.Fatalf("DecodeRsaPrivateKey auto failed: %v", err)
}
if autoPriv.D.Cmp(priv.D) != 0 {
t.Fatalf("auto decoded private key mismatch")
}
if _, err := DecodePrivateKey(modernPEM, "pwd"); err != nil {
t.Fatalf("DecodePrivateKey for encrypted PKCS8 failed: %v", err)
}
legacyPEM, err := EncodeRsaPrivateKeyWithLegacy(priv, "pwd", true)
if err != nil {
t.Fatalf("EncodeRsaPrivateKeyWithLegacy legacy failed: %v", err)
}
if !bytes.Contains(legacyPEM, []byte("RSA PRIVATE KEY")) {
t.Fatalf("legacy PEM should be RSA PRIVATE KEY")
}
legacyPriv, err := DecodeRsaPrivateKeyWithLegacy(legacyPEM, "pwd", true)
if err != nil {
t.Fatalf("DecodeRsaPrivateKeyWithLegacy legacy failed: %v", err)
}
if legacyPriv.D.Cmp(priv.D) != 0 {
t.Fatalf("legacy decoded private key mismatch")
}
pubPEM, err := EncodeRsaPublicKey(pub)
if err != nil {
t.Fatalf("EncodeRsaPublicKey failed: %v", err)
}
decodedPub, err := DecodeRsaPublicKey(pubPEM)
if err != nil {
t.Fatalf("DecodeRsaPublicKey failed: %v", err)
}
if decodedPub.N.Cmp(pub.N) != 0 || decodedPub.E != pub.E {
t.Fatalf("decoded public key mismatch")
}
}
func TestRSAOAEPEncryptDecrypt(t *testing.T) {
priv, pub, err := GenerateRsaKey(1024)
if err != nil {
t.Fatalf("GenerateRsaKey failed: %v", err)
}
msg := []byte("rsa-oaep-message")
label := []byte("label")
enc, err := RSAEncryptOAEP(pub, msg, label, 0)
if err != nil {
t.Fatalf("RSAEncryptOAEP failed: %v", err)
}
dec, err := RSADecryptOAEP(priv, enc, label, 0)
if err != nil {
t.Fatalf("RSADecryptOAEP failed: %v", err)
}
if !bytes.Equal(dec, msg) {
t.Fatalf("oaep decrypt mismatch")
}
}
func TestRSAPSSSignVerify(t *testing.T) {
priv, pub, err := GenerateRsaKey(1024)
if err != nil {
t.Fatalf("GenerateRsaKey failed: %v", err)
}
privPEM, err := EncodeRsaPrivateKeyWithLegacy(priv, "pwd", false)
if err != nil {
t.Fatalf("EncodeRsaPrivateKeyWithLegacy failed: %v", err)
}
pubPEM, err := EncodeRsaPublicKey(pub)
if err != nil {
t.Fatalf("EncodeRsaPublicKey failed: %v", err)
}
msg := []byte("rsa-pss-message")
sig, err := RSASignPSS(msg, privPEM, "pwd", 0, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
if err != nil {
t.Fatalf("RSASignPSS failed: %v", err)
}
if err := RSAVerifyPSS(sig, msg, pubPEM, 0, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}); err != nil {
t.Fatalf("RSAVerifyPSS failed: %v", err)
}
}
func TestRSAPKCS1v15EncryptDecryptCompatibility(t *testing.T) {
priv, pub, err := GenerateRsaKey(1024)
if err != nil {
t.Fatalf("GenerateRsaKey failed: %v", err)
}
msg := []byte("rsa-pkcs1v15-message")
enc, err := RSAEncrypt(pub, msg)
if err != nil {
t.Fatalf("RSAEncrypt failed: %v", err)
}
dec, err := RSADecrypt(priv, enc)
if err != nil {
t.Fatalf("RSADecrypt failed: %v", err)
}
if !bytes.Equal(dec, msg) {
t.Fatalf("pkcs1v15 decrypt mismatch")
}
}