1
0
mirror of https://github.com/golang/go synced 2024-11-25 11:07:59 -07:00

crypto: reorg, cleanup and add function for generating CRLs.

This change moves a number of common PKIX structures into
crypto/x509/pkix, from where x509, and ocsp can reference
them, saving duplication. It also removes x509/crl and merges it into
x509 and x509/pkix.

x509 is changed to take advantage of the big.Int support that now
exists in asn1. Because of this, the public/private key pair in
http/httptest/server.go had to be updated because it was serialised
with an old version of the code that didn't zero pad ASN.1 INTEGERs.

R=bradfitz, rsc
CC=golang-dev
https://golang.org/cl/4532115
This commit is contained in:
Adam Langley 2011-06-06 10:35:46 -04:00
parent 548e3d0342
commit c281ddf1eb
12 changed files with 438 additions and 421 deletions

View File

@ -59,7 +59,7 @@ DIRS=\
crypto/tls\ crypto/tls\
crypto/twofish\ crypto/twofish\
crypto/x509\ crypto/x509\
crypto/x509/crl\ crypto/x509/pkix\
crypto/xtea\ crypto/xtea\
debug/dwarf\ debug/dwarf\
debug/macho\ debug/macho\
@ -184,6 +184,7 @@ endif
NOTEST+=\ NOTEST+=\
crypto\ crypto\
crypto/openpgp/error\ crypto/openpgp/error\
crypto/x509/pkix\
debug/proc\ debug/proc\
exp/gui\ exp/gui\
exp/gui/x11\ exp/gui/x11\

View File

