aboutsummaryrefslogtreecommitdiff
path: root/ssh
diff options
context:
space:
mode:
authorHan-Wen Nienhuys <hanwen@google.com>2013-09-26 11:17:52 -0400
committerAdam Langley <agl@golang.org>2013-09-26 11:17:52 -0400
commit4e0581245c387292aaa4f50016fbf2c32ed9b954 (patch)
treefea957691f2a0a51efe8f158e47e611bc7a1200c /ssh
parent73e99326fb280d3bfd0c28ed55d0a989bf70f8d8 (diff)
downloadgo-x-crypto-4e0581245c387292aaa4f50016fbf2c32ed9b954.tar.xz
go.crypto/ssh: parse DSA private keys too.
R=golang-dev, agl CC=golang-dev https://golang.org/cl/13966043
Diffstat (limited to 'ssh')
-rw-r--r--ssh/keys.go47
-rw-r--r--ssh/keys_test.go31
2 files changed, 73 insertions, 5 deletions
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 @@ import (
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
+ "encoding/asn1"
"encoding/base64"
"encoding/pem"
"errors"
@@ -557,8 +558,8 @@ func NewPublicKey(k interface{}) (PublicKey, error) {
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 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
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 @@ AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+
}
}
+// 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)