From e7db6d78a21f7c88542d477ff42a6dbfa254f962 Mon Sep 17 00:00:00 2001 From: Jonathan Allie Date: Wed, 18 May 2011 12:59:04 -0700 Subject: [PATCH] crypto/x509: add support for parsing and verifying DSA signatures (DSA with SHA1, DSA with SHA256). Cleanup getSignatureFromOID function. R=agl, agl, rsc CC=golang-dev https://golang.org/cl/4530055 --- CONTRIBUTORS | 1 + src/pkg/crypto/x509/x509.go | 115 ++++++++++++++++++++++++------- src/pkg/crypto/x509/x509_test.go | 24 +++++++ 3 files changed, 114 insertions(+), 26 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index b10d8657621..6f025e25f0a 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -120,6 +120,7 @@ Jim McGrath Joe Poirier Johan Euphrosine John DeNero +Jonathan Allie Jonathan Wills Jos Visser Jose Luis Vázquez González diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index f82e5e83cb8..6ae1f8e395b 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -173,6 +173,10 @@ type dsaAlgorithmParameters struct { P, Q, G asn1.RawValue } +type dsaSignature struct { + R, S asn1.RawValue +} + type algorithmIdentifier struct { Algorithm asn1.ObjectIdentifier Parameters asn1.RawValue "optional" @@ -218,6 +222,8 @@ const ( SHA256WithRSA SHA384WithRSA SHA512WithRSA + DSAWithSHA1 + DSAWithSHA256 ) type PublicKeyAlgorithm int @@ -322,25 +328,69 @@ func (n Name) toRDNSequence() (ret rdnSequence) { return ret } -func getSignatureAlgorithmFromOID(oid []int) SignatureAlgorithm { - if len(oid) == 7 && oid[0] == 1 && oid[1] == 2 && oid[2] == 840 && - oid[3] == 113549 && oid[4] == 1 && oid[5] == 1 { - switch oid[6] { - case 2: - return MD2WithRSA - case 4: - return MD5WithRSA - case 5: - return SHA1WithRSA - case 11: - return SHA256WithRSA - case 12: - return SHA384WithRSA - case 13: - return SHA512WithRSA - } - } +// OIDs for signature algorithms +// +// pkcs-1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } +// +// +// RFC 3279 2.2.1 RSA Signature Algorithms +// +// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } +// +// md5WithRSAEncryption OBJECT IDENTIFER ::= { pkcs-1 4 } +// +// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } +// +// dsaWithSha1 OBJECT IDENTIFIER ::= { +// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } +// +// +// RFC 4055 5 PKCS #1 Version 1.5 +// +// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } +// +// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } +// +// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +// +// +// RFC 5758 3.1 DSA Signature Algorithms +// +// dsaWithSha356 OBJECT IDENTIFER ::= { +// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) +// algorithms(4) id-dsa-with-sha2(3) 2} +// +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} +) +func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { + switch { + case oid.Equal(oidSignatureMD2WithRSA): + return MD2WithRSA + case oid.Equal(oidSignatureMD5WithRSA): + return MD5WithRSA + case oid.Equal(oidSignatureSHA1WithRSA): + return SHA1WithRSA + case oid.Equal(oidSignatureSHA256WithRSA): + return SHA256WithRSA + case oid.Equal(oidSignatureSHA384WithRSA): + return SHA384WithRSA + case oid.Equal(oidSignatureSHA512WithRSA): + return SHA512WithRSA + case oid.Equal(oidSignatureDSAWithSHA1): + return DSAWithSHA1 + case oid.Equal(oidSignatureDSAWithSHA256): + return DSAWithSHA256 + } return UnknownSignatureAlgorithm } @@ -513,9 +563,9 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature var hashType crypto.Hash switch algo { - case SHA1WithRSA: + case SHA1WithRSA, DSAWithSHA1: hashType = crypto.SHA1 - case SHA256WithRSA: + case SHA256WithRSA, DSAWithSHA256: hashType = crypto.SHA256 case SHA384WithRSA: hashType = crypto.SHA384 @@ -530,15 +580,28 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return UnsupportedAlgorithmError{} } - pub, ok := c.PublicKey.(*rsa.PublicKey) - if !ok { - return UnsupportedAlgorithmError{} - } - h.Write(signed) digest := h.Sum() - return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) + switch pub := c.PublicKey.(type) { + case *rsa.PublicKey: + return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) + case *dsa.PublicKey: + dsaSig := new(dsaSignature) + if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { + return err + } + if !rawValueIsInteger(&dsaSig.R) || !rawValueIsInteger(&dsaSig.S) { + return asn1.StructuralError{"tags don't match"} + } + r := new(big.Int).SetBytes(dsaSig.R.Bytes) + s := new(big.Int).SetBytes(dsaSig.S.Bytes) + if !dsa.Verify(pub, digest, r, s) { + return os.ErrorString("DSA verification failure") + } + return + } + return UnsupportedAlgorithmError{} } // CheckCRLSignature checks that the signature in crl is from c. diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index b431ec43588..89d60f6a490 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -253,6 +253,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) { } } +// Self-signed certificate using DSA with SHA1 var dsaCertPem = `-----BEGIN CERTIFICATE----- MIIEDTCCA82gAwIBAgIJALHPghaoxeDhMAkGByqGSM44BAMweTELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAk5DMQ8wDQYDVQQHEwZOZXd0b24xFDASBgNVBAoTC0dvb2ds @@ -307,3 +308,26 @@ func TestParseCertificateWithDsaPublicKey(t *testing.T) { t.Fatal("Parsed key differs from expected key") } } + +func TestParseCertificateWithDSASignatureAlgorithm(t *testing.T) { + pemBlock, _ := pem.Decode([]byte(dsaCertPem)) + cert, err := ParseCertificate(pemBlock.Bytes) + if err != nil { + t.Fatal("Failed to parse certificate: %s", err) + } + if cert.SignatureAlgorithm != DSAWithSHA1 { + t.Errorf("Parsed signature algorithm was not DSAWithSHA1") + } +} + +func TestVerifyCertificateWithDSASignature(t *testing.T) { + pemBlock, _ := pem.Decode([]byte(dsaCertPem)) + cert, err := ParseCertificate(pemBlock.Bytes) + if err != nil { + t.Fatal("Failed to parse certificate: %s", err) + } + // test cert is self-signed + if err = cert.CheckSignatureFrom(cert); err != nil { + t.Fatal("DSA Certificate verfication failed: %s", err) + } +}