@ -13,6 +13,7 @@ import (
"crypto/rsa" "crypto/rsa"
_ "crypto/sha1" _ "crypto/sha1"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"os" "os"
"time" "time"
) )
@ -32,21 +33,9 @@ const (
ocspUnauthorized = 5 ocspUnauthorized = 5
) )
type rdnSequence []relativeDistinguishedNameSET
type relativeDistinguishedNameSET []attributeTypeAndValue
type attributeTypeAndValue struct {
Type asn1.ObjectIdentifier
Value interface{}
}
type algorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier
}
type certID struct { type certID struct {
HashAlgorithm algorithmIdentifier HashAlgorithm pkix.AlgorithmIdentifier
NameHash []byte NameHash []byte
IssuerKeyHash []byte IssuerKeyHash []byte
SerialNumber asn1.RawValue SerialNumber asn1.RawValue
@ -64,7 +53,7 @@ type responseBytes struct {
type basicResponse struct { type basicResponse struct {
TBSResponseData responseData TBSResponseData responseData
SignatureAlgorithm algorithmIdentifier SignatureAlgorithm pkix.AlgorithmIdentifier
Signature asn1.BitString Signature asn1.BitString
Certificates []asn1.RawValue "explicit,tag:0,optional" Certificates []asn1.RawValue "explicit,tag:0,optional"
} }
@ -72,7 +61,7 @@ type basicResponse struct {
type responseData struct { type responseData struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int "optional,default:1,explicit,tag:0" Version int "optional,default:1,explicit,tag:0"
RequestorName rdnSequence "optional,explicit,tag:1" RequestorName pkix.RDNSequence "optional,explicit,tag:1"
KeyHash []byte "optional,explicit,tag:2" KeyHash []byte "optional,explicit,tag:2"
ProducedAt *time.Time ProducedAt *time.Time
Responses []singleResponse Responses []singleResponse

View File

@ -64,7 +64,7 @@ func (priv *PrivateKey) Validate() os.Error {
// easy for an attack to generate composites that pass this test. // easy for an attack to generate composites that pass this test.
for _, prime := range priv.Primes { for _, prime := range priv.Primes {
if !big.ProbablyPrime(prime, 20) { if !big.ProbablyPrime(prime, 20) {
return os.ErrorString("Prime factor is composite") return os.ErrorString("prime factor is composite")
} }
} }

View File

@ -159,7 +159,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err
key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
if err != nil { if err != nil {
err = os.ErrorString("crypto/tls: failed to parse key") err = os.ErrorString("crypto/tls: failed to parse key: " + err.String())
return return
} }

View File

@ -5,6 +5,7 @@
package x509 package x509
import ( import (
"crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"strings" "strings"
) )
@ -25,7 +26,7 @@ func NewCertPool() *CertPool {
} }
} }
func nameToKey(name *Name) string { func nameToKey(name *pkix.Name) string {
return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
} }

View File

@ -1,96 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package crl exposes low-level details of PKIX Certificate Revocation Lists
// as specified in RFC 5280, section 5.
package crl
import (
"asn1"
"bytes"
"encoding/pem"
"os"
"time"
)
// CertificateList represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1. Use crypto/x509/Certificate.CheckCRLSignature to verify
// the signature.
type CertificateList struct {
TBSCertList TBSCertificateList
SignatureAlgorithm AlgorithmIdentifier
SignatureValue asn1.BitString
}
// HasExpired returns true iff currentTimeSeconds is past the expiry time of
// certList.
func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds
}
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
type TBSCertificateList struct {
Raw asn1.RawContent
Version int "optional,default:2"
Signature AlgorithmIdentifier
Issuer asn1.RawValue
ThisUpdate *time.Time
NextUpdate *time.Time
RevokedCertificates []RevokedCertificate "optional"
Extensions []Extension "tag:0,optional,explicit"
}
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
// 5280, section 4.1.1.2.
type AlgorithmIdentifier struct {
Algo asn1.ObjectIdentifier
Params asn1.RawValue "optional"
}
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
type RevokedCertificate struct {
SerialNumber asn1.RawValue
RevocationTime *time.Time
Extensions []Extension "optional"
}
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
// 5280, section 4.2.
type Extension struct {
Id asn1.ObjectIdentifier
IsCritial bool "optional"
Value []byte
}
// pemCRLPrefix is the magic string that indicates that we have a PEM encoded
// CRL.
var pemCRLPrefix = []byte("-----BEGIN X509 CRL")
// pemType is the type of a PEM encoded CRL.
var pemType = "X509 CRL"
// Parse parses a CRL from the given bytes. It's often the case that PEM
// encoded CRLs will appear where they should be DER encoded, so this function
// will transparently handle PEM encoding as long as there isn't any leading
// garbage.
func Parse(crlBytes []byte) (certList *CertificateList, err os.Error) {
if bytes.HasPrefix(crlBytes, pemCRLPrefix) {
block, _ := pem.Decode(crlBytes)
if block != nil && block.Type == pemType {
crlBytes = block.Bytes
}
}
return ParseDER(crlBytes)
}
// ParseDER parses a DER encoded CRL from the given bytes.
func ParseDER(derBytes []byte) (certList *CertificateList, err os.Error) {
certList = new(CertificateList)
_, err = asn1.Unmarshal(derBytes, certList)
if err != nil {
certList = nil
}
return
}

View File

@ -1,63 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package crl
import (
"encoding/base64"
"testing"
)
func fromBase64(in string) []byte {
out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
_, err := base64.StdEncoding.Decode(out, []byte(in))
if err != nil {
panic("failed to base64 decode")
}
return out
}
func TestParseDER(t *testing.T) {
derBytes := fromBase64(derCRLBase64)
certList, err := ParseDER(derBytes)
if err != nil {
t.Errorf("error parsing: %s", err)
return
}
numCerts := len(certList.TBSCertList.RevokedCertificates)
expected := 88
if numCerts != expected {
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
}
if certList.HasExpired(1302517272) {
t.Errorf("CRL has expired (but shouldn't have)")
}
// Can't check the signature here without a package cycle.
}
func TestParsePEM(t *testing.T) {
pemBytes := fromBase64(pemCRLBase64)
certList, err := Parse(pemBytes)
if err != nil {
t.Errorf("error parsing: %s", err)
return
}
numCerts := len(certList.TBSCertList.RevokedCertificates)
expected := 2
if numCerts != expected {
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
}
if certList.HasExpired(1302517272) {
t.Errorf("CRL has expired (but shouldn't have)")
}
// Can't check the signature here without a package cycle.
}
const derCRLBase64 = "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"
const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K"

View File

@ -4,8 +4,8 @@
include ../../../../Make.inc include ../../../../Make.inc
TARG=crypto/x509/crl TARG=crypto/x509/pkix
GOFILES=\ GOFILES=\
crl.go\ pkix.go\
include ../../../../Make.pkg include ../../../../Make.pkg

View File

@ -0,0 +1,167 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package pkix contains shared, low level structures used for ASN.1 parsing
// and serialization of X.509 certificates, CRL and OCSP.
package pkix
import (
"asn1"
"big"
"time"
)
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
// 5280, section 4.1.1.2.
type AlgorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier
Parameters asn1.RawValue "optional"
}
type RDNSequence []RelativeDistinguishedNameSET
type RelativeDistinguishedNameSET []AttributeTypeAndValue
type AttributeTypeAndValue struct {
Type asn1.ObjectIdentifier
Value interface{}
}
// Extension represents the ASN.1 structure of the same name. See RFC
// 5280, section 4.2.
type Extension struct {
Id asn1.ObjectIdentifier
Critical bool "optional"
Value []byte
}
// Name represents an X.509 distinguished name. This only includes the common
// elements of a DN. Additional elements in the name are ignored.
type Name struct {
Country, Organization, OrganizationalUnit []string
Locality, Province []string
StreetAddress, PostalCode []string
SerialNumber, CommonName string
}
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
for _, rdn := range *rdns {
if len(rdn) == 0 {
continue
}
atv := rdn[0]
value, ok := atv.Value.(string)
if !ok {
continue
}
t := atv.Type
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
switch t[3] {
case 3:
n.CommonName = value
case 5:
n.SerialNumber = value
case 6:
n.Country = append(n.Country, value)
case 7:
n.Locality = append(n.Locality, value)
case 8:
n.Province = append(n.Province, value)
case 9:
n.StreetAddress = append(n.StreetAddress, value)
case 10:
n.Organization = append(n.Organization, value)
case 11:
n.OrganizationalUnit = append(n.OrganizationalUnit, value)
case 17:
n.PostalCode = append(n.PostalCode, value)
}
}
}
}
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}
oidLocality = []int{2, 5, 4, 7}
oidProvince = []int{2, 5, 4, 8}
oidStreetAddress = []int{2, 5, 4, 9}
oidPostalCode = []int{2, 5, 4, 17}
)
// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence
// and returns the new value. The relativeDistinguishedNameSET contains an
// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
// search for AttributeTypeAndValue.
func appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence {
if len(values) == 0 {
return in
}
s := make([]AttributeTypeAndValue, len(values))
for i, value := range values {
s[i].Type = oid
s[i].Value = value
}
return append(in, s)
}
func (n Name) ToRDNSequence() (ret RDNSequence) {
ret = appendRDNs(ret, n.Country, oidCountry)
ret = appendRDNs(ret, n.Organization, oidOrganization)
ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
ret = appendRDNs(ret, n.Locality, oidLocality)
ret = appendRDNs(ret, n.Province, oidProvince)
ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress)
ret = appendRDNs(ret, n.PostalCode, oidPostalCode)
if len(n.CommonName) > 0 {
ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName)
}
if len(n.SerialNumber) > 0 {
ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
}
return ret
}
// CertificateList represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the
// signature.
type CertificateList struct {
TBSCertList TBSCertificateList
SignatureAlgorithm AlgorithmIdentifier
SignatureValue asn1.BitString
}
// HasExpired returns true iff currentTimeSeconds is past the expiry time of
// certList.
func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds
}
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
type TBSCertificateList struct {
Raw asn1.RawContent
Version int "optional,default:2"
Signature AlgorithmIdentifier
Issuer RDNSequence
ThisUpdate *time.Time
NextUpdate *time.Time
RevokedCertificates []RevokedCertificate "optional"
Extensions []Extension "tag:0,optional,explicit"
}
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime *time.Time
Extensions []Extension "optional"
}

