Skip to content

Commit

Permalink
feat(pkix): support passphrase used for private key PEM block
Browse files Browse the repository at this point in the history
  • Loading branch information
yichengq committed Mar 12, 2014
1 parent c8143a3 commit ce1f91f
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 1 deletion.
47 changes: 46 additions & 1 deletion pkix/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewKey(pub crypto.PublicKey, priv crypto.PrivateKey) *Key {
return &Key{Public: pub, Private: priv}
}

// NewKeyFromRSAPrivateKeyPEM inits Key from PEM-format rsa private key bytes
// NewKeyFromPrivateKeyPEM inits Key from PEM-format rsa private key bytes
func NewKeyFromPrivateKeyPEM(data []byte) (*Key, error) {
pemBlock, _ := pem.Decode(data)
if pemBlock == nil {
Expand All @@ -57,6 +57,29 @@ func NewKeyFromPrivateKeyPEM(data []byte) (*Key, error) {
return NewKey(&priv.PublicKey, priv), nil
}

// NewKeyFromEncryptedPrivateKeyPEM inits Key from encrypted PEM-format rsa private key bytes
func NewKeyFromEncryptedPrivateKeyPEM(data []byte, password []byte) (*Key, error) {
pemBlock, _ := pem.Decode(data)
if pemBlock == nil {
return nil, errors.New("cannot find the next PEM formatted block")
}
if pemBlock.Type != rsaPrivateKeyPEMBlockType {
return nil, errors.New("unmatched type or headers")
}

b, err := x509.DecryptPEMBlock(pemBlock, password)
if err != nil {
return nil, err
}

priv, err := x509.ParsePKCS1PrivateKey(b)
if err != nil {
return nil, err
}

return NewKey(&priv.PublicKey, priv), nil
}

// ExportPrivate exports PEM-format private key
func (k *Key) ExportPrivate() ([]byte, error) {
var privPEMBlock *pem.Block
Expand All @@ -78,6 +101,28 @@ func (k *Key) ExportPrivate() ([]byte, error) {
return buf.Bytes(), nil
}

// ExportEncryptedPrivate exports encrypted PEM-format private key
func (k *Key) ExportEncryptedPrivate(password []byte) ([]byte, error) {
var privBytes []byte
switch priv := k.Private.(type) {
case *rsa.PrivateKey:
privBytes = x509.MarshalPKCS1PrivateKey(priv)
default:
return nil, errors.New("only RSA private key is supported")
}

privPEMBlock, err := x509.EncryptPEMBlock(rand.Reader, rsaPrivateKeyPEMBlockType, privBytes, password, x509.PEMCipher3DES)
if err != nil {
return nil, err
}

buf := new(bytes.Buffer)
if err := pem.Encode(buf, privPEMBlock); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key.
type rsaPublicKey struct {
N *big.Int
Expand Down
47 changes: 47 additions & 0 deletions pkix/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ IEzY0Lcuq3pwJlQyyaNQxXF4orPp5Rzi5pNabuGJ8Q==
-----END RSA PRIVATE KEY-----
`

rsaEncryptedPrivKeyAuthPEM = `-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,2ef29f8702fc501a
VP9jGydG3iPHjGutyZC+4g5wx0wQ847YcdyyXUfwuC3XJoQ1IeAhPlNhgYuaTBU2
5yonpHwEo4f2QrRY+0bVwQylQxQvqQsOp8uvyXoD2jQE9fwfkJrFmiBYGL0zu9NT
mSQETzqyFkF7IlUy0AQbks+dXrPNJ4BhaDE/BQ4L3Hc6N72EoOPjWf+Pp1SsGED5
IOzvnFTEhJR9JTH1E4vuac4MsY9eXvlKNGy5r06U4TSS7hkcetFzIcpXNl2TQk+k
9hh9LSkHUG5ruLP8v+8GpdOtmgJ4BZ0FUhmkGfhVBGZik4iMBDPvy7pZ2fVldtAL
8n09uLQZ0DL8GrNJNudWEGvZCMYVhseaQkIQu9540oHG3IxPzqR4whNx4ABB6oKQ
t1Sl4NRB9T+zUbjjdigYhEiDGbB/b0yfu6HsNouLEGXvXy7yORfJLkIIfjngIMtM
7FUylgsRL1U1swDvJOKmFEHaMbUfKQ0s14zCFk/Wx7mXxgWWRNxI33Y0ttxya18n
fm6Q0/q3ZHqczaDhsY7Kk5RpYX9aIP2LDjLisN9J4MEVawJ+jiwdHqCHozb63fTx
rvRPORfRDOFxx33eTh0D4f21y/AgOxuENOiQaUNoq9VMg9rzI14i9/wwIk2DlkxF
SH2sSBFOBRXWsJQFgme+278hubaQAVf0vrUSAeiB2ZA9ACb0c8mOYygeTZ2Mjnq4
A3J1riefAkQ1RZ97LHvzYxyBFklUr7+NeZztv3N6cuOZ1JEka2AKE6SOo9xMqWt9
2TR5s0BqeOjU45XGuiXOAipLc1pAkoKqtE/7IKRr/IY=
-----END RSA PRIVATE KEY-----
`
password = "123456"
wrongPassword = "654321"

subjectKeyIdOfRSAPubKeyAuthBASE64 = "wqt53Slv45QgmFh7AiIj+dx1NOw="
)

Expand Down Expand Up @@ -113,6 +135,31 @@ func TestRSAKeyExport(t *testing.T) {
}
}

// TestRSAKeyExportEncrypted tests the ability to convert rsa key into encrypted PEM bytes
func TestRSAKeyExportEncrypted(t *testing.T) {
key, err := NewKeyFromEncryptedPrivateKeyPEM([]byte(rsaEncryptedPrivKeyAuthPEM), []byte(password))
if err != nil {
t.Fatal("Failed to parse certificate from PEM:", err)
}

pemBytes, err := key.ExportPrivate()
if err != nil {
t.Fatal("Failed exporting PEM-format bytes:", err)
}
if bytes.Compare(pemBytes, []byte(rsaPrivKeyAuthPEM)) != 0 {
t.Fatal("Failed exporting the same PEM-format bytes")
}

pemBytes, err = key.ExportEncryptedPrivate([]byte(password))
if err != nil {
t.Fatal("Failed exporting PEM-format bytes:", err)
}

if _, err := NewKeyFromEncryptedPrivateKeyPEM(pemBytes, []byte(wrongPassword)); err == nil {
t.Fatal("Expect not parsing certificate from PEM:", err)
}
}

func TestRSAKeyGenerateSubjectKeyId(t *testing.T) {
key, err := NewKeyFromPrivateKeyPEM([]byte(rsaPrivKeyAuthPEM))
if err != nil {
Expand Down

0 comments on commit ce1f91f

Please sign in to comment.