Sfoglia il codice sorgente

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>
Yasuhiro Matsumoto 8 anni fa
parent
commit
fea6c2c835
2 ha cambiato i file con 62 aggiunte e 0 eliminazioni
  1. 43 0
      ssh/keys.go
  2. 19 0
      ssh/keys_test.go

+ 43 - 0
ssh/keys.go

@@ -756,6 +756,18 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
 	return NewSignerFromKey(key)
 	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
 // encryptedBlock tells whether a private key is
 // encrypted by examining its Proc-Type header
 // encrypted by examining its Proc-Type header
 // for a mention of ENCRYPTED
 // 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
 // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
 // specified by the OpenSSL DSA man page.
 // specified by the OpenSSL DSA man page.
 func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
 func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {

+ 19 - 0
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) {
 func TestParseDSA(t *testing.T) {
 	// We actually exercise the ParsePrivateKey codepath here, as opposed to
 	// We actually exercise the ParsePrivateKey codepath here, as opposed to
 	// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
 	// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go