View File

@ -14,7 +14,8 @@ import (
"crypto/dsa" "crypto/dsa"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/x509/crl" "crypto/x509/pkix"
"encoding/pem"
"io" "io"
"os" "os"
"time" "time"
@ -23,30 +24,25 @@ import (
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. // pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
type pkcs1PrivateKey struct { type pkcs1PrivateKey struct {
Version int Version int
N asn1.RawValue N *big.Int
E int E int
D asn1.RawValue D *big.Int
P asn1.RawValue P *big.Int
Q asn1.RawValue Q *big.Int
// We ignore these values, if present, because rsa will calculate them. // We ignore these values, if present, because rsa will calculate them.
Dp asn1.RawValue "optional" Dp *big.Int "optional"
Dq asn1.RawValue "optional" Dq *big.Int "optional"
Qinv asn1.RawValue "optional" Qinv *big.Int "optional"
AdditionalPrimes []pkcs1AdditionalRSAPrime "optional" AdditionalPrimes []pkcs1AdditionalRSAPrime "optional"
} }
type pkcs1AdditionalRSAPrime struct { type pkcs1AdditionalRSAPrime struct {
Prime asn1.RawValue Prime *big.Int
// We ignore these values because rsa will calculate them. // We ignore these values because rsa will calculate them.
Exp asn1.RawValue Exp *big.Int
Coeff asn1.RawValue Coeff *big.Int
}
// rawValueIsInteger returns true iff the given ASN.1 RawValue is an INTEGER type.
func rawValueIsInteger(raw *asn1.RawValue) bool {
return raw.Class == 0 && raw.Tag == 2 && raw.IsCompound == false
} }
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
@ -65,29 +61,25 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
return nil, os.ErrorString("x509: unsupported private key version") return nil, os.ErrorString("x509: unsupported private key version")
} }
if !rawValueIsInteger(&priv.N) || if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
!rawValueIsInteger(&priv.D) || return nil, os.ErrorString("private key contains zero or negative value")
!rawValueIsInteger(&priv.P) ||
!rawValueIsInteger(&priv.Q) {
err = asn1.StructuralError{"tags don't match"}
return
} }
key = new(rsa.PrivateKey) key = new(rsa.PrivateKey)
key.PublicKey = rsa.PublicKey{ key.PublicKey = rsa.PublicKey{
E: priv.E, E: priv.E,
N: new(big.Int).SetBytes(priv.N.Bytes), N: priv.N,
} }
key.D = new(big.Int).SetBytes(priv.D.Bytes) key.D = priv.D
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
key.Primes[0] = new(big.Int).SetBytes(priv.P.Bytes) key.Primes[0] = priv.P
key.Primes[1] = new(big.Int).SetBytes(priv.Q.Bytes) key.Primes[1] = priv.Q
for i, a := range priv.AdditionalPrimes { for i, a := range priv.AdditionalPrimes {
if !rawValueIsInteger(&a.Prime) { if a.Prime.Sign() <= 0 {
return nil, asn1.StructuralError{"tags don't match"} return nil, os.ErrorString("private key contains zero or negative prime")
} }
key.Primes[i+2] = new(big.Int).SetBytes(a.Prime.Bytes) key.Primes[i+2] = a.Prime
// We ignore the other two values because rsa will calculate // We ignore the other two values because rsa will calculate
// them as needed. // them as needed.
} }
@ -101,19 +93,6 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
return return
} }
// rawValueForBig returns an asn1.RawValue which represents the given integer.
func rawValueForBig(n *big.Int) asn1.RawValue {
b := n.Bytes()
if n.Sign() >= 0 && len(b) > 0 && b[0]&0x80 != 0 {
// This positive number would be interpreted as a negative
// number in ASN.1 because the MSB is set.
padded := make([]byte, len(b)+1)
copy(padded[1:], b)
b = padded
}
return asn1.RawValue{Tag: 2, Bytes: b}
}
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. // MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
key.Precompute() key.Precompute()
@ -125,21 +104,21 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
priv := pkcs1PrivateKey{ priv := pkcs1PrivateKey{
Version: version, Version: version,
N: rawValueForBig(key.N), N: key.N,
E: key.PublicKey.E, E: key.PublicKey.E,
D: rawValueForBig(key.D), D: key.D,
P: rawValueForBig(key.Primes[0]), P: key.Primes[0],
Q: rawValueForBig(key.Primes[1]), Q: key.Primes[1],
Dp: rawValueForBig(key.Precomputed.Dp), Dp: key.Precomputed.Dp,
Dq: rawValueForBig(key.Precomputed.Dq), Dq: key.Precomputed.Dq,
Qinv: rawValueForBig(key.Precomputed.Qinv), Qinv: key.Precomputed.Qinv,
} }
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
for i, values := range key.Precomputed.CRTValues { for i, values := range key.Precomputed.CRTValues {
priv.AdditionalPrimes[i].Prime = rawValueForBig(key.Primes[2+i]) priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
priv.AdditionalPrimes[i].Exp = rawValueForBig(values.Exp) priv.AdditionalPrimes[i].Exp = values.Exp
priv.AdditionalPrimes[i].Coeff = rawValueForBig(values.Coeff) priv.AdditionalPrimes[i].Coeff = values.Coeff
} }
b, _ := asn1.Marshal(priv) b, _ := asn1.Marshal(priv)
@ -151,44 +130,30 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
type certificate struct { type certificate struct {
Raw asn1.RawContent Raw asn1.RawContent
TBSCertificate tbsCertificate TBSCertificate tbsCertificate
SignatureAlgorithm algorithmIdentifier SignatureAlgorithm pkix.AlgorithmIdentifier
SignatureValue asn1.BitString SignatureValue asn1.BitString
} }
type tbsCertificate struct { type tbsCertificate struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int "optional,explicit,default:1,tag:0" Version int "optional,explicit,default:1,tag:0"
SerialNumber asn1.RawValue SerialNumber *big.Int
SignatureAlgorithm algorithmIdentifier SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer rdnSequence Issuer pkix.RDNSequence
Validity validity Validity validity
Subject rdnSequence Subject pkix.RDNSequence
PublicKey publicKeyInfo PublicKey publicKeyInfo
UniqueId asn1.BitString "optional,tag:1" UniqueId asn1.BitString "optional,tag:1"
SubjectUniqueId asn1.BitString "optional,tag:2" SubjectUniqueId asn1.BitString "optional,tag:2"
Extensions []extension "optional,explicit,tag:3" Extensions []pkix.Extension "optional,explicit,tag:3"
} }
type dsaAlgorithmParameters struct { type dsaAlgorithmParameters struct {
P, Q, G asn1.RawValue P, Q, G *big.Int
} }
type dsaSignature struct { type dsaSignature struct {
R, S asn1.RawValue R, S *big.Int
}
type algorithmIdentifier struct {
Algorithm asn1.ObjectIdentifier
Parameters asn1.RawValue "optional"
}
type rdnSequence []relativeDistinguishedNameSET
type relativeDistinguishedNameSET []attributeTypeAndValue
type attributeTypeAndValue struct {
Type asn1.ObjectIdentifier
Value interface{}
} }
type validity struct { type validity struct {
@ -197,16 +162,10 @@ type validity struct {
type publicKeyInfo struct { type publicKeyInfo struct {
Raw asn1.RawContent Raw asn1.RawContent
Algorithm algorithmIdentifier Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString PublicKey asn1.BitString
} }
type extension struct {
Id asn1.ObjectIdentifier
Critical bool "optional"
Value []byte
}
// RFC 5280, 4.2.1.1 // RFC 5280, 4.2.1.1
type authKeyId struct { type authKeyId struct {
Id []byte "optional,tag:0" Id []byte "optional,tag:0"
@ -234,100 +193,6 @@ const (
DSA DSA
) )
// Name represents an X.509 distinguished name. This only includes the common
// elements of a DN. Additional elements in the name are ignored.
type Name struct {
Country, Organization, OrganizationalUnit []string
Locality, Province []string
StreetAddress, PostalCode []string
SerialNumber, CommonName string
}
func (n *Name) fillFromRDNSequence(rdns *rdnSequence) {
for _, rdn := range *rdns {
if len(rdn) == 0 {
continue
}
atv := rdn[0]
value, ok := atv.Value.(string)
if !ok {
continue
}
t := atv.Type
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
switch t[3] {
case 3:
n.CommonName = value
case 5:
n.SerialNumber = value
case 6:
n.Country = append(n.Country, value)
case 7:
n.Locality = append(n.Locality, value)
case 8:
n.Province = append(n.Province, value)
case 9:
n.StreetAddress = append(n.StreetAddress, value)
case 10:
n.Organization = append(n.Organization, value)
case 11:
n.OrganizationalUnit = append(n.OrganizationalUnit, value)
case 17:
n.PostalCode = append(n.PostalCode, value)
}
}
}
}
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}
oidLocality = []int{2, 5, 4, 7}
oidProvince = []int{2, 5, 4, 8}
oidStreetAddress = []int{2, 5, 4, 9}
oidPostalCode = []int{2, 5, 4, 17}
)
// appendRDNs appends a relativeDistinguishedNameSET to the given rdnSequence
// and returns the new value. The relativeDistinguishedNameSET contains an
// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
// search for AttributeTypeAndValue.
func appendRDNs(in rdnSequence, values []string, oid asn1.ObjectIdentifier) rdnSequence {
if len(values) == 0 {
return in
}
s := make([]attributeTypeAndValue, len(values))
for i, value := range values {
s[i].Type = oid
s[i].Value = value
}
return append(in, s)
}
func (n Name) toRDNSequence() (ret rdnSequence) {
ret = appendRDNs(ret, n.Country, oidCountry)
ret = appendRDNs(ret, n.Organization, oidOrganization)
ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
ret = appendRDNs(ret, n.Locality, oidLocality)
ret = appendRDNs(ret, n.Province, oidProvince)
ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress)
ret = appendRDNs(ret, n.PostalCode, oidPostalCode)
if len(n.CommonName) > 0 {
ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName)
}
if len(n.SerialNumber) > 0 {
ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
}
return ret
}
// OIDs for signature algorithms // OIDs for signature algorithms
// //
// pkcs-1 OBJECT IDENTIFIER ::= { // pkcs-1 OBJECT IDENTIFIER ::= {
@ -483,9 +348,9 @@ type Certificate struct {
PublicKey interface{} PublicKey interface{}
Version int Version int
SerialNumber []byte SerialNumber *big.Int
Issuer Name Issuer pkix.Name
Subject Name Subject pkix.Name
NotBefore, NotAfter *time.Time // Validity bounds. NotBefore, NotAfter *time.Time // Validity bounds.
KeyUsage KeyUsage KeyUsage KeyUsage
@ -591,12 +456,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { if _, err := asn1.Unmarshal(signature, dsaSig); err != nil {
return err return err
} }
if !rawValueIsInteger(&dsaSig.R) || !rawValueIsInteger(&dsaSig.S) { if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
return asn1.StructuralError{"tags don't match"} return os.ErrorString("DSA signature contained zero or negative values")
} }
r := new(big.Int).SetBytes(dsaSig.R.Bytes) if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) {
s := new(big.Int).SetBytes(dsaSig.S.Bytes)
if !dsa.Verify(pub, digest, r, s) {
return os.ErrorString("DSA verification failure") return os.ErrorString("DSA verification failure")
} }
return return
@ -605,8 +468,8 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
} }
// CheckCRLSignature checks that the signature in crl is from c. // CheckCRLSignature checks that the signature in crl is from c.
func (c *Certificate) CheckCRLSignature(crl *crl.CertificateList) (err os.Error) { func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err os.Error) {
algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algo) algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm)
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
} }
@ -622,7 +485,7 @@ type basicConstraints struct {
} }
type rsaPublicKey struct { type rsaPublicKey struct {
N asn1.RawValue N *big.Int
E int E int
} }
@ -654,42 +517,33 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
return nil, err return nil, err
} }
if !rawValueIsInteger(&p.N) {
return nil, asn1.StructuralError{"tags don't match"}
}
pub := &rsa.PublicKey{ pub := &rsa.PublicKey{
E: p.E, E: p.E,
N: new(big.Int).SetBytes(p.N.Bytes), N: p.N,
} }
return pub, nil return pub, nil
case DSA: case DSA:
p := new(asn1.RawValue) var p *big.Int
_, err := asn1.Unmarshal(asn1Data, p) _, err := asn1.Unmarshal(asn1Data, &p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !rawValueIsInteger(p) {
return nil, asn1.StructuralError{"tags don't match"}
}
paramsData := keyData.Algorithm.Parameters.FullBytes paramsData := keyData.Algorithm.Parameters.FullBytes
params := new(dsaAlgorithmParameters) params := new(dsaAlgorithmParameters)
_, err = asn1.Unmarshal(paramsData, params) _, err = asn1.Unmarshal(paramsData, params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !rawValueIsInteger(&params.P) || if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 {
!rawValueIsInteger(&params.Q) || return nil, os.ErrorString("zero or negative DSA parameter")
!rawValueIsInteger(&params.G) {
return nil, asn1.StructuralError{"tags don't match"}
} }
pub := &dsa.PublicKey{ pub := &dsa.PublicKey{
Parameters: dsa.Parameters{ Parameters: dsa.Parameters{
P: new(big.Int).SetBytes(params.P.Bytes), P: params.P,
Q: new(big.Int).SetBytes(params.Q.Bytes), Q: params.Q,
G: new(big.Int).SetBytes(params.G.Bytes), G: params.G,
}, },
Y: new(big.Int).SetBytes(p.Bytes), Y: p,
} }
return pub, nil return pub, nil
default: default:
@ -716,10 +570,14 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {
return nil, err return nil, err
} }
if in.TBSCertificate.SerialNumber.Sign() < 0 {
return nil, os.ErrorString("negative serial number")
}
out.Version = in.TBSCertificate.Version + 1 out.Version = in.TBSCertificate.Version + 1
out.SerialNumber = in.TBSCertificate.SerialNumber.Bytes out.SerialNumber = in.TBSCertificate.SerialNumber
out.Issuer.fillFromRDNSequence(&in.TBSCertificate.Issuer) out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer)
out.Subject.fillFromRDNSequence(&in.TBSCertificate.Subject) out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject)
out.NotBefore = in.TBSCertificate.Validity.NotBefore out.NotBefore = in.TBSCertificate.Validity.NotBefore
out.NotAfter = in.TBSCertificate.Validity.NotAfter out.NotAfter = in.TBSCertificate.Validity.NotAfter
@ -977,8 +835,8 @@ var (
oidExtensionNameConstraints = []int{2, 5, 29, 30} oidExtensionNameConstraints = []int{2, 5, 29, 30}
) )
func buildExtensions(template *Certificate) (ret []extension, err os.Error) { func buildExtensions(template *Certificate) (ret []pkix.Extension, err os.Error) {
ret = make([]extension, 7 /* maximum number of elements. */ ) ret = make([]pkix.Extension, 7 /* maximum number of elements. */ )
n := 0 n := 0
if template.KeyUsage != 0 { if template.KeyUsage != 0 {
@ -1095,7 +953,7 @@ var (
// The returned slice is the certificate in DER encoding. // The returned slice is the certificate in DER encoding.
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) { func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) {
asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ asn1PublicKey, err := asn1.Marshal(rsaPublicKey{
N: asn1.RawValue{Tag: 2, Bytes: pub.N.Bytes()}, N: pub.N,
E: pub.E, E: pub.E,
}) })
if err != nil { if err != nil {
@ -1114,12 +972,12 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}
c := tbsCertificate{ c := tbsCertificate{
Version: 2, Version: 2,
SerialNumber: asn1.RawValue{Bytes: template.SerialNumber, Tag: 2}, SerialNumber: template.SerialNumber,
SignatureAlgorithm: algorithmIdentifier{Algorithm: oidSHA1WithRSA}, SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
Issuer: parent.Subject.toRDNSequence(), Issuer: parent.Subject.ToRDNSequence(),
Validity: validity{template.NotBefore, template.NotAfter}, Validity: validity{template.NotBefore, template.NotAfter},
Subject: template.Subject.toRDNSequence(), Subject: template.Subject.ToRDNSequence(),
PublicKey: publicKeyInfo{nil, algorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},
Extensions: extensions, Extensions: extensions,
} }
@ -1142,8 +1000,75 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
cert, err = asn1.Marshal(certificate{ cert, err = asn1.Marshal(certificate{
nil, nil,
c, c,
algorithmIdentifier{Algorithm: oidSHA1WithRSA}, pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
}) })
return return
} }
// pemCRLPrefix is the magic string that indicates that we have a PEM encoded
// CRL.
var pemCRLPrefix = []byte("-----BEGIN X509 CRL")
// pemType is the type of a PEM encoded CRL.
var pemType = "X509 CRL"
// ParseCRL parses a CRL from the given bytes. It's often the case that PEM
// encoded CRLs will appear where they should be DER encoded, so this function
// will transparently handle PEM encoding as long as there isn't any leading
// garbage.
func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) {
if bytes.HasPrefix(crlBytes, pemCRLPrefix) {
block, _ := pem.Decode(crlBytes)
if block != nil && block.Type == pemType {
crlBytes = block.Bytes
}
}
return ParseDERCRL(crlBytes)
}
// ParseDERCRL parses a DER encoded CRL from the given bytes.
func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) {
certList = new(pkix.CertificateList)
_, err = asn1.Unmarshal(derBytes, certList)
if err != nil {
certList = nil
}
return
}
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
// contains the given list of revoked certificates.
func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err os.Error) {
tbsCertList := pkix.TBSCertificateList{
Version: 2,
Signature: pkix.AlgorithmIdentifier{
Algorithm: oidSignatureSHA1WithRSA,
},
Issuer: c.Subject.ToRDNSequence(),
ThisUpdate: now,
NextUpdate: expiry,
RevokedCertificates: revokedCerts,
}
tbsCertListContents, err := asn1.Marshal(tbsCertList)
if err != nil {
return
}
h := sha1.New()
h.Write(tbsCertListContents)
digest := h.Sum()
signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest)
if err != nil {
return
}
return asn1.Marshal(pkix.CertificateList{
TBSCertList: tbsCertList,
SignatureAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oidSignatureSHA1WithRSA,
},
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
}

