diff options
| author | Yasuhiro Matsumoto <mattn.jp@gmail.com> | 2017-02-02 15:10:04 +0900 |
|---|---|---|
| committer | Han-Wen Nienhuys <hanwen@google.com> | 2017-06-13 13:45:37 +0000 |
| commit | fea6c2c83557701d46ea1cc0ea4c8272632fa3bd (patch) | |
| tree | 376658d13e6c76c0bc69b8a5dfa8e4c0a73d3c6f /ssh | |
| parent | e7ba82683099cae71475961448ab8f903ea77c26 (diff) | |
| download | go-x-crypto-fea6c2c83557701d46ea1cc0ea4c8272632fa3bd.tar.xz | |
ssh: add ParsePrivateKeysWithPassphrase
ssh package doesn't provide way to parse private keys with passphrase.
Fixes golang/go#18692
Change-Id: Ic139f11b6dfe7ef61690d6125e0673d50a48db16
Reviewed-on: https://go-review.googlesource.com/36079
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Diffstat (limited to 'ssh')
| -rw-r--r-- | ssh/keys.go | 43 | ||||
| -rw-r--r-- | ssh/keys_test.go | 19 |
2 files changed, 62 insertions, 0 deletions
diff --git a/ssh/keys.go b/ssh/keys.go index cf68532..4c8b1a8 100644 --- a/ssh/keys.go +++ b/ssh/keys.go @@ -756,6 +756,18 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) { return NewSignerFromKey(key) } +// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private +// key and passphrase. It supports the same keys as +// ParseRawPrivateKeyWithPassphrase. +func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) { + key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + // encryptedBlock tells whether a private key is // encrypted by examining its Proc-Type header // for a mention of ENCRYPTED @@ -790,6 +802,37 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { } } +func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + buf := block.Bytes + + if encryptedBlock(block) { + if x509.IsEncryptedPEMBlock(block) { + var err error + buf, err = x509.DecryptPEMBlock(block, passPhrase) + if err != nil { + return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err) + } + } + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(buf) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(buf) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(buf) + case "OPENSSH PRIVATE KEY": + return parseOpenSSHPrivateKey(buf) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as // specified by the OpenSSL DSA man page. func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { diff --git a/ssh/keys_test.go b/ssh/keys_test.go index a65e87e..2bacc52 100644 --- a/ssh/keys_test.go +++ b/ssh/keys_test.go @@ -148,6 +148,25 @@ func TestParseEncryptedPrivateKeysFails(t *testing.T) { } } +// Parse encrypted private keys with passphrase +func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) { + data := []byte("sign me") + for _, tt := range testdata.PEMEncryptedKeys { + s, err := ParsePrivateKeyWithPassphrase(tt.PEMBytes, []byte(tt.EncryptionKey)) + if err != nil { + t.Fatalf("ParsePrivateKeyWithPassphrase returned error: %s", err) + continue + } + sig, err := s.Sign(rand.Reader, data) + if err != nil { + t.Fatalf("dsa.Sign: %v", err) + } + if err := s.PublicKey().Verify(data, sig); err != nil { + t.Errorf("Verify failed: %v", err) + } + } +} + func TestParseDSA(t *testing.T) { // We actually exercise the ParsePrivateKey codepath here, as opposed to // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go |
