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:
parent
548e3d0342
commit
c281ddf1eb
@ -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\
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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"
|
|
@ -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
|
167
src/pkg/crypto/x509/pkix/pkix.go
Normal file
167
src/pkg/crypto/x509/pkix/pkix.go
Normal 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"
|
||||||
|
}
|
@ -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(¶ms.P) ||
|
if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 {
|
||||||
!rawValueIsInteger(¶ms.Q) ||
|
return nil, os.ErrorString("zero or negative DSA parameter")
|
||||||
!rawValueIsInteger(¶ms.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},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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-----
|
||||||
`)
|
`)
|
||||||
|
Loading…
Reference in New Issue
Block a user