View File

@ -10,6 +10,8 @@ import (
"crypto/dsa" "crypto/dsa"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509/pkix"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/pem" "encoding/pem"
"testing" "testing"
@ -207,8 +209,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
} }
template := Certificate{ template := Certificate{
SerialNumber: []byte{1}, SerialNumber: big.NewInt(1),
Subject: Name{ Subject: pkix.Name{
CommonName: "test.example.com", CommonName: "test.example.com",
Organization: []string{"Acme Co"}, Organization: []string{"Acme Co"},
}, },
@ -331,3 +333,99 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) {
t.Fatalf("DSA Certificate verfication failed: %s", err) t.Fatalf("DSA Certificate verfication failed: %s", err)
} }
} }
const pemCertificate = `-----BEGIN CERTIFICATE-----
MIIB5DCCAZCgAwIBAgIBATALBgkqhkiG9w0BAQUwLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UE
AxMQdGVzdC5leGFtcGxlLmNvbTAeFw03MDAxMDEwMDE2NDBaFw03MDAxMDIwMzQ2NDBaMC0xEDAO
BgNVBAoTB0FjbWUgQ28xGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wWjALBgkqhkiG9w0BAQED
SwAwSAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0fd7Ai2KW5ToIwzFo
fvJcS/STa6HA5gQenRUCAwEAAaOBnjCBmzAOBgNVHQ8BAf8EBAMCAAQwDwYDVR0TAQH/BAUwAwEB
/zANBgNVHQ4EBgQEAQIDBDAPBgNVHSMECDAGgAQBAgMEMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBs
ZS5jb20wDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4
YW1wbGUuY29tMAsGCSqGSIb3DQEBBQNBAHKZKoS1wEQOGhgklx4+/yFYQlnqwKXvar/ZecQvJwui
0seMQnwBhwdBkHfVIU2Fu5VUMRyxlf0ZNaDXcpU581k=
-----END CERTIFICATE-----`
func TestCRLCreation(t *testing.T) {
block, _ := pem.Decode([]byte(pemPrivateKey))
priv, _ := ParsePKCS1PrivateKey(block.Bytes)
block, _ = pem.Decode([]byte(pemCertificate))
cert, _ := ParseCertificate(block.Bytes)
now := time.SecondsToUTC(1000)
expiry := time.SecondsToUTC(10000)
revokedCerts := []pkix.RevokedCertificate{
{
SerialNumber: big.NewInt(1),
RevocationTime: now,
},
{
SerialNumber: big.NewInt(42),
RevocationTime: now,
},
}
crlBytes, err := cert.CreateCRL(rand.Reader, priv, revokedCerts, now, expiry)
if err != nil {
t.Errorf("error creating CRL: %s", err)
}
_, err = ParseDERCRL(crlBytes)
if err != nil {
t.Errorf("error reparsing CRL: %s", err)
}
}
func fromBase64(in string) []byte {
out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
_, err := base64.StdEncoding.Decode(out, []byte(in))
if err != nil {
panic("failed to base64 decode")
}
return out
}
func TestParseDERCRL(t *testing.T) {
derBytes := fromBase64(derCRLBase64)
certList, err := ParseDERCRL(derBytes)
if err != nil {
t.Errorf("error parsing: %s", err)
return
}
numCerts := len(certList.TBSCertList.RevokedCertificates)
expected := 88
if numCerts != expected {
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
}
if certList.HasExpired(1302517272) {
t.Errorf("CRL has expired (but shouldn't have)")
}
// Can't check the signature here without a package cycle.
}
func TestParsePEMCRL(t *testing.T) {
pemBytes := fromBase64(pemCRLBase64)
certList, err := ParseCRL(pemBytes)
if err != nil {
t.Errorf("error parsing: %s", err)
return
}
numCerts := len(certList.TBSCertList.RevokedCertificates)
expected := 2
if numCerts != expected {
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
}
if certList.HasExpired(1302517272) {
t.Errorf("CRL has expired (but shouldn't have)")
}
// Can't check the signature here without a package cycle.
}
const derCRLBase64 = "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"
const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K"

