go.crypto/ssh: parse DSA private keys too.
R=golang-dev, agl
CC=golang-dev
https://golang.org/cl/13966043
diff --git a/ssh/keys.go b/ssh/keys.go
index 9738694..0c6c6b3 100644
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -12,6 +12,7 @@
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
+ "encoding/asn1"
"encoding/base64"
"encoding/pem"
"errors"
@@ -557,8 +558,8 @@
return sshKey, nil
}
-// ParsePublicKey parses a PEM encoded private key. Currently, only
-// PKCS#1, RSA and ECDSA private keys are supported.
+// ParsePublicKey parses a PEM encoded private key. It supports
+// PKCS#1, RSA, DSA and ECDSA private keys.
func ParsePrivateKey(pemBytes []byte) (Signer, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
@@ -579,12 +580,48 @@
return nil, err
}
rawkey = ec
-
- // TODO(hanwen): find doc for format and implement PEM parsing
- // for DSA keys.
+ case "DSA PRIVATE KEY":
+ ec, err := parseDSAPrivate(block.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ rawkey = ec
default:
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
}
return NewSignerFromKey(rawkey)
}
+
+// parseDSAPrivate parses a DSA key in ASN.1 DER encoding, as
+// documented in the OpenSSL DSA manpage.
+// TODO(hanwen): move this in to crypto/x509 after the Go 1.2 freeze.
+func parseDSAPrivate(p []byte) (*dsa.PrivateKey, error) {
+ k := struct {
+ Version int
+ P *big.Int
+ Q *big.Int
+ G *big.Int
+ Priv *big.Int
+ Pub *big.Int
+ }{}
+ rest, err := asn1.Unmarshal(p, &k)
+ if err != nil {
+ return nil, errors.New("ssh: failed to parse DSA key: " + err.Error())
+ }
+ if len(rest) > 0 {
+ return nil, errors.New("ssh: garbage after DSA key")
+ }
+
+ return &dsa.PrivateKey{
+ PublicKey: dsa.PublicKey{
+ Parameters: dsa.Parameters{
+ P: k.P,
+ Q: k.Q,
+ G: k.G,
+ },
+ Y: k.Priv,
+ },
+ X: k.Pub,
+ }, nil
+}
diff --git a/ssh/keys_test.go b/ssh/keys_test.go
index fb3f21e..595fb29 100644
--- a/ssh/keys_test.go
+++ b/ssh/keys_test.go
@@ -132,6 +132,37 @@
}
}
+// ssh-keygen -t dsa -f /tmp/idsa.pem
+var dsaPEM = `-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB
+lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3
+EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD
+nD82+lxh2fb4PMsIiaXudAsBAoGAQRf7Q/iaPRn43ZquUhd6WwvirqUj+tkIu6eV
+2nZWYmXLlqFQKEy4Tejl7Wkyzr2OSYvbXLzo7TNxLKoWor6ips0phYPPMyXld14r
+juhT24CrhOzuLMhDduMDi032wDIZG4Y+K7ElU8Oufn8Sj5Wge8r6ANmmVgmFfynr
+FhdYCngCgYEA3ucGJ93/Mx4q4eKRDxcWD3QzWyqpbRVRRV1Vmih9Ha/qC994nJFz
+DQIdjxDIT2Rk2AGzMqFEB68Zc3O+Wcsmz5eWWzEwFxaTwOGWTyDqsDRLm3fD+QYj
+nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY
+Fmsr0W6fHB9nhS4/UXM8
+-----END DSA PRIVATE KEY-----`
+
+func TestParseDSA(t *testing.T) {
+ s, err := ParsePrivateKey([]byte(dsaPEM))
+ if err != nil {
+ t.Fatalf("ParsePrivateKey returned error: %s", err)
+ }
+
+ data := []byte("sign me")
+ sig, err := s.Sign(rand.Reader, data)
+ if err != nil {
+ t.Fatalf("dsa.Sign: %v", err)
+ }
+
+ if !s.PublicKey().Verify(data, sig) {
+ t.Error("Verify failed.")
+ }
+}
+
func init() {
raw, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
ecdsaKey, _ = NewSignerFromKey(raw)