feat: 新增XTS/CCM流式与KDF能力,补充安全测试并更新README/CHANGELOG
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user