View File

@ -108,29 +108,24 @@ func (s *Server) CloseClientConnections() {
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time). // of ASN.1 time).
var localhostCert = []byte(`-----BEGIN CERTIFICATE----- var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBwTCCASugAwIBAgIBADALBgkqhkiG9w0BAQUwADAeFw0xMTAzMzEyMDI1MDda MIIBOTCB5qADAgECAgEAMAsGCSqGSIb3DQEBBTAAMB4XDTcwMDEwMTAwMDAwMFoX
Fw00OTEyMzEyMzU5NTlaMAAwggCdMAsGCSqGSIb3DQEBAQOCAIwAMIIAhwKCAIB6 DTQ5MTIzMTIzNTk1OVowADBaMAsGCSqGSIb3DQEBAQNLADBIAkEAsuA5mAFMj6Q7
oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZLKq2sM3gRaimsktIw qoBzcvKzIq4kzuT5epSp2AkcQfyBHm7K13Ws7u+0b5Vb9gqTf5cAiIKcrtrXVqkL
nNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0SjdZ7vTPnFDPNsHGe 8i1UQF6AzwIDAQABo08wTTAOBgNVHQ8BAf8EBAMCACQwDQYDVR0OBAYEBAECAwQw
KBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBA6NPME0wDgYDVR0PAQH/ DwYDVR0jBAgwBoAEAQIDBDAbBgNVHREEFDASggkxMjcuMC4wLjGCBVs6OjFdMAsG
BAQDAgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwGwYDVR0RBBQw CSqGSIb3DQEBBQNBAJH30zjLWRztrWpOCgJL8RQWLaKzhK79pVhAx6q/3NrF16C7
EoIJMTI3LjAuMC4xggVbOjoxXTALBgkqhkiG9w0BAQUDggCBAHC3gbdvc44vs+wD +l1BRZstTwIGdoGId8BRpErK1TXkniFb95ZMynM=
g2kONiENnx8WKc0UTGg/TOXS3gaRb+CUIQtHWja65l8rAfclEovjHgZ7gx8brO0W
JuC6p3MUAKsgOssIrrRIx2rpnfcmFVMzguCmrMNVmKUAalw18Yp0F72xYAIitVQl
kJrLdIhBajcJRYu/YGltHQRaXuVt
-----END CERTIFICATE----- -----END CERTIFICATE-----
`) `)
// localhostKey is the private key for localhostCert. // localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBkgIBAQKCAIB6oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZL MIIBPQIBAAJBALLgOZgBTI+kO6qAc3LysyKuJM7k+XqUqdgJHEH8gR5uytd1rO7v
Kq2sM3gRaimsktIwnNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0S tG+VW/YKk3+XAIiCnK7a11apC/ItVEBegM8CAwEAAQJBAI5sxq7naeR9ahyqRkJi
jdZ7vTPnFDPNsHGeKBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBAwKC SIv2iMxLuPEHaezf5CYOPWjSjBPyVhyRevkhtqEjF/WkgL7C2nWpYHsUcBDBQVF0
AIBRwh7Bil5Z8cYpZZv7jdQxDvbim7Z7ocRdeDmzZuF2I9RW04QyHHPIIlALnBvI 3KECIQDtEGB2ulnkZAahl3WuJziXGLB+p8Wgx7wzSM6bHu1c6QIhAMEp++CaS+SJ
YeF1veASz1gEFGUjzmbUGqKYSbCoTzXoev+F4bmbRxcX9sOmtslqvhMSHRSzA5NH /TrU0zwY/fW4SvQeb49BPZUF3oqR8Xz3AiEA1rAJHBzBgdOQKdE3ksMUPcnvNJSN
aDVI3Hn4wvBVD8gePu8ACWqvPGbCiql11OKCMfjlPn2uuwJAx/24/F5DjXZ6hQQ7 poCcELmz2clVXtkCIQCLytuLV38XHToTipR4yMl6O+6arzAjZ56uq7m7ZRV0TwIh
HxScOxKrpx5WnA9r1wZTltOTZkhRRzuLc21WJeE3M15QUdWi3zZxCKRFoth65HEs AM65XAOw8Dsg9Kq78aYXiOEDc5DL0sbFUu/SlmRcCg93
jy9YHQJAnPueRI44tz79b5QqVbeaOMUr7ZCb1Kp0uo6G+ANPLdlfliAupwij2eIz
mHRJOWk0jBtXfRft1McH2H51CpXAyw==
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
`) `)