diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pkg/crypto/x509/x509.go | 232 | ||||
| -rw-r--r-- | src/pkg/crypto/x509/x509_test.go | 50 | 
2 files changed, 280 insertions, 2 deletions
| diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index a85022859..f486a5a68 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -12,6 +12,7 @@ import (  	"crypto/rsa"  	"crypto/sha1"  	"hash" +	"io"  	"os"  	"strings"  	"time" @@ -69,6 +70,21 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {  	return  } +// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. +func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { +	priv := pkcs1PrivateKey{ +		Version: 1, +		N: asn1.RawValue{Tag: 2, Bytes: key.PublicKey.N.Bytes()}, +		E: key.PublicKey.E, +		D: asn1.RawValue{Tag: 2, Bytes: key.D.Bytes()}, +		P: asn1.RawValue{Tag: 2, Bytes: key.P.Bytes()}, +		Q: asn1.RawValue{Tag: 2, Bytes: key.Q.Bytes()}, +	} + +	b, _ := asn1.MarshalToMemory(priv) +	return b +} +  // These structures reflect the ASN.1 structure of X.509 certificates.:  type certificate struct { @@ -86,8 +102,8 @@ type tbsCertificate struct {  	Validity           validity  	Subject            rdnSequence  	PublicKey          publicKeyInfo -	UniqueId           asn1.BitString "optional,explicit,tag:1" -	SubjectUniqueId    asn1.BitString "optional,explicit,tag:2" +	UniqueId           asn1.BitString "optional,tag:1" +	SubjectUniqueId    asn1.BitString "optional,tag:2"  	Extensions         []extension    "optional,explicit,tag:3"  } @@ -188,6 +204,64 @@ func (n *Name) fillFromRDNSequence(rdns *rdnSequence) {  	}  } +var ( +	oidCountry            = []int{2, 5, 4, 6} +	oidOrganization       = []int{2, 5, 4, 10} +	oidOrganizationalUnit = []int{2, 5, 4, 11} +	oidCommonName         = []int{2, 5, 4, 3} +	oidSerialNumber       = []int{2, 5, 4, 5} +	oidLocatity           = []int{2, 5, 4, 7} +	oidProvince           = []int{2, 5, 4, 8} +	oidStreetAddress      = []int{2, 5, 4, 9} +	oidPostalCode         = []int{2, 5, 4, 17} +) + +func (n Name) toRDNSequence() (ret rdnSequence) { +	ret = make([]relativeDistinguishedNameSET, 9 /* maximum number of elements */ ) +	i := 0 +	if len(n.Country) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidCountry, n.Country}} +		i++ +	} +	if len(n.Organization) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidOrganization, n.Organization}} +		i++ +	} +	if len(n.OrganizationalUnit) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidOrganizationalUnit, n.OrganizationalUnit}} +		i++ +	} +	if len(n.CommonName) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidCommonName, n.CommonName}} +		i++ +	} +	if len(n.SerialNumber) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidSerialNumber, n.SerialNumber}} +		i++ +	} +	if len(n.Locality) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidLocatity, n.Locality}} +		i++ +	} +	if len(n.Province) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidProvince, n.Province}} +		i++ +	} +	if len(n.StreetAddress) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidStreetAddress, n.StreetAddress}} +		i++ +	} +	if len(n.PostalCode) > 0 { +		ret[i] = []attributeTypeAndValue{attributeTypeAndValue{oidPostalCode, n.PostalCode}} +		i++ +	} + +	// Adding another RDN here? Remember to update the maximum number of +	// elements in the make() at the top of the function. + +	return ret[0:i] +} +  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 { @@ -589,3 +663,157 @@ func ParseCertificates(asn1Data []byte) ([]*Certificate, os.Error) {  	return ret, nil  } + +func reverseBitsInAByte(in byte) byte { +	b1 := in>>4 | in<<4 +	b2 := b1>>2&0x33 | b1<<2&0xcc +	b3 := b2>>1&0x55 | b2<<1&0xaa +	return b3 +} + +var ( +	oidExtensionSubjectKeyId     = []int{2, 5, 29, 14} +	oidExtensionKeyUsage         = []int{2, 5, 29, 15} +	oidExtensionAuthorityKeyId   = []int{2, 5, 29, 35} +	oidExtensionBasicConstraints = []int{2, 5, 29, 19} +	oidExtensionSubjectAltName   = []int{2, 5, 29, 17} +) + +func buildExtensions(template *Certificate) (ret []extension, err os.Error) { +	ret = make([]extension, 5 /* maximum number of elements. */ ) +	n := 0 + +	if template.KeyUsage != 0 { +		ret[n].Id = oidExtensionKeyUsage +		ret[n].Critical = true + +		var a [2]byte +		a[0] = reverseBitsInAByte(byte(template.KeyUsage)) +		a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8)) + +		l := 1 +		if a[1] != 0 { +			l = 2 +		} + +		ret[n].Value, err = asn1.MarshalToMemory(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) +		if err != nil { +			return +		} +		n++ +	} + +	if template.BasicConstraintsValid { +		ret[n].Id = oidExtensionBasicConstraints +		ret[n].Value, err = asn1.MarshalToMemory(basicConstraints{template.IsCA, template.MaxPathLen}) +		ret[n].Critical = true +		if err != nil { +			return +		} +		n++ +	} + +	if len(template.SubjectKeyId) > 0 { +		ret[n].Id = oidExtensionSubjectKeyId +		ret[n].Value, err = asn1.MarshalToMemory(template.SubjectKeyId) +		if err != nil { +			return +		} +		n++ +	} + +	if len(template.AuthorityKeyId) > 0 { +		ret[n].Id = oidExtensionAuthorityKeyId +		ret[n].Value, err = asn1.MarshalToMemory(authKeyId{template.AuthorityKeyId}) +		if err != nil { +			return +		} +		n++ +	} + +	if len(template.DNSNames) > 0 { +		ret[n].Id = oidExtensionSubjectAltName +		rawValues := make([]asn1.RawValue, len(template.DNSNames)) +		for i, name := range template.DNSNames { +			rawValues[i] = asn1.RawValue{Tag: 2, Class: 2, Bytes: strings.Bytes(name)} +		} +		ret[n].Value, err = asn1.MarshalToMemory(rawValues) +		if err != nil { +			return +		} +		n++ +	} + +	// Adding another extension here? Remember to update the maximum number +	// of elements in the make() at the top of the function. + +	return ret[0:n], nil +} + +var ( +	oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5} +	oidRSA         = []int{1, 2, 840, 113549, 1, 1, 1} +) + +// CreateSelfSignedCertificate creates a new certificate based on +// a template. The following members of template are used: SerialNumber, +// Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA, +// MaxPathLen, SubjectKeyId, DNSNames. +// +// The certificate is signed by parent. If parent is equal to template then the +// certificate is self-signed. +// +// The returned slice is the certificate in DER encoding. +func CreateCertificate(rand io.Reader, template, parent *Certificate, priv *rsa.PrivateKey) (cert []byte, err os.Error) { +	asn1PublicKey, err := asn1.MarshalToMemory(rsaPublicKey{ +		N: asn1.RawValue{Tag: 2, Bytes: priv.PublicKey.N.Bytes()}, +		E: priv.PublicKey.E, +	}) +	if err != nil { +		return +	} + +	if len(template.SubjectKeyId) > 0 && len(parent.SubjectKeyId) > 0 { +		template.AuthorityKeyId = parent.SubjectKeyId +	} + +	extensions, err := buildExtensions(template) +	if err != nil { +		return +	} + +	encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} +	c := tbsCertificate{ +		Version: 3, +		SerialNumber: asn1.RawValue{Bytes: template.SerialNumber, Tag: 2}, +		SignatureAlgorithm: algorithmIdentifier{oidSHA1WithRSA}, +		Issuer: parent.Subject.toRDNSequence(), +		Validity: validity{template.NotBefore, template.NotAfter}, +		Subject: template.Subject.toRDNSequence(), +		PublicKey: publicKeyInfo{algorithmIdentifier{oidRSA}, encodedPublicKey}, +		Extensions: extensions, +	} + +	tbsCertContents, err := asn1.MarshalToMemory(c) +	if err != nil { +		return +	} + +	c.Raw = tbsCertContents + +	h := sha1.New() +	h.Write(tbsCertContents) +	digest := h.Sum() + +	signature, err := rsa.SignPKCS1v15(rand, priv, rsa.HashSHA1, digest) +	if err != nil { +		return +	} + +	cert, err = asn1.MarshalToMemory(certificate{ +		c, +		algorithmIdentifier{oidSHA1WithRSA}, +		asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, +	}) +	return +} diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index 796807b4b..49560267b 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -9,9 +9,11 @@ import (  	"crypto/rsa"  	"encoding/hex"  	"encoding/pem" +	"os"  	"reflect"  	"strings"  	"testing" +	"time"  )  func TestParsePKCS1PrivateKey(t *testing.T) { @@ -142,3 +144,51 @@ var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be13  	"e9e83994dcab72792f06bfab8170c4a8edea5334edef1e53d906c7562bd15cf4d18a8eb42bb137" +  	"9048084225c53e8acb7feb6f04d16dc574a2f7a27c7b603c77cd0ece48027f012fb69b37e02a2a" +  	"36dcd585d6ace53f546f961e05af" + +func TestCreateSelfSignedCertificate(t *testing.T) { +	urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0) +	if err != nil { +		t.Errorf("failed to open /dev/urandom") +	} + +	block, _ := pem.Decode(strings.Bytes(pemPrivateKey)) +	priv, err := ParsePKCS1PrivateKey(block.Bytes) +	if err != nil { +		t.Errorf("Failed to parse private key: %s", err) +		return +	} + +	template := Certificate{ +		SerialNumber: []byte{1}, +		Subject: Name{ +			CommonName: "test.example.com", +			Organization: "Acme Co", +		}, +		NotBefore: time.SecondsToUTC(1000), +		NotAfter: time.SecondsToUTC(100000), + +		SubjectKeyId: []byte{1, 2, 3, 4}, +		KeyUsage: KeyUsageCertSign, + +		BasicConstraintsValid: true, +		IsCA: true, +		DNSNames: []string{"test.example.com"}, +	} + +	derBytes, err := CreateCertificate(urandom, &template, &template, priv) +	if err != nil { +		t.Errorf("Failed to create certificate: %s", err) +		return +	} + +	cert, err := ParseCertificate(derBytes) +	if err != nil { +		t.Errorf("Failed to parse certificate: %s", err) +		return +	} +	err = cert.CheckSignatureFrom(cert) +	if err != nil { +		t.Errorf("Signature verification failed: %s", err) +		return +	} +} | 
