Import forked x509 for parsing of CSRs with empty integers (#2088)
Part of #2080.
This change vendors `crypto/x509`, `crypto/x509/pkix`, and `encoding/asn1` from 1d5f6a765d
. That commit is a direct child of the Go 1.5.4 release tag, so it contains the same code as the current Go version we are using. In that commit I rewrote imports in those packages so they depend on each other internally rather than calling out to the standard library, which would cause type disagreements.
I changed the imports in each place where we're parsing CSRs, and imported under a different name `oldx509`, both to avoid collisions and make it clear what's going on. Places that only use `x509` to parse certificates are not changed, and will use the current standard library.
This will unblock us from moving to Go 1.6, and subsequently Go 1.7.
This commit is contained in:
parent
2a89063fce
commit
474b76ad95
|
@ -135,6 +135,21 @@
|
|||
"Comment": "2.0.0-10-ga109a23",
|
||||
"Rev": "a109a231e10a089285a0aac0fd03c8a083975758"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/letsencrypt/go/src/crypto/x509",
|
||||
"Comment": "go1.5.4-1-g1d5f6a7",
|
||||
"Rev": "1d5f6a765da3eb45fc195fcdb2f2a5e872d6cbb2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/letsencrypt/go/src/crypto/x509/pkix",
|
||||
"Comment": "go1.5.4-1-g1d5f6a7",
|
||||
"Rev": "1d5f6a765da3eb45fc195fcdb2f2a5e872d6cbb2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/letsencrypt/go/src/encoding/asn1",
|
||||
"Comment": "go1.5.4-1-g1d5f6a7",
|
||||
"Rev": "1d5f6a765da3eb45fc195fcdb2f2a5e872d6cbb2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/letsencrypt/pkcs11key",
|
||||
"Rev": "bda7cf218ae2225f745ead23f9b65901dfebbf45"
|
||||
|
|
7
ca/ca.go
7
ca/ca.go
|
@ -7,7 +7,6 @@ import (
|
|||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
@ -33,6 +32,8 @@ import (
|
|||
csrlib "github.com/letsencrypt/boulder/csr"
|
||||
"github.com/letsencrypt/boulder/goodkey"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
"github.com/letsencrypt/go/src/encoding/asn1"
|
||||
)
|
||||
|
||||
// Miscellaneous PKIX OIDs that we need to refer to
|
||||
|
@ -271,7 +272,7 @@ func (ca *CertificateAuthorityImpl) noteSignError(err error) {
|
|||
// Any other value will result in an error.
|
||||
//
|
||||
// Other requested extensions are silently ignored.
|
||||
func (ca *CertificateAuthorityImpl) extensionsFromCSR(csr *x509.CertificateRequest) ([]signer.Extension, error) {
|
||||
func (ca *CertificateAuthorityImpl) extensionsFromCSR(csr *oldx509.CertificateRequest) ([]signer.Extension, error) {
|
||||
extensions := []signer.Extension{}
|
||||
|
||||
extensionSeen := map[string]bool{}
|
||||
|
@ -373,7 +374,7 @@ func (ca *CertificateAuthorityImpl) GenerateOCSP(ctx context.Context, xferObj co
|
|||
// enforcing all policies. Names (domains) in the CertificateRequest will be
|
||||
// lowercased before storage.
|
||||
// Currently it will always sign with the defaultIssuer.
|
||||
func (ca *CertificateAuthorityImpl) IssueCertificate(ctx context.Context, csr x509.CertificateRequest, regID int64) (core.Certificate, error) {
|
||||
func (ca *CertificateAuthorityImpl) IssueCertificate(ctx context.Context, csr oldx509.CertificateRequest, regID int64) (core.Certificate, error) {
|
||||
emptyCert := core.Certificate{}
|
||||
|
||||
if err := csrlib.VerifyCSR(&csr, ca.maxNames, &ca.keyPolicy, ca.PA, ca.forceCNFromSAN, regID); err != nil {
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/mocks"
|
||||
"github.com/letsencrypt/boulder/policy"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -288,7 +289,7 @@ func TestIssueCertificate(t *testing.T) {
|
|||
sa := &mockSA{}
|
||||
ca.SA = sa
|
||||
|
||||
csr, _ := x509.ParseCertificateRequest(CNandSANCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(CNandSANCSR)
|
||||
|
||||
// Sign CSR
|
||||
issuedCert, err := ca.IssueCertificate(ctx, *csr, 1001)
|
||||
|
@ -348,7 +349,7 @@ func TestIssueCertificateMultipleIssuers(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
csr, _ := x509.ParseCertificateRequest(CNandSANCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(CNandSANCSR)
|
||||
issuedCert, err := ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertNotError(t, err, "Failed to sign certificate")
|
||||
|
||||
|
@ -373,7 +374,7 @@ func TestOCSP(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
csr, _ := x509.ParseCertificateRequest(CNandSANCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(CNandSANCSR)
|
||||
cert, err := ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertNotError(t, err, "Failed to issue")
|
||||
parsedCert, err := x509.ParseCertificate(cert.DER)
|
||||
|
@ -469,7 +470,7 @@ func TestNoHostnames(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
csr, _ := x509.ParseCertificateRequest(NoNamesCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(NoNamesCSR)
|
||||
_, err = ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertError(t, err, "Issued certificate with no names")
|
||||
_, ok := err.(core.MalformedRequestError)
|
||||
|
@ -491,7 +492,7 @@ func TestRejectTooManyNames(t *testing.T) {
|
|||
ca.SA = &mockSA{}
|
||||
|
||||
// Test that the CA rejects a CSR with too many names
|
||||
csr, _ := x509.ParseCertificateRequest(TooManyNameCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(TooManyNameCSR)
|
||||
_, err = ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertError(t, err, "Issued certificate with too many names")
|
||||
_, ok := err.(core.MalformedRequestError)
|
||||
|
@ -518,7 +519,7 @@ func TestRejectValidityTooLong(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to parse time")
|
||||
testCtx.fc.Set(future)
|
||||
// Test that the CA rejects CSRs that would expire after the intermediate cert
|
||||
csr, _ := x509.ParseCertificateRequest(NoCNCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(NoCNCSR)
|
||||
_, err = ca.IssueCertificate(ctx, *csr, 1)
|
||||
test.AssertError(t, err, "Cannot issue a certificate that expires after the intermediate certificate")
|
||||
_, ok := err.(core.InternalServerError)
|
||||
|
@ -539,7 +540,7 @@ func TestShortKey(t *testing.T) {
|
|||
ca.SA = &mockSA{}
|
||||
|
||||
// Test that the CA rejects CSRs that would expire after the intermediate cert
|
||||
csr, _ := x509.ParseCertificateRequest(ShortKeyCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(ShortKeyCSR)
|
||||
_, err = ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertError(t, err, "Issued a certificate with too short a key.")
|
||||
_, ok := err.(core.MalformedRequestError)
|
||||
|
@ -560,7 +561,7 @@ func TestAllowNoCN(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(NoCNCSR)
|
||||
csr, err := oldx509.ParseCertificateRequest(NoCNCSR)
|
||||
test.AssertNotError(t, err, "Couldn't parse CSR")
|
||||
issuedCert, err := ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertNotError(t, err, "Failed to sign certificate")
|
||||
|
@ -600,7 +601,7 @@ func TestLongCommonName(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
csr, _ := x509.ParseCertificateRequest(LongCNCSR)
|
||||
csr, _ := oldx509.ParseCertificateRequest(LongCNCSR)
|
||||
_, err = ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertError(t, err, "Issued a certificate with a CN over 64 bytes.")
|
||||
_, ok := err.(core.MalformedRequestError)
|
||||
|
@ -621,8 +622,8 @@ func TestWrongSignature(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
// x509.ParseCertificateRequest() does not check for invalid signatures...
|
||||
csr, _ := x509.ParseCertificateRequest(WrongSignatureCSR)
|
||||
// oldx509.ParseCertificateRequest() does not check for invalid signatures...
|
||||
csr, _ := oldx509.ParseCertificateRequest(WrongSignatureCSR)
|
||||
|
||||
_, err = ca.IssueCertificate(ctx, *csr, 1001)
|
||||
if err == nil {
|
||||
|
@ -653,7 +654,7 @@ func TestProfileSelection(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
csr, err := x509.ParseCertificateRequest(testCase.CSR)
|
||||
csr, err := oldx509.ParseCertificateRequest(testCase.CSR)
|
||||
test.AssertNotError(t, err, "Cannot parse CSR")
|
||||
|
||||
// Sign CSR
|
||||
|
@ -670,6 +671,7 @@ func TestProfileSelection(t *testing.T) {
|
|||
}
|
||||
|
||||
func countMustStaple(t *testing.T, cert *x509.Certificate) (count int) {
|
||||
oidTLSFeature := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
|
||||
for _, ext := range cert.Extensions {
|
||||
if ext.Id.Equal(oidTLSFeature) {
|
||||
test.Assert(t, !ext.Critical, "Extension was marked critical")
|
||||
|
@ -694,19 +696,19 @@ func TestExtensions(t *testing.T) {
|
|||
ca.PA = testCtx.pa
|
||||
ca.SA = &mockSA{}
|
||||
|
||||
mustStapleCSR, err := x509.ParseCertificateRequest(MustStapleCSR)
|
||||
mustStapleCSR, err := oldx509.ParseCertificateRequest(MustStapleCSR)
|
||||
test.AssertNotError(t, err, "Error parsing MustStapleCSR")
|
||||
|
||||
duplicateMustStapleCSR, err := x509.ParseCertificateRequest(DuplicateMustStapleCSR)
|
||||
duplicateMustStapleCSR, err := oldx509.ParseCertificateRequest(DuplicateMustStapleCSR)
|
||||
test.AssertNotError(t, err, "Error parsing DuplicateMustStapleCSR")
|
||||
|
||||
tlsFeatureUnknownCSR, err := x509.ParseCertificateRequest(TLSFeatureUnknownCSR)
|
||||
tlsFeatureUnknownCSR, err := oldx509.ParseCertificateRequest(TLSFeatureUnknownCSR)
|
||||
test.AssertNotError(t, err, "Error parsing TLSFeatureUnknownCSR")
|
||||
|
||||
unsupportedExtensionCSR, err := x509.ParseCertificateRequest(UnsupportedExtensionCSR)
|
||||
unsupportedExtensionCSR, err := oldx509.ParseCertificateRequest(UnsupportedExtensionCSR)
|
||||
test.AssertNotError(t, err, "Error parsing UnsupportedExtensionCSR")
|
||||
|
||||
sign := func(csr *x509.CertificateRequest) *x509.Certificate {
|
||||
sign := func(csr *oldx509.CertificateRequest) *x509.Certificate {
|
||||
coreCert, err := ca.IssueCertificate(ctx, *csr, 1001)
|
||||
test.AssertNotError(t, err, "Failed to issue")
|
||||
cert, err := x509.ParseCertificate(coreCert.DER)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"testing"
|
||||
|
@ -20,13 +19,14 @@ import (
|
|||
"github.com/letsencrypt/boulder/sa/satest"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
"github.com/letsencrypt/boulder/test/vars"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
type mockCA struct{}
|
||||
|
||||
func (ca *mockCA) IssueCertificate(_ context.Context, csr x509.CertificateRequest, regID int64) (core.Certificate, error) {
|
||||
func (ca *mockCA) IssueCertificate(_ context.Context, csr oldx509.CertificateRequest, regID int64) (core.Certificate, error) {
|
||||
return core.Certificate{}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
jose "github.com/square/go-jose"
|
||||
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
// A WebFrontEnd object supplies methods that can be hooked into
|
||||
|
@ -75,7 +77,7 @@ type RegistrationAuthority interface {
|
|||
// CertificateAuthority defines the public interface for the Boulder CA
|
||||
type CertificateAuthority interface {
|
||||
// [RegistrationAuthority]
|
||||
IssueCertificate(ctx context.Context, csr x509.CertificateRequest, regID int64) (Certificate, error)
|
||||
IssueCertificate(ctx context.Context, csr oldx509.CertificateRequest, regID int64) (Certificate, error)
|
||||
GenerateOCSP(ctx context.Context, ocspReq OCSPSigningRequest) ([]byte, error)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package core
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -11,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
"github.com/square/go-jose"
|
||||
)
|
||||
|
||||
|
@ -103,8 +103,8 @@ type AcmeIdentifier struct {
|
|||
// This data is unmarshalled from JSON by way of RawCertificateRequest, which
|
||||
// represents the actual structure received from the client.
|
||||
type CertificateRequest struct {
|
||||
CSR *x509.CertificateRequest // The CSR
|
||||
Bytes []byte // The original bytes of the CSR, for logging.
|
||||
CSR *oldx509.CertificateRequest // The CSR
|
||||
Bytes []byte // The original bytes of the CSR, for logging.
|
||||
}
|
||||
|
||||
type RawCertificateRequest struct {
|
||||
|
@ -118,7 +118,7 @@ func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(raw.CSR)
|
||||
csr, err := oldx509.ParseCertificateRequest(raw.CSR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
20
csr/csr.go
20
csr/csr.go
|
@ -2,13 +2,13 @@ package csr
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/goodkey"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
// maxCNLength is the maximum length allowed for the common name as specified in RFC 5280
|
||||
|
@ -21,19 +21,19 @@ const maxCNLength = 64
|
|||
//
|
||||
// SHA1WithRSA is allowed because there's still a fair bit of it
|
||||
// out there, but we should try to remove it soon.
|
||||
var badSignatureAlgorithms = map[x509.SignatureAlgorithm]bool{
|
||||
x509.UnknownSignatureAlgorithm: true,
|
||||
x509.MD2WithRSA: true,
|
||||
x509.MD5WithRSA: true,
|
||||
x509.DSAWithSHA1: true,
|
||||
x509.DSAWithSHA256: true,
|
||||
x509.ECDSAWithSHA1: true,
|
||||
var badSignatureAlgorithms = map[oldx509.SignatureAlgorithm]bool{
|
||||
oldx509.UnknownSignatureAlgorithm: true,
|
||||
oldx509.MD2WithRSA: true,
|
||||
oldx509.MD5WithRSA: true,
|
||||
oldx509.DSAWithSHA1: true,
|
||||
oldx509.DSAWithSHA256: true,
|
||||
oldx509.ECDSAWithSHA1: true,
|
||||
}
|
||||
|
||||
// VerifyCSR checks the validity of a x509.CertificateRequest. Before doing checks it normalizes
|
||||
// the CSR which lowers the case of DNS names and subject CN, and if forceCNFromSAN is true it
|
||||
// will hoist a DNS name into the CN if it is empty.
|
||||
func VerifyCSR(csr *x509.CertificateRequest, maxNames int, keyPolicy *goodkey.KeyPolicy, pa core.PolicyAuthority, forceCNFromSAN bool, regID int64) error {
|
||||
func VerifyCSR(csr *oldx509.CertificateRequest, maxNames int, keyPolicy *goodkey.KeyPolicy, pa core.PolicyAuthority, forceCNFromSAN bool, regID int64) error {
|
||||
normalizeCSR(csr, forceCNFromSAN)
|
||||
key, ok := csr.PublicKey.(crypto.PublicKey)
|
||||
if !ok {
|
||||
|
@ -76,7 +76,7 @@ func VerifyCSR(csr *x509.CertificateRequest, maxNames int, keyPolicy *goodkey.Ke
|
|||
|
||||
// normalizeCSR deduplicates and lowers the case of dNSNames and the subject CN.
|
||||
// If forceCNFromSAN is true it will also hoist a dNSName into the CN if it is empty.
|
||||
func normalizeCSR(csr *x509.CertificateRequest, forceCNFromSAN bool) {
|
||||
func normalizeCSR(csr *oldx509.CertificateRequest, forceCNFromSAN bool) {
|
||||
if forceCNFromSAN && csr.Subject.CommonName == "" {
|
||||
if len(csr.DNSNames) > 0 {
|
||||
csr.Subject.CommonName = csr.DNSNames[0]
|
||||
|
|
|
@ -3,8 +3,6 @@ package csr
|
|||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -12,6 +10,8 @@ import (
|
|||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/goodkey"
|
||||
"github.com/letsencrypt/boulder/test"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
"github.com/letsencrypt/go/src/crypto/x509/pkix"
|
||||
)
|
||||
|
||||
var testingPolicy = &goodkey.KeyPolicy{
|
||||
|
@ -36,25 +36,25 @@ func (pa *mockPA) WillingToIssue(id core.AcmeIdentifier) error {
|
|||
func TestVerifyCSR(t *testing.T) {
|
||||
private, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
test.AssertNotError(t, err, "error generating test key")
|
||||
signedReqBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{PublicKey: private.PublicKey, SignatureAlgorithm: x509.SHA256WithRSA}, private)
|
||||
signedReqBytes, err := oldx509.CreateCertificateRequest(rand.Reader, &oldx509.CertificateRequest{PublicKey: private.PublicKey, SignatureAlgorithm: oldx509.SHA256WithRSA}, private)
|
||||
test.AssertNotError(t, err, "error generating test CSR")
|
||||
signedReq, err := x509.ParseCertificateRequest(signedReqBytes)
|
||||
signedReq, err := oldx509.ParseCertificateRequest(signedReqBytes)
|
||||
test.AssertNotError(t, err, "error parsing test CSR")
|
||||
brokenSignedReq := new(x509.CertificateRequest)
|
||||
brokenSignedReq := new(oldx509.CertificateRequest)
|
||||
*brokenSignedReq = *signedReq
|
||||
brokenSignedReq.Signature = []byte{1, 1, 1, 1}
|
||||
signedReqWithHosts := new(x509.CertificateRequest)
|
||||
signedReqWithHosts := new(oldx509.CertificateRequest)
|
||||
*signedReqWithHosts = *signedReq
|
||||
signedReqWithHosts.DNSNames = []string{"a.com", "b.com"}
|
||||
signedReqWithLongCN := new(x509.CertificateRequest)
|
||||
signedReqWithLongCN := new(oldx509.CertificateRequest)
|
||||
*signedReqWithLongCN = *signedReq
|
||||
signedReqWithLongCN.Subject.CommonName = strings.Repeat("a", maxCNLength+1)
|
||||
signedReqWithBadName := new(x509.CertificateRequest)
|
||||
signedReqWithBadName := new(oldx509.CertificateRequest)
|
||||
*signedReqWithBadName = *signedReq
|
||||
signedReqWithBadName.DNSNames = []string{"bad-name.com"}
|
||||
|
||||
cases := []struct {
|
||||
csr *x509.CertificateRequest
|
||||
csr *oldx509.CertificateRequest
|
||||
maxNames int
|
||||
keyPolicy *goodkey.KeyPolicy
|
||||
pa core.PolicyAuthority
|
||||
|
@ -62,7 +62,7 @@ func TestVerifyCSR(t *testing.T) {
|
|||
expectedError error
|
||||
}{
|
||||
{
|
||||
&x509.CertificateRequest{},
|
||||
&oldx509.CertificateRequest{},
|
||||
0,
|
||||
testingPolicy,
|
||||
&mockPA{},
|
||||
|
@ -70,7 +70,7 @@ func TestVerifyCSR(t *testing.T) {
|
|||
errors.New("invalid public key in CSR"),
|
||||
},
|
||||
{
|
||||
&x509.CertificateRequest{PublicKey: private.PublicKey},
|
||||
&oldx509.CertificateRequest{PublicKey: private.PublicKey},
|
||||
1,
|
||||
testingPolicy,
|
||||
&mockPA{},
|
||||
|
@ -127,37 +127,37 @@ func TestVerifyCSR(t *testing.T) {
|
|||
|
||||
func TestNormalizeCSR(t *testing.T) {
|
||||
cases := []struct {
|
||||
csr *x509.CertificateRequest
|
||||
csr *oldx509.CertificateRequest
|
||||
forceCN bool
|
||||
expectedCN string
|
||||
expectedNames []string
|
||||
}{
|
||||
{
|
||||
&x509.CertificateRequest{DNSNames: []string{"a.com"}},
|
||||
&oldx509.CertificateRequest{DNSNames: []string{"a.com"}},
|
||||
true,
|
||||
"a.com",
|
||||
[]string{"a.com"},
|
||||
},
|
||||
{
|
||||
&x509.CertificateRequest{Subject: pkix.Name{CommonName: "A.com"}, DNSNames: []string{"a.com"}},
|
||||
&oldx509.CertificateRequest{Subject: pkix.Name{CommonName: "A.com"}, DNSNames: []string{"a.com"}},
|
||||
true,
|
||||
"a.com",
|
||||
[]string{"a.com"},
|
||||
},
|
||||
{
|
||||
&x509.CertificateRequest{DNSNames: []string{"a.com"}},
|
||||
&oldx509.CertificateRequest{DNSNames: []string{"a.com"}},
|
||||
false,
|
||||
"",
|
||||
[]string{"a.com"},
|
||||
},
|
||||
{
|
||||
&x509.CertificateRequest{DNSNames: []string{"a.com", "a.com"}},
|
||||
&oldx509.CertificateRequest{DNSNames: []string{"a.com", "a.com"}},
|
||||
false,
|
||||
"",
|
||||
[]string{"a.com"},
|
||||
},
|
||||
{
|
||||
&x509.CertificateRequest{Subject: pkix.Name{CommonName: "A.com"}, DNSNames: []string{"B.com"}},
|
||||
&oldx509.CertificateRequest{Subject: pkix.Name{CommonName: "A.com"}, DNSNames: []string{"B.com"}},
|
||||
false,
|
||||
"a.com",
|
||||
[]string{"a.com", "b.com"},
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
// MockCA is a mock of a CA that always returns the cert from PEM in response to
|
||||
|
@ -17,7 +18,7 @@ type MockCA struct {
|
|||
}
|
||||
|
||||
// IssueCertificate is a mock
|
||||
func (ca *MockCA) IssueCertificate(ctx context.Context, csr x509.CertificateRequest, regID int64) (core.Certificate, error) {
|
||||
func (ca *MockCA) IssueCertificate(ctx context.Context, csr oldx509.CertificateRequest, regID int64) (core.Certificate, error) {
|
||||
if ca.PEM == nil {
|
||||
return core.Certificate{}, fmt.Errorf("MockCA's PEM field must be set before calling IssueCertificate")
|
||||
}
|
||||
|
|
3
ra/ra.go
3
ra/ra.go
|
@ -28,6 +28,7 @@ import (
|
|||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/ratelimit"
|
||||
vaPB "github.com/letsencrypt/boulder/va/proto"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
// Note: the issuanceExpvar must be a global. If it is a member of the RA, or
|
||||
|
@ -440,7 +441,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(ctx context.Context, reque
|
|||
// * IsCA is false
|
||||
// * ExtKeyUsage only contains ExtKeyUsageServerAuth & ExtKeyUsageClientAuth
|
||||
// * Subject only contains CommonName & Names
|
||||
func (ra *RegistrationAuthorityImpl) MatchesCSR(cert core.Certificate, csr *x509.CertificateRequest) (err error) {
|
||||
func (ra *RegistrationAuthorityImpl) MatchesCSR(cert core.Certificate, csr *oldx509.CertificateRequest) (err error) {
|
||||
parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER))
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/test"
|
||||
"github.com/letsencrypt/boulder/test/vars"
|
||||
vaPB "github.com/letsencrypt/boulder/va/proto"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
type DummyValidationAuthority struct {
|
||||
|
@ -115,7 +116,7 @@ var (
|
|||
|
||||
ResponseIndex = 0
|
||||
|
||||
ExampleCSR = &x509.CertificateRequest{}
|
||||
ExampleCSR = &oldx509.CertificateRequest{}
|
||||
|
||||
// These values are populated by the tests as we go
|
||||
url0, _ = url.Parse("http://acme.invalid/authz/60p2Dc_XmUB2UUJBV4wYkF7BJbPD9KlDnUL3SmFMuTE?challenge=0")
|
||||
|
@ -231,7 +232,7 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
|
|||
}
|
||||
|
||||
block, _ := pem.Decode(CSRPEM)
|
||||
ExampleCSR, _ = x509.ParseCertificateRequest(block.Bytes)
|
||||
ExampleCSR, _ = oldx509.ParseCertificateRequest(block.Bytes)
|
||||
|
||||
Registration, _ = ssa.NewRegistration(ctx, core.Registration{
|
||||
Key: AccountKeyA,
|
||||
|
@ -759,7 +760,7 @@ func TestCertificateKeyNotEqualAccountKey(t *testing.T) {
|
|||
}
|
||||
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csr, AccountPrivateKey.Key)
|
||||
test.AssertNotError(t, err, "Failed to sign CSR")
|
||||
parsedCSR, err := x509.ParseCertificateRequest(csrBytes)
|
||||
parsedCSR, err := oldx509.ParseCertificateRequest(csrBytes)
|
||||
test.AssertNotError(t, err, "Failed to parse CSR")
|
||||
err = sa.FinalizeAuthorization(ctx, authz)
|
||||
test.AssertNotError(t, err, "Could not store test data")
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
vaPB "github.com/letsencrypt/boulder/va/proto"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
)
|
||||
|
||||
// This file defines RPC wrappers around the ${ROLE}Impl classes,
|
||||
|
@ -649,7 +650,7 @@ func NewCertificateAuthorityServer(rpc Server, impl core.CertificateAuthority) (
|
|||
return
|
||||
}
|
||||
|
||||
csr, err := x509.ParseCertificateRequest(icReq.Bytes)
|
||||
csr, err := oldx509.ParseCertificateRequest(icReq.Bytes)
|
||||
if err != nil {
|
||||
// AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64
|
||||
improperMessage(MethodIssueCertificate, err, req)
|
||||
|
@ -703,7 +704,7 @@ func NewCertificateAuthorityClient(clientName string, amqpConf *cmd.AMQPConfig,
|
|||
}
|
||||
|
||||
// IssueCertificate sends a request to issue a certificate
|
||||
func (cac CertificateAuthorityClient) IssueCertificate(ctx context.Context, csr x509.CertificateRequest, regID int64) (cert core.Certificate, err error) {
|
||||
func (cac CertificateAuthorityClient) IssueCertificate(ctx context.Context, csr oldx509.CertificateRequest, regID int64) (cert core.Certificate, err error) {
|
||||
var icReq issueCertificateRequest
|
||||
icReq.Bytes = csr.Raw
|
||||
icReq.RegID = regID
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,22 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
|
@ -0,0 +1,116 @@
|
|||
// 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 x509
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
)
|
||||
|
||||
// CertPool is a set of certificates.
|
||||
type CertPool struct {
|
||||
bySubjectKeyId map[string][]int
|
||||
byName map[string][]int
|
||||
certs []*Certificate
|
||||
}
|
||||
|
||||
// NewCertPool returns a new, empty CertPool.
|
||||
func NewCertPool() *CertPool {
|
||||
return &CertPool{
|
||||
make(map[string][]int),
|
||||
make(map[string][]int),
|
||||
nil,
|
||||
}
|
||||
}
|
||||
|
||||
// findVerifiedParents attempts to find certificates in s which have signed the
|
||||
// given certificate. If any candidates were rejected then errCert will be set
|
||||
// to one of them, arbitrarily, and err will contain the reason that it was
|
||||
// rejected.
|
||||
func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
var candidates []int
|
||||
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
candidates = s.byName[string(cert.RawIssuer)]
|
||||
}
|
||||
|
||||
for _, c := range candidates {
|
||||
if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
|
||||
parents = append(parents, c)
|
||||
} else {
|
||||
errCert = s.certs[c]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to a pool.
|
||||
func (s *CertPool) AddCert(cert *Certificate) {
|
||||
if cert == nil {
|
||||
panic("adding nil Certificate to CertPool")
|
||||
}
|
||||
|
||||
// Check that the certificate isn't being added twice.
|
||||
for _, c := range s.certs {
|
||||
if c.Equal(cert) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
n := len(s.certs)
|
||||
s.certs = append(s.certs, cert)
|
||||
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
keyId := string(cert.SubjectKeyId)
|
||||
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
|
||||
}
|
||||
name := string(cert.RawSubject)
|
||||
s.byName[name] = append(s.byName[name], n)
|
||||
}
|
||||
|
||||
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
|
||||
// It appends any certificates found to s and reports whether any certificates
|
||||
// were successfully parsed.
|
||||
//
|
||||
// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
|
||||
// of root CAs in a format suitable for this function.
|
||||
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
|
||||
for len(pemCerts) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemCerts = pem.Decode(pemCerts)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s.AddCert(cert)
|
||||
ok = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Subjects returns a list of the DER-encoded subjects of
|
||||
// all of the certificates in the pool.
|
||||
func (s *CertPool) Subjects() (res [][]byte) {
|
||||
res = make([][]byte, len(s.certs))
|
||||
for i, c := range s.certs {
|
||||
res[i] = c.RawSubject
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2012 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 x509
|
||||
|
||||
// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
|
||||
// generate a key from the password was derived by looking at the OpenSSL
|
||||
// implementation.
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PEMCipher int
|
||||
|
||||
// Possible values for the EncryptPEMBlock encryption algorithm.
|
||||
const (
|
||||
_ PEMCipher = iota
|
||||
PEMCipherDES
|
||||
PEMCipher3DES
|
||||
PEMCipherAES128
|
||||
PEMCipherAES192
|
||||
PEMCipherAES256
|
||||
)
|
||||
|
||||
// rfc1423Algo holds a method for enciphering a PEM block.
|
||||
type rfc1423Algo struct {
|
||||
cipher PEMCipher
|
||||
name string
|
||||
cipherFunc func(key []byte) (cipher.Block, error)
|
||||
keySize int
|
||||
blockSize int
|
||||
}
|
||||
|
||||
// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
|
||||
// block. The ivSize numbers were taken from the OpenSSL source.
|
||||
var rfc1423Algos = []rfc1423Algo{{
|
||||
cipher: PEMCipherDES,
|
||||
name: "DES-CBC",
|
||||
cipherFunc: des.NewCipher,
|
||||
keySize: 8,
|
||||
blockSize: des.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipher3DES,
|
||||
name: "DES-EDE3-CBC",
|
||||
cipherFunc: des.NewTripleDESCipher,
|
||||
keySize: 24,
|
||||
blockSize: des.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES128,
|
||||
name: "AES-128-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 16,
|
||||
blockSize: aes.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES192,
|
||||
name: "AES-192-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 24,
|
||||
blockSize: aes.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES256,
|
||||
name: "AES-256-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 32,
|
||||
blockSize: aes.BlockSize,
|
||||
},
|
||||
}
|
||||
|
||||
// deriveKey uses a key derivation function to stretch the password into a key
|
||||
// with the number of bits our cipher requires. This algorithm was derived from
|
||||
// the OpenSSL source.
|
||||
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
|
||||
hash := md5.New()
|
||||
out := make([]byte, c.keySize)
|
||||
var digest []byte
|
||||
|
||||
for i := 0; i < len(out); i += len(digest) {
|
||||
hash.Reset()
|
||||
hash.Write(digest)
|
||||
hash.Write(password)
|
||||
hash.Write(salt)
|
||||
digest = hash.Sum(digest[:0])
|
||||
copy(out[i:], digest)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
|
||||
func IsEncryptedPEMBlock(b *pem.Block) bool {
|
||||
_, ok := b.Headers["DEK-Info"]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IncorrectPasswordError is returned when an incorrect password is detected.
|
||||
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
|
||||
|
||||
// DecryptPEMBlock takes a password encrypted PEM block and the password used to
|
||||
// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
|
||||
// the DEK-Info header to determine the algorithm used for decryption. If no
|
||||
// DEK-Info header is present, an error is returned. If an incorrect password
|
||||
// is detected an IncorrectPasswordError is returned. Because of deficiencies
|
||||
// in the encrypted-PEM format, it's not always possible to detect an incorrect
|
||||
// password. In these cases no error will be returned but the decrypted DER
|
||||
// bytes will be random noise.
|
||||
func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
|
||||
dek, ok := b.Headers["DEK-Info"]
|
||||
if !ok {
|
||||
return nil, errors.New("x509: no DEK-Info header in block")
|
||||
}
|
||||
|
||||
idx := strings.Index(dek, ",")
|
||||
if idx == -1 {
|
||||
return nil, errors.New("x509: malformed DEK-Info header")
|
||||
}
|
||||
|
||||
mode, hexIV := dek[:idx], dek[idx+1:]
|
||||
ciph := cipherByName(mode)
|
||||
if ciph == nil {
|
||||
return nil, errors.New("x509: unknown encryption mode")
|
||||
}
|
||||
iv, err := hex.DecodeString(hexIV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(iv) != ciph.blockSize {
|
||||
return nil, errors.New("x509: incorrect IV size")
|
||||
}
|
||||
|
||||
// Based on the OpenSSL implementation. The salt is the first 8 bytes
|
||||
// of the initialization vector.
|
||||
key := ciph.deriveKey(password, iv[:8])
|
||||
block, err := ciph.cipherFunc(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b.Bytes)%block.BlockSize() != 0 {
|
||||
return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
|
||||
}
|
||||
|
||||
data := make([]byte, len(b.Bytes))
|
||||
dec := cipher.NewCBCDecrypter(block, iv)
|
||||
dec.CryptBlocks(data, b.Bytes)
|
||||
|
||||
// Blocks are padded using a scheme where the last n bytes of padding are all
|
||||
// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
|
||||
// For example:
|
||||
// [x y z 2 2]
|
||||
// [x y 7 7 7 7 7 7 7]
|
||||
// If we detect a bad padding, we assume it is an invalid password.
|
||||
dlen := len(data)
|
||||
if dlen == 0 || dlen%ciph.blockSize != 0 {
|
||||
return nil, errors.New("x509: invalid padding")
|
||||
}
|
||||
last := int(data[dlen-1])
|
||||
if dlen < last {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
if last == 0 || last > ciph.blockSize {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
for _, val := range data[dlen-last:] {
|
||||
if int(val) != last {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
}
|
||||
return data[:dlen-last], nil
|
||||
}
|
||||
|
||||
// EncryptPEMBlock returns a PEM block of the specified type holding the
|
||||
// given DER-encoded data encrypted with the specified algorithm and
|
||||
// password.
|
||||
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
|
||||
ciph := cipherByKey(alg)
|
||||
if ciph == nil {
|
||||
return nil, errors.New("x509: unknown encryption mode")
|
||||
}
|
||||
iv := make([]byte, ciph.blockSize)
|
||||
if _, err := io.ReadFull(rand, iv); err != nil {
|
||||
return nil, errors.New("x509: cannot generate IV: " + err.Error())
|
||||
}
|
||||
// The salt is the first 8 bytes of the initialization vector,
|
||||
// matching the key derivation in DecryptPEMBlock.
|
||||
key := ciph.deriveKey(password, iv[:8])
|
||||
block, err := ciph.cipherFunc(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enc := cipher.NewCBCEncrypter(block, iv)
|
||||
pad := ciph.blockSize - len(data)%ciph.blockSize
|
||||
encrypted := make([]byte, len(data), len(data)+pad)
|
||||
// We could save this copy by encrypting all the whole blocks in
|
||||
// the data separately, but it doesn't seem worth the additional
|
||||
// code.
|
||||
copy(encrypted, data)
|
||||
// See RFC 1423, section 1.1
|
||||
for i := 0; i < pad; i++ {
|
||||
encrypted = append(encrypted, byte(pad))
|
||||
}
|
||||
enc.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
return &pem.Block{
|
||||
Type: blockType,
|
||||
Headers: map[string]string{
|
||||
"Proc-Type": "4,ENCRYPTED",
|
||||
"DEK-Info": ciph.name + "," + hex.EncodeToString(iv),
|
||||
},
|
||||
Bytes: encrypted,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cipherByName(name string) *rfc1423Algo {
|
||||
for i := range rfc1423Algos {
|
||||
alg := &rfc1423Algos[i]
|
||||
if alg.name == name {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherByKey(key PEMCipher) *rfc1423Algo {
|
||||
for i := range rfc1423Algos {
|
||||
alg := &rfc1423Algos[i]
|
||||
if alg.cipher == key {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
// 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 x509
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"github.com/letsencrypt/go/src/encoding/asn1"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
|
||||
type pkcs1PrivateKey struct {
|
||||
Version int
|
||||
N *big.Int
|
||||
E int
|
||||
D *big.Int
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
// We ignore these values, if present, because rsa will calculate them.
|
||||
Dp *big.Int `asn1:"optional"`
|
||||
Dq *big.Int `asn1:"optional"`
|
||||
Qinv *big.Int `asn1:"optional"`
|
||||
|
||||
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
|
||||
}
|
||||
|
||||
type pkcs1AdditionalRSAPrime struct {
|
||||
Prime *big.Int
|
||||
|
||||
// We ignore these values because rsa will calculate them.
|
||||
Exp *big.Int
|
||||
Coeff *big.Int
|
||||
}
|
||||
|
||||
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
|
||||
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
|
||||
var priv pkcs1PrivateKey
|
||||
rest, err := asn1.Unmarshal(der, &priv)
|
||||
if len(rest) > 0 {
|
||||
err = asn1.SyntaxError{Msg: "trailing data"}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if priv.Version > 1 {
|
||||
return nil, errors.New("x509: unsupported private key version")
|
||||
}
|
||||
|
||||
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
|
||||
return nil, errors.New("x509: private key contains zero or negative value")
|
||||
}
|
||||
|
||||
key = new(rsa.PrivateKey)
|
||||
key.PublicKey = rsa.PublicKey{
|
||||
E: priv.E,
|
||||
N: priv.N,
|
||||
}
|
||||
|
||||
key.D = priv.D
|
||||
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
|
||||
key.Primes[0] = priv.P
|
||||
key.Primes[1] = priv.Q
|
||||
for i, a := range priv.AdditionalPrimes {
|
||||
if a.Prime.Sign() <= 0 {
|
||||
return nil, errors.New("x509: private key contains zero or negative prime")
|
||||
}
|
||||
key.Primes[i+2] = a.Prime
|
||||
// We ignore the other two values because rsa will calculate
|
||||
// them as needed.
|
||||
}
|
||||
|
||||
err = key.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key.Precompute()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
|
||||
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
|
||||
key.Precompute()
|
||||
|
||||
version := 0
|
||||
if len(key.Primes) > 2 {
|
||||
version = 1
|
||||
}
|
||||
|
||||
priv := pkcs1PrivateKey{
|
||||
Version: version,
|
||||
N: key.N,
|
||||
E: key.PublicKey.E,
|
||||
D: key.D,
|
||||
P: key.Primes[0],
|
||||
Q: key.Primes[1],
|
||||
Dp: key.Precomputed.Dp,
|
||||
Dq: key.Precomputed.Dq,
|
||||
Qinv: key.Precomputed.Qinv,
|
||||
}
|
||||
|
||||
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
|
||||
for i, values := range key.Precomputed.CRTValues {
|
||||
priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
|
||||
priv.AdditionalPrimes[i].Exp = values.Exp
|
||||
priv.AdditionalPrimes[i].Coeff = values.Coeff
|
||||
}
|
||||
|
||||
b, _ := asn1.Marshal(priv)
|
||||
return b
|
||||
}
|
||||
|
||||
// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key.
|
||||
type rsaPublicKey struct {
|
||||
N *big.Int
|
||||
E int
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// 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 x509
|
||||
|
||||
import (
|
||||
"github.com/letsencrypt/go/src/crypto/x509/pkix"
|
||||
"github.com/letsencrypt/go/src/encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
|
||||
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
|
||||
// and RFC5208.
|
||||
type pkcs8 struct {
|
||||
Version int
|
||||
Algo pkix.AlgorithmIdentifier
|
||||
PrivateKey []byte
|
||||
// optional attributes omitted.
|
||||
}
|
||||
|
||||
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
|
||||
// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
|
||||
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
|
||||
var privKey pkcs8
|
||||
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
|
||||
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
|
||||
}
|
||||
return key, nil
|
||||
|
||||
case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
|
||||
bytes := privKey.Algo.Parameters.FullBytes
|
||||
namedCurveOID := new(asn1.ObjectIdentifier)
|
||||
if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
|
||||
namedCurveOID = nil
|
||||
}
|
||||
key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
|
||||
}
|
||||
return key, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
// 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 (
|
||||
"github.com/letsencrypt/go/src/encoding/asn1"
|
||||
"math/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 `asn1:"optional"`
|
||||
}
|
||||
|
||||
type RDNSequence []RelativeDistinguishedNameSET
|
||||
|
||||
type RelativeDistinguishedNameSET []AttributeTypeAndValue
|
||||
|
||||
// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in
|
||||
// http://tools.ietf.org/html/rfc5280#section-4.1.2.4
|
||||
type AttributeTypeAndValue struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// AttributeTypeAndValueSET represents a set of ASN.1 sequences of
|
||||
// AttributeTypeAndValue sequences from RFC 2986 (PKCS #10).
|
||||
type AttributeTypeAndValueSET struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value [][]AttributeTypeAndValue `asn1:"set"`
|
||||
}
|
||||
|
||||
// Extension represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.2.
|
||||
type Extension struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
Critical bool `asn1:"optional"`
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// Name represents an X.509 distinguished name. This only includes the common
|
||||
// elements of a DN. When parsing, all elements are stored in Names and
|
||||
// non-standard elements can be extracted from there. When marshaling, elements
|
||||
// in ExtraNames are appended and override other values with the same OID.
|
||||
type Name struct {
|
||||
Country, Organization, OrganizationalUnit []string
|
||||
Locality, Province []string
|
||||
StreetAddress, PostalCode []string
|
||||
SerialNumber, CommonName string
|
||||
|
||||
Names []AttributeTypeAndValue
|
||||
ExtraNames []AttributeTypeAndValue
|
||||
}
|
||||
|
||||
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
|
||||
for _, rdn := range *rdns {
|
||||
if len(rdn) == 0 {
|
||||
continue
|
||||
}
|
||||
atv := rdn[0]
|
||||
n.Names = append(n.Names, atv)
|
||||
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 (n Name) appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence {
|
||||
if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) {
|
||||
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 = n.appendRDNs(ret, n.Country, oidCountry)
|
||||
ret = n.appendRDNs(ret, n.Organization, oidOrganization)
|
||||
ret = n.appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
|
||||
ret = n.appendRDNs(ret, n.Locality, oidLocality)
|
||||
ret = n.appendRDNs(ret, n.Province, oidProvince)
|
||||
ret = n.appendRDNs(ret, n.StreetAddress, oidStreetAddress)
|
||||
ret = n.appendRDNs(ret, n.PostalCode, oidPostalCode)
|
||||
if len(n.CommonName) > 0 {
|
||||
ret = n.appendRDNs(ret, []string{n.CommonName}, oidCommonName)
|
||||
}
|
||||
if len(n.SerialNumber) > 0 {
|
||||
ret = n.appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
|
||||
}
|
||||
for _, atv := range n.ExtraNames {
|
||||
ret = append(ret, []AttributeTypeAndValue{atv})
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// oidInAttributeTypeAndValue returns whether a type with the given OID exists
|
||||
// in atv.
|
||||
func oidInAttributeTypeAndValue(oid asn1.ObjectIdentifier, atv []AttributeTypeAndValue) bool {
|
||||
for _, a := range atv {
|
||||
if a.Type.Equal(oid) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 reports whether now is past the expiry time of certList.
|
||||
func (certList *CertificateList) HasExpired(now time.Time) bool {
|
||||
return now.After(certList.TBSCertList.NextUpdate)
|
||||
}
|
||||
|
||||
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 5.1.
|
||||
type TBSCertificateList struct {
|
||||
Raw asn1.RawContent
|
||||
Version int `asn1:"optional,default:1"`
|
||||
Signature AlgorithmIdentifier
|
||||
Issuer RDNSequence
|
||||
ThisUpdate time.Time
|
||||
NextUpdate time.Time `asn1:"optional"`
|
||||
RevokedCertificates []RevokedCertificate `asn1:"optional"`
|
||||
Extensions []Extension `asn1:"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 `asn1:"optional"`
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2012 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 x509
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
systemRoots *CertPool
|
||||
)
|
||||
|
||||
func systemRootsPool() *CertPool {
|
||||
once.Do(initSystemRoots)
|
||||
return systemRoots
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build dragonfly freebsd netbsd openbsd
|
||||
|
||||
package x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
|
||||
"/etc/ssl/cert.pem", // OpenBSD
|
||||
"/etc/openssl/certs/ca-certificates.crt", // NetBSD
|
||||
}
|
79
vendor/github.com/letsencrypt/go/src/crypto/x509/root_cgo_darwin.go
generated
vendored
Normal file
79
vendor/github.com/letsencrypt/go/src/crypto/x509/root_cgo_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
// 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.
|
||||
|
||||
// +build cgo,!arm,!arm64,!ios
|
||||
|
||||
package x509
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
|
||||
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
// FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
|
||||
//
|
||||
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
|
||||
// certificates of the system. On failure, the function returns -1.
|
||||
//
|
||||
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
|
||||
// we've consumed its content.
|
||||
int FetchPEMRoots(CFDataRef *pemRoots) {
|
||||
if (pemRoots == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CFArrayRef certs = NULL;
|
||||
OSStatus err = SecTrustCopyAnchorCertificates(&certs);
|
||||
if (err != noErr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
int i, ncerts = CFArrayGetCount(certs);
|
||||
for (i = 0; i < ncerts; i++) {
|
||||
CFDataRef data = NULL;
|
||||
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
|
||||
if (cert == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
|
||||
// Once we support weak imports via cgo we should prefer that, and fall back to this
|
||||
// for older systems.
|
||||
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
CFRelease(data);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(certs);
|
||||
|
||||
*pemRoots = combinedData;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
func initSystemRoots() {
|
||||
roots := NewCertPool()
|
||||
|
||||
var data C.CFDataRef = nil
|
||||
err := C.FetchPEMRoots(&data)
|
||||
if err == -1 {
|
||||
return
|
||||
}
|
||||
|
||||
defer C.CFRelease(C.CFTypeRef(data))
|
||||
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
|
||||
roots.AppendCertsFromPEM(buf)
|
||||
systemRoots = roots
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go
|
||||
|
||||
package x509
|
||||
|
||||
import "os/exec"
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func execSecurityRoots() (*CertPool, error) {
|
||||
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roots := NewCertPool()
|
||||
roots.AppendCertsFromPEM(data)
|
||||
return roots, nil
|
||||
}
|
191
vendor/github.com/letsencrypt/go/src/crypto/x509/root_darwin_arm_gen.go
generated
vendored
Normal file
191
vendor/github.com/letsencrypt/go/src/crypto/x509/root_darwin_arm_gen.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Generates root_darwin_armx.go.
|
||||
//
|
||||
// As of iOS 8, there is no API for querying the system trusted X.509 root
|
||||
// certificates. We could use SecTrustEvaluate to verify that a trust chain
|
||||
// exists for a certificate, but the x509 API requires returning the entire
|
||||
// chain.
|
||||
//
|
||||
// Apple publishes the list of trusted root certificates for iOS on
|
||||
// support.apple.com. So we parse the list and extract the certificates from
|
||||
// an OS X machine and embed them into the x509 package.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var output = flag.String("output", "root_darwin_armx.go", "file name to write")
|
||||
|
||||
func main() {
|
||||
certs, err := selectCerts()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
fmt.Fprintf(buf, "// Created by root_darwin_arm_gen --output %s; DO NOT EDIT\n", *output)
|
||||
fmt.Fprintf(buf, "%s", header)
|
||||
|
||||
fmt.Fprintf(buf, "const systemRootsPEM = `\n")
|
||||
for _, cert := range certs {
|
||||
b := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
if err := pem.Encode(buf, b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(buf, "`")
|
||||
|
||||
source, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal("source format error:", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(*output, source, 0644); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func selectCerts() ([]*x509.Certificate, error) {
|
||||
ids, err := fetchCertIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scerts, err := sysCerts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var certs []*x509.Certificate
|
||||
for _, id := range ids {
|
||||
sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber)
|
||||
}
|
||||
ski, ok := big.NewInt(0).SetString(id.subjectKeyID, 0)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid Subject Key ID: %q", id.subjectKeyID)
|
||||
}
|
||||
|
||||
for _, cert := range scerts {
|
||||
if sn.Cmp(cert.SerialNumber) != 0 {
|
||||
continue
|
||||
}
|
||||
cski := big.NewInt(0).SetBytes(cert.SubjectKeyId)
|
||||
if ski.Cmp(cski) != 0 {
|
||||
continue
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
break
|
||||
}
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
func sysCerts() (certs []*x509.Certificate, err error) {
|
||||
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for len(data) > 0 {
|
||||
var block *pem.Block
|
||||
block, data = pem.Decode(data)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
type certID struct {
|
||||
serialNumber string
|
||||
subjectKeyID string
|
||||
}
|
||||
|
||||
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
|
||||
func fetchCertIDs() ([]certID, error) {
|
||||
resp, err := http.Get("https://support.apple.com/en-us/HT204132")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
text := string(body)
|
||||
text = text[strings.Index(text, "<section id=trusted"):]
|
||||
text = text[:strings.Index(text, "</section>")]
|
||||
|
||||
lines := strings.Split(text, "\n")
|
||||
var ids []certID
|
||||
var id certID
|
||||
for i, ln := range lines {
|
||||
if i == len(lines)-1 {
|
||||
break
|
||||
}
|
||||
const sn = "Serial Number:"
|
||||
if ln == sn {
|
||||
id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(ln, sn) {
|
||||
// extract hex value from parentheses.
|
||||
id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1]
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" {
|
||||
id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
|
||||
ids = append(ids, id)
|
||||
id = certID{}
|
||||
}
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
const header = `
|
||||
// Copyright 2015 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.
|
||||
|
||||
// +build cgo
|
||||
// +build darwin
|
||||
// +build arm arm64
|
||||
|
||||
package x509
|
||||
|
||||
func initSystemRoots() {
|
||||
systemRoots = NewCertPool()
|
||||
systemRoots.AppendCertsFromPEM([]byte(systemRootsPEM))
|
||||
}
|
||||
`
|
4907
vendor/github.com/letsencrypt/go/src/crypto/x509/root_darwin_armx.go
generated
vendored
Normal file
4907
vendor/github.com/letsencrypt/go/src/crypto/x509/root_darwin_armx.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2015 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 x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
|
||||
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright 2015 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 x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{}
|
11
vendor/github.com/letsencrypt/go/src/crypto/x509/root_nocgo_darwin.go
generated
vendored
Normal file
11
vendor/github.com/letsencrypt/go/src/crypto/x509/root_nocgo_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
// +build !cgo
|
||||
|
||||
package x509
|
||||
|
||||
func initSystemRoots() {
|
||||
systemRoots, _ = execSecurityRoots()
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// +build plan9
|
||||
|
||||
package x509
|
||||
|
||||
import "io/ioutil"
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/sys/lib/tls/ca.pem",
|
||||
}
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func initSystemRoots() {
|
||||
roots := NewCertPool()
|
||||
for _, file := range certFiles {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err == nil {
|
||||
roots.AppendCertsFromPEM(data)
|
||||
systemRoots = roots
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// All of the files failed to load. systemRoots will be nil which will
|
||||
// trigger a specific error at verification time.
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2015 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 x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/etc/certs/ca-certificates.crt", // Solaris 11.2+
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Joyent SmartOS
|
||||
"/etc/ssl/cacert.pem", // OmniOS
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// 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.
|
||||
|
||||
// +build dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package x509
|
||||
|
||||
import "io/ioutil"
|
||||
|
||||
// Possible directories with certificate files; stop after successfully
|
||||
// reading at least one file from a directory.
|
||||
var certDirectories = []string{
|
||||
"/system/etc/security/cacerts", // Android
|
||||
}
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func initSystemRoots() {
|
||||
roots := NewCertPool()
|
||||
for _, file := range certFiles {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err == nil {
|
||||
roots.AppendCertsFromPEM(data)
|
||||
systemRoots = roots
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, directory := range certDirectories {
|
||||
fis, err := ioutil.ReadDir(directory)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
rootsAdded := false
|
||||
for _, fi := range fis {
|
||||
data, err := ioutil.ReadFile(directory + "/" + fi.Name())
|
||||
if err == nil && roots.AppendCertsFromPEM(data) {
|
||||
rootsAdded = true
|
||||
}
|
||||
}
|
||||
if rootsAdded {
|
||||
systemRoots = roots
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// All of the files failed to load. systemRoots will be nil which will
|
||||
// trigger a specific error at verification time.
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright 2012 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 x509
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
|
||||
// certificate store containing itself and all of the intermediate certificates specified
|
||||
// in the opts.Intermediates CertPool.
|
||||
//
|
||||
// A pointer to the in-memory store is available in the returned CertContext's Store field.
|
||||
// The store is automatically freed when the CertContext is freed using
|
||||
// syscall.CertFreeCertificateContext.
|
||||
func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
|
||||
var storeCtx *syscall.CertContext
|
||||
|
||||
leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertFreeCertificateContext(leafCtx)
|
||||
|
||||
handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertCloseStore(handle, 0)
|
||||
|
||||
err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Intermediates != nil {
|
||||
for _, intermediate := range opts.Intermediates.certs {
|
||||
ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
|
||||
syscall.CertFreeCertificateContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return storeCtx, nil
|
||||
}
|
||||
|
||||
// extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
|
||||
func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
|
||||
if simpleChain == nil || count == 0 {
|
||||
return nil, errors.New("x509: invalid simple chain")
|
||||
}
|
||||
|
||||
simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
|
||||
lastChain := simpleChains[count-1]
|
||||
elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
|
||||
for i := 0; i < int(lastChain.NumElements); i++ {
|
||||
// Copy the buf, since ParseCertificate does not create its own copy.
|
||||
cert := elements[i].CertContext
|
||||
encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
|
||||
buf := make([]byte, cert.Length)
|
||||
copy(buf, encodedCert[:])
|
||||
parsedCert, err := ParseCertificate(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chain = append(chain, parsedCert)
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
// checkChainTrustStatus checks the trust status of the certificate chain, translating
|
||||
// any errors it finds into Go errors in the process.
|
||||
func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
|
||||
if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
|
||||
status := chainCtx.TrustStatus.ErrorStatus
|
||||
switch status {
|
||||
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
|
||||
return CertificateInvalidError{c, Expired}
|
||||
default:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
|
||||
// use as a certificate chain for a SSL/TLS server.
|
||||
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
|
||||
servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sslPara := &syscall.SSLExtraCertChainPolicyPara{
|
||||
AuthType: syscall.AUTHTYPE_SERVER,
|
||||
ServerName: servernamep,
|
||||
}
|
||||
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
|
||||
|
||||
para := &syscall.CertChainPolicyPara{
|
||||
ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
|
||||
}
|
||||
para.Size = uint32(unsafe.Sizeof(*para))
|
||||
|
||||
status := syscall.CertChainPolicyStatus{}
|
||||
err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(mkrautz): use the lChainIndex and lElementIndex fields
|
||||
// of the CertChainPolicyStatus to provide proper context, instead
|
||||
// using c.
|
||||
if status.Error != 0 {
|
||||
switch status.Error {
|
||||
case syscall.CERT_E_EXPIRED:
|
||||
return CertificateInvalidError{c, Expired}
|
||||
case syscall.CERT_E_CN_NO_MATCH:
|
||||
return HostnameError{c, opts.DNSName}
|
||||
case syscall.CERT_E_UNTRUSTEDROOT:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
default:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// systemVerify is like Verify, except that it uses CryptoAPI calls
|
||||
// to build certificate chains and verify them.
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
hasDNSName := opts != nil && len(opts.DNSName) > 0
|
||||
|
||||
storeCtx, err := createStoreContext(c, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertFreeCertificateContext(storeCtx)
|
||||
|
||||
para := new(syscall.CertChainPara)
|
||||
para.Size = uint32(unsafe.Sizeof(*para))
|
||||
|
||||
// If there's a DNSName set in opts, assume we're verifying
|
||||
// a certificate from a TLS server.
|
||||
if hasDNSName {
|
||||
oids := []*byte{
|
||||
&syscall.OID_PKIX_KP_SERVER_AUTH[0],
|
||||
// Both IE and Chrome allow certificates with
|
||||
// Server Gated Crypto as well. Some certificates
|
||||
// in the wild require them.
|
||||
&syscall.OID_SERVER_GATED_CRYPTO[0],
|
||||
&syscall.OID_SGC_NETSCAPE[0],
|
||||
}
|
||||
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
|
||||
para.RequestedUsage.Usage.Length = uint32(len(oids))
|
||||
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
|
||||
} else {
|
||||
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
|
||||
para.RequestedUsage.Usage.Length = 0
|
||||
para.RequestedUsage.Usage.UsageIdentifiers = nil
|
||||
}
|
||||
|
||||
var verifyTime *syscall.Filetime
|
||||
if opts != nil && !opts.CurrentTime.IsZero() {
|
||||
ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
|
||||
verifyTime = &ft
|
||||
}
|
||||
|
||||
// CertGetCertificateChain will traverse Windows's root stores
|
||||
// in an attempt to build a verified certificate chain. Once
|
||||
// it has found a verified chain, it stops. MSDN docs on
|
||||
// CERT_CHAIN_CONTEXT:
|
||||
//
|
||||
// When a CERT_CHAIN_CONTEXT is built, the first simple chain
|
||||
// begins with an end certificate and ends with a self-signed
|
||||
// certificate. If that self-signed certificate is not a root
|
||||
// or otherwise trusted certificate, an attempt is made to
|
||||
// build a new chain. CTLs are used to create the new chain
|
||||
// beginning with the self-signed certificate from the original
|
||||
// chain as the end certificate of the new chain. This process
|
||||
// continues building additional simple chains until the first
|
||||
// self-signed certificate is a trusted certificate or until
|
||||
// an additional simple chain cannot be built.
|
||||
//
|
||||
// The result is that we'll only get a single trusted chain to
|
||||
// return to our caller.
|
||||
var chainCtx *syscall.CertChainContext
|
||||
err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertFreeCertificateChain(chainCtx)
|
||||
|
||||
err = checkChainTrustStatus(c, chainCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hasDNSName {
|
||||
err = checkChainSSLServerPolicy(c, chainCtx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chains = append(chains, chain)
|
||||
|
||||
return chains, nil
|
||||
}
|
||||
|
||||
func initSystemRoots() {
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2012 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 x509
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"github.com/letsencrypt/go/src/encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
const ecPrivKeyVersion = 1
|
||||
|
||||
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
|
||||
// References:
|
||||
// RFC5915
|
||||
// SEC1 - http://www.secg.org/sec1-v2.pdf
|
||||
// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
|
||||
// most cases it is not.
|
||||
type ecPrivateKey struct {
|
||||
Version int
|
||||
PrivateKey []byte
|
||||
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
|
||||
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
|
||||
}
|
||||
|
||||
// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
|
||||
func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
|
||||
return parseECPrivateKey(nil, der)
|
||||
}
|
||||
|
||||
// MarshalECPrivateKey marshals an EC private key into ASN.1, DER format.
|
||||
func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||
oid, ok := oidFromNamedCurve(key.Curve)
|
||||
if !ok {
|
||||
return nil, errors.New("x509: unknown elliptic curve")
|
||||
}
|
||||
return asn1.Marshal(ecPrivateKey{
|
||||
Version: 1,
|
||||
PrivateKey: key.D.Bytes(),
|
||||
NamedCurveOID: oid,
|
||||
PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
|
||||
})
|
||||
}
|
||||
|
||||
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
|
||||
// The OID for the named curve may be provided from another source (such as
|
||||
// the PKCS8 container) - if it is provided then use this instead of the OID
|
||||
// that may exist in the EC private key structure.
|
||||
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
|
||||
var privKey ecPrivateKey
|
||||
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||
return nil, errors.New("x509: failed to parse EC private key: " + err.Error())
|
||||
}
|
||||
if privKey.Version != ecPrivKeyVersion {
|
||||
return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
|
||||
}
|
||||
|
||||
var curve elliptic.Curve
|
||||
if namedCurveOID != nil {
|
||||
curve = namedCurveFromOID(*namedCurveOID)
|
||||
} else {
|
||||
curve = namedCurveFromOID(privKey.NamedCurveOID)
|
||||
}
|
||||
if curve == nil {
|
||||
return nil, errors.New("x509: unknown elliptic curve")
|
||||
}
|
||||
|
||||
k := new(big.Int).SetBytes(privKey.PrivateKey)
|
||||
if k.Cmp(curve.Params().N) >= 0 {
|
||||
return nil, errors.New("x509: invalid elliptic curve private key value")
|
||||
}
|
||||
priv := new(ecdsa.PrivateKey)
|
||||
priv.Curve = curve
|
||||
priv.D = k
|
||||
priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey)
|
||||
|
||||
return priv, nil
|
||||
}
|
|
@ -0,0 +1,483 @@
|
|||
// 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 x509
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type InvalidReason int
|
||||
|
||||
const (
|
||||
// NotAuthorizedToSign results when a certificate is signed by another
|
||||
// which isn't marked as a CA certificate.
|
||||
NotAuthorizedToSign InvalidReason = iota
|
||||
// Expired results when a certificate has expired, based on the time
|
||||
// given in the VerifyOptions.
|
||||
Expired
|
||||
// CANotAuthorizedForThisName results when an intermediate or root
|
||||
// certificate has a name constraint which doesn't include the name
|
||||
// being checked.
|
||||
CANotAuthorizedForThisName
|
||||
// TooManyIntermediates results when a path length constraint is
|
||||
// violated.
|
||||
TooManyIntermediates
|
||||
// IncompatibleUsage results when the certificate's key usage indicates
|
||||
// that it may only be used for a different purpose.
|
||||
IncompatibleUsage
|
||||
)
|
||||
|
||||
// CertificateInvalidError results when an odd error occurs. Users of this
|
||||
// library probably want to handle all these errors uniformly.
|
||||
type CertificateInvalidError struct {
|
||||
Cert *Certificate
|
||||
Reason InvalidReason
|
||||
}
|
||||
|
||||
func (e CertificateInvalidError) Error() string {
|
||||
switch e.Reason {
|
||||
case NotAuthorizedToSign:
|
||||
return "x509: certificate is not authorized to sign other certificates"
|
||||
case Expired:
|
||||
return "x509: certificate has expired or is not yet valid"
|
||||
case CANotAuthorizedForThisName:
|
||||
return "x509: a root or intermediate certificate is not authorized to sign in this domain"
|
||||
case TooManyIntermediates:
|
||||
return "x509: too many intermediates for path length constraint"
|
||||
case IncompatibleUsage:
|
||||
return "x509: certificate specifies an incompatible key usage"
|
||||
}
|
||||
return "x509: unknown error"
|
||||
}
|
||||
|
||||
// HostnameError results when the set of authorized names doesn't match the
|
||||
// requested name.
|
||||
type HostnameError struct {
|
||||
Certificate *Certificate
|
||||
Host string
|
||||
}
|
||||
|
||||
func (h HostnameError) Error() string {
|
||||
c := h.Certificate
|
||||
|
||||
var valid string
|
||||
if ip := net.ParseIP(h.Host); ip != nil {
|
||||
// Trying to validate an IP
|
||||
if len(c.IPAddresses) == 0 {
|
||||
return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs"
|
||||
}
|
||||
for _, san := range c.IPAddresses {
|
||||
if len(valid) > 0 {
|
||||
valid += ", "
|
||||
}
|
||||
valid += san.String()
|
||||
}
|
||||
} else {
|
||||
if len(c.DNSNames) > 0 {
|
||||
valid = strings.Join(c.DNSNames, ", ")
|
||||
} else {
|
||||
valid = c.Subject.CommonName
|
||||
}
|
||||
}
|
||||
return "x509: certificate is valid for " + valid + ", not " + h.Host
|
||||
}
|
||||
|
||||
// UnknownAuthorityError results when the certificate issuer is unknown
|
||||
type UnknownAuthorityError struct {
|
||||
cert *Certificate
|
||||
// hintErr contains an error that may be helpful in determining why an
|
||||
// authority wasn't found.
|
||||
hintErr error
|
||||
// hintCert contains a possible authority certificate that was rejected
|
||||
// because of the error in hintErr.
|
||||
hintCert *Certificate
|
||||
}
|
||||
|
||||
func (e UnknownAuthorityError) Error() string {
|
||||
s := "x509: certificate signed by unknown authority"
|
||||
if e.hintErr != nil {
|
||||
certName := e.hintCert.Subject.CommonName
|
||||
if len(certName) == 0 {
|
||||
if len(e.hintCert.Subject.Organization) > 0 {
|
||||
certName = e.hintCert.Subject.Organization[0]
|
||||
}
|
||||
certName = "serial:" + e.hintCert.SerialNumber.String()
|
||||
}
|
||||
s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SystemRootsError results when we fail to load the system root certificates.
|
||||
type SystemRootsError struct{}
|
||||
|
||||
func (SystemRootsError) Error() string {
|
||||
return "x509: failed to load system roots and no roots provided"
|
||||
}
|
||||
|
||||
// VerifyOptions contains parameters for Certificate.Verify. It's a structure
|
||||
// because other PKIX verification APIs have ended up needing many options.
|
||||
type VerifyOptions struct {
|
||||
DNSName string
|
||||
Intermediates *CertPool
|
||||
Roots *CertPool // if nil, the system roots are used
|
||||
CurrentTime time.Time // if zero, the current time is used
|
||||
// KeyUsage specifies which Extended Key Usage values are acceptable.
|
||||
// An empty list means ExtKeyUsageServerAuth. Key usage is considered a
|
||||
// constraint down the chain which mirrors Windows CryptoAPI behaviour,
|
||||
// but not the spec. To accept any key usage, include ExtKeyUsageAny.
|
||||
KeyUsages []ExtKeyUsage
|
||||
}
|
||||
|
||||
const (
|
||||
leafCertificate = iota
|
||||
intermediateCertificate
|
||||
rootCertificate
|
||||
)
|
||||
|
||||
// isValid performs validity checks on the c.
|
||||
func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
|
||||
now := opts.CurrentTime
|
||||
if now.IsZero() {
|
||||
now = time.Now()
|
||||
}
|
||||
if now.Before(c.NotBefore) || now.After(c.NotAfter) {
|
||||
return CertificateInvalidError{c, Expired}
|
||||
}
|
||||
|
||||
if len(c.PermittedDNSDomains) > 0 {
|
||||
ok := false
|
||||
for _, domain := range c.PermittedDNSDomains {
|
||||
if opts.DNSName == domain ||
|
||||
(strings.HasSuffix(opts.DNSName, domain) &&
|
||||
len(opts.DNSName) >= 1+len(domain) &&
|
||||
opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return CertificateInvalidError{c, CANotAuthorizedForThisName}
|
||||
}
|
||||
}
|
||||
|
||||
// KeyUsage status flags are ignored. From Engineering Security, Peter
|
||||
// Gutmann: A European government CA marked its signing certificates as
|
||||
// being valid for encryption only, but no-one noticed. Another
|
||||
// European CA marked its signature keys as not being valid for
|
||||
// signatures. A different CA marked its own trusted root certificate
|
||||
// as being invalid for certificate signing. Another national CA
|
||||
// distributed a certificate to be used to encrypt data for the
|
||||
// country’s tax authority that was marked as only being usable for
|
||||
// digital signatures but not for encryption. Yet another CA reversed
|
||||
// the order of the bit flags in the keyUsage due to confusion over
|
||||
// encoding endianness, essentially setting a random keyUsage in
|
||||
// certificates that it issued. Another CA created a self-invalidating
|
||||
// certificate by adding a certificate policy statement stipulating
|
||||
// that the certificate had to be used strictly as specified in the
|
||||
// keyUsage, and a keyUsage containing a flag indicating that the RSA
|
||||
// encryption key could only be used for Diffie-Hellman key agreement.
|
||||
|
||||
if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) {
|
||||
return CertificateInvalidError{c, NotAuthorizedToSign}
|
||||
}
|
||||
|
||||
if c.BasicConstraintsValid && c.MaxPathLen >= 0 {
|
||||
numIntermediates := len(currentChain) - 1
|
||||
if numIntermediates > c.MaxPathLen {
|
||||
return CertificateInvalidError{c, TooManyIntermediates}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify attempts to verify c by building one or more chains from c to a
|
||||
// certificate in opts.Roots, using certificates in opts.Intermediates if
|
||||
// needed. If successful, it returns one or more chains where the first
|
||||
// element of the chain is c and the last element is from opts.Roots.
|
||||
//
|
||||
// If opts.Roots is nil and system roots are unavailable the returned error
|
||||
// will be of type SystemRootsError.
|
||||
//
|
||||
// WARNING: this doesn't do any revocation checking.
|
||||
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
// Use Windows's own verification and chain building.
|
||||
if opts.Roots == nil && runtime.GOOS == "windows" {
|
||||
return c.systemVerify(&opts)
|
||||
}
|
||||
|
||||
if len(c.UnhandledCriticalExtensions) > 0 {
|
||||
return nil, UnhandledCriticalExtension{}
|
||||
}
|
||||
|
||||
if opts.Roots == nil {
|
||||
opts.Roots = systemRootsPool()
|
||||
if opts.Roots == nil {
|
||||
return nil, SystemRootsError{}
|
||||
}
|
||||
}
|
||||
|
||||
err = c.isValid(leafCertificate, nil, &opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(opts.DNSName) > 0 {
|
||||
err = c.VerifyHostname(opts.DNSName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
keyUsages := opts.KeyUsages
|
||||
if len(keyUsages) == 0 {
|
||||
keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
|
||||
}
|
||||
|
||||
// If any key usage is acceptable then we're done.
|
||||
for _, usage := range keyUsages {
|
||||
if usage == ExtKeyUsageAny {
|
||||
chains = candidateChains
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, candidate := range candidateChains {
|
||||
if checkChainForKeyUsage(candidate, keyUsages) {
|
||||
chains = append(chains, candidate)
|
||||
}
|
||||
}
|
||||
|
||||
if len(chains) == 0 {
|
||||
err = CertificateInvalidError{c, IncompatibleUsage}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
|
||||
n := make([]*Certificate, len(chain)+1)
|
||||
copy(n, chain)
|
||||
n[len(chain)] = cert
|
||||
return n
|
||||
}
|
||||
|
||||
func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
|
||||
for _, rootNum := range possibleRoots {
|
||||
root := opts.Roots.certs[rootNum]
|
||||
err = root.isValid(rootCertificate, currentChain, opts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
chains = append(chains, appendToFreshChain(currentChain, root))
|
||||
}
|
||||
|
||||
possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c)
|
||||
nextIntermediate:
|
||||
for _, intermediateNum := range possibleIntermediates {
|
||||
intermediate := opts.Intermediates.certs[intermediateNum]
|
||||
for _, cert := range currentChain {
|
||||
if cert == intermediate {
|
||||
continue nextIntermediate
|
||||
}
|
||||
}
|
||||
err = intermediate.isValid(intermediateCertificate, currentChain, opts)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var childChains [][]*Certificate
|
||||
childChains, ok := cache[intermediateNum]
|
||||
if !ok {
|
||||
childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts)
|
||||
cache[intermediateNum] = childChains
|
||||
}
|
||||
chains = append(chains, childChains...)
|
||||
}
|
||||
|
||||
if len(chains) > 0 {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if len(chains) == 0 && err == nil {
|
||||
hintErr := rootErr
|
||||
hintCert := failedRoot
|
||||
if hintErr == nil {
|
||||
hintErr = intermediateErr
|
||||
hintCert = failedIntermediate
|
||||
}
|
||||
err = UnknownAuthorityError{c, hintErr, hintCert}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func matchHostnames(pattern, host string) bool {
|
||||
host = strings.TrimSuffix(host, ".")
|
||||
pattern = strings.TrimSuffix(pattern, ".")
|
||||
|
||||
if len(pattern) == 0 || len(host) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
patternParts := strings.Split(pattern, ".")
|
||||
hostParts := strings.Split(host, ".")
|
||||
|
||||
if len(patternParts) != len(hostParts) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, patternPart := range patternParts {
|
||||
if i == 0 && patternPart == "*" {
|
||||
continue
|
||||
}
|
||||
if patternPart != hostParts[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
|
||||
// an explicitly ASCII function to avoid any sharp corners resulting from
|
||||
// performing Unicode operations on DNS labels.
|
||||
func toLowerCaseASCII(in string) string {
|
||||
// If the string is already lower-case then there's nothing to do.
|
||||
isAlreadyLowerCase := true
|
||||
for _, c := range in {
|
||||
if c == utf8.RuneError {
|
||||
// If we get a UTF-8 error then there might be
|
||||
// upper-case ASCII bytes in the invalid sequence.
|
||||
isAlreadyLowerCase = false
|
||||
break
|
||||
}
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
isAlreadyLowerCase = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isAlreadyLowerCase {
|
||||
return in
|
||||
}
|
||||
|
||||
out := []byte(in)
|
||||
for i, c := range out {
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
out[i] += 'a' - 'A'
|
||||
}
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// VerifyHostname returns nil if c is a valid certificate for the named host.
|
||||
// Otherwise it returns an error describing the mismatch.
|
||||
func (c *Certificate) VerifyHostname(h string) error {
|
||||
// IP addresses may be written in [ ].
|
||||
candidateIP := h
|
||||
if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
|
||||
candidateIP = h[1 : len(h)-1]
|
||||
}
|
||||
if ip := net.ParseIP(candidateIP); ip != nil {
|
||||
// We only match IP addresses against IP SANs.
|
||||
// https://tools.ietf.org/html/rfc6125#appendix-B.2
|
||||
for _, candidate := range c.IPAddresses {
|
||||
if ip.Equal(candidate) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return HostnameError{c, candidateIP}
|
||||
}
|
||||
|
||||
lowered := toLowerCaseASCII(h)
|
||||
|
||||
if len(c.DNSNames) > 0 {
|
||||
for _, match := range c.DNSNames {
|
||||
if matchHostnames(toLowerCaseASCII(match), lowered) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// If Subject Alt Name is given, we ignore the common name.
|
||||
} else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return HostnameError{c, h}
|
||||
}
|
||||
|
||||
func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool {
|
||||
usages := make([]ExtKeyUsage, len(keyUsages))
|
||||
copy(usages, keyUsages)
|
||||
|
||||
if len(chain) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
usagesRemaining := len(usages)
|
||||
|
||||
// We walk down the list and cross out any usages that aren't supported
|
||||
// by each certificate. If we cross out all the usages, then the chain
|
||||
// is unacceptable.
|
||||
|
||||
NextCert:
|
||||
for i := len(chain) - 1; i >= 0; i-- {
|
||||
cert := chain[i]
|
||||
if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
|
||||
// The certificate doesn't have any extended key usage specified.
|
||||
continue
|
||||
}
|
||||
|
||||
for _, usage := range cert.ExtKeyUsage {
|
||||
if usage == ExtKeyUsageAny {
|
||||
// The certificate is explicitly good for any usage.
|
||||
continue NextCert
|
||||
}
|
||||
}
|
||||
|
||||
const invalidUsage ExtKeyUsage = -1
|
||||
|
||||
NextRequestedUsage:
|
||||
for i, requestedUsage := range usages {
|
||||
if requestedUsage == invalidUsage {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, usage := range cert.ExtKeyUsage {
|
||||
if requestedUsage == usage {
|
||||
continue NextRequestedUsage
|
||||
} else if requestedUsage == ExtKeyUsageServerAuth &&
|
||||
(usage == ExtKeyUsageNetscapeServerGatedCrypto ||
|
||||
usage == ExtKeyUsageMicrosoftServerGatedCrypto) {
|
||||
// In order to support COMODO
|
||||
// certificate chains, we have to
|
||||
// accept Netscape or Microsoft SGC
|
||||
// usages as equal to ServerAuth.
|
||||
continue NextRequestedUsage
|
||||
}
|
||||
}
|
||||
|
||||
usages[i] = invalidUsage
|
||||
usagesRemaining--
|
||||
if usagesRemaining == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
File diff suppressed because it is too large
Load Diff
53
vendor/github.com/letsencrypt/go/src/crypto/x509/x509_test_import.go
generated
vendored
Normal file
53
vendor/github.com/letsencrypt/go/src/crypto/x509/x509_test_import.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// This file is run by the x509 tests to ensure that a program with minimal
|
||||
// imports can sign certificates without errors resulting from missing hash
|
||||
// functions.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"github.com/letsencrypt/go/src/crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
block, _ := pem.Decode([]byte(pemPrivateKey))
|
||||
rsaPriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic("Failed to parse private key: " + err.Error())
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: "test",
|
||||
Organization: []string{"Σ Acme Co"},
|
||||
},
|
||||
NotBefore: time.Unix(1000, 0),
|
||||
NotAfter: time.Unix(100000, 0),
|
||||
KeyUsage: x509.KeyUsageCertSign,
|
||||
}
|
||||
|
||||
if _, err = x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv); err != nil {
|
||||
panic("failed to create certificate with basic imports: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
|
||||
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
|
||||
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
|
||||
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
|
||||
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
|
||||
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
|
||||
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
|
@ -0,0 +1,962 @@
|
|||
// Copyright 2009 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 asn1 implements parsing of DER-encoded ASN.1 data structures,
|
||||
// as defined in ITU-T Rec X.690.
|
||||
//
|
||||
// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
|
||||
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
|
||||
package asn1
|
||||
|
||||
// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
|
||||
// are different encoding formats for those objects. Here, we'll be dealing
|
||||
// with DER, the Distinguished Encoding Rules. DER is used in X.509 because
|
||||
// it's fast to parse and, unlike BER, has a unique encoding for every object.
|
||||
// When calculating hashes over objects, it's important that the resulting
|
||||
// bytes be the same at both ends and DER removes this margin of error.
|
||||
//
|
||||
// ASN.1 is very complex and this package doesn't attempt to implement
|
||||
// everything by any means.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A StructuralError suggests that the ASN.1 data is valid, but the Go type
|
||||
// which is receiving it doesn't match.
|
||||
type StructuralError struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
|
||||
|
||||
// A SyntaxError suggests that the ASN.1 data is invalid.
|
||||
type SyntaxError struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
|
||||
|
||||
// We start by dealing with each of the primitive types in turn.
|
||||
|
||||
// BOOLEAN
|
||||
|
||||
func parseBool(bytes []byte) (ret bool, err error) {
|
||||
if len(bytes) != 1 {
|
||||
err = SyntaxError{"invalid boolean"}
|
||||
return
|
||||
}
|
||||
|
||||
// DER demands that "If the encoding represents the boolean value TRUE,
|
||||
// its single contents octet shall have all eight bits set to one."
|
||||
// Thus only 0 and 255 are valid encoded values.
|
||||
switch bytes[0] {
|
||||
case 0:
|
||||
ret = false
|
||||
case 0xff:
|
||||
ret = true
|
||||
default:
|
||||
err = SyntaxError{"invalid boolean"}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// INTEGER
|
||||
|
||||
// parseInt64 treats the given bytes as a big-endian, signed integer and
|
||||
// returns the result.
|
||||
func parseInt64(bytes []byte) (ret int64, err error) {
|
||||
if len(bytes) > 8 {
|
||||
// We'll overflow an int64 in this case.
|
||||
err = StructuralError{"integer too large"}
|
||||
return
|
||||
}
|
||||
for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
|
||||
ret <<= 8
|
||||
ret |= int64(bytes[bytesRead])
|
||||
}
|
||||
|
||||
// Shift up and down in order to sign extend the result.
|
||||
ret <<= 64 - uint8(len(bytes))*8
|
||||
ret >>= 64 - uint8(len(bytes))*8
|
||||
return
|
||||
}
|
||||
|
||||
// parseInt treats the given bytes as a big-endian, signed integer and returns
|
||||
// the result.
|
||||
func parseInt32(bytes []byte) (int32, error) {
|
||||
ret64, err := parseInt64(bytes)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ret64 != int64(int32(ret64)) {
|
||||
return 0, StructuralError{"integer too large"}
|
||||
}
|
||||
return int32(ret64), nil
|
||||
}
|
||||
|
||||
var bigOne = big.NewInt(1)
|
||||
|
||||
// parseBigInt treats the given bytes as a big-endian, signed integer and returns
|
||||
// the result.
|
||||
func parseBigInt(bytes []byte) *big.Int {
|
||||
ret := new(big.Int)
|
||||
if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
|
||||
// This is a negative number.
|
||||
notBytes := make([]byte, len(bytes))
|
||||
for i := range notBytes {
|
||||
notBytes[i] = ^bytes[i]
|
||||
}
|
||||
ret.SetBytes(notBytes)
|
||||
ret.Add(ret, bigOne)
|
||||
ret.Neg(ret)
|
||||
return ret
|
||||
}
|
||||
ret.SetBytes(bytes)
|
||||
return ret
|
||||
}
|
||||
|
||||
// BIT STRING
|
||||
|
||||
// BitString is the structure to use when you want an ASN.1 BIT STRING type. A
|
||||
// bit string is padded up to the nearest byte in memory and the number of
|
||||
// valid bits is recorded. Padding bits will be zero.
|
||||
type BitString struct {
|
||||
Bytes []byte // bits packed into bytes.
|
||||
BitLength int // length in bits.
|
||||
}
|
||||
|
||||
// At returns the bit at the given index. If the index is out of range it
|
||||
// returns false.
|
||||
func (b BitString) At(i int) int {
|
||||
if i < 0 || i >= b.BitLength {
|
||||
return 0
|
||||
}
|
||||
x := i / 8
|
||||
y := 7 - uint(i%8)
|
||||
return int(b.Bytes[x]>>y) & 1
|
||||
}
|
||||
|
||||
// RightAlign returns a slice where the padding bits are at the beginning. The
|
||||
// slice may share memory with the BitString.
|
||||
func (b BitString) RightAlign() []byte {
|
||||
shift := uint(8 - (b.BitLength % 8))
|
||||
if shift == 8 || len(b.Bytes) == 0 {
|
||||
return b.Bytes
|
||||
}
|
||||
|
||||
a := make([]byte, len(b.Bytes))
|
||||
a[0] = b.Bytes[0] >> shift
|
||||
for i := 1; i < len(b.Bytes); i++ {
|
||||
a[i] = b.Bytes[i-1] << (8 - shift)
|
||||
a[i] |= b.Bytes[i] >> shift
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
|
||||
func parseBitString(bytes []byte) (ret BitString, err error) {
|
||||
if len(bytes) == 0 {
|
||||
err = SyntaxError{"zero length BIT STRING"}
|
||||
return
|
||||
}
|
||||
paddingBits := int(bytes[0])
|
||||
if paddingBits > 7 ||
|
||||
len(bytes) == 1 && paddingBits > 0 ||
|
||||
bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
|
||||
err = SyntaxError{"invalid padding bits in BIT STRING"}
|
||||
return
|
||||
}
|
||||
ret.BitLength = (len(bytes)-1)*8 - paddingBits
|
||||
ret.Bytes = bytes[1:]
|
||||
return
|
||||
}
|
||||
|
||||
// OBJECT IDENTIFIER
|
||||
|
||||
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
|
||||
type ObjectIdentifier []int
|
||||
|
||||
// Equal reports whether oi and other represent the same identifier.
|
||||
func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
|
||||
if len(oi) != len(other) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(oi); i++ {
|
||||
if oi[i] != other[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (oi ObjectIdentifier) String() string {
|
||||
var s string
|
||||
|
||||
for i, v := range oi {
|
||||
if i > 0 {
|
||||
s += "."
|
||||
}
|
||||
s += strconv.Itoa(v)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
|
||||
// returns it. An object identifier is a sequence of variable length integers
|
||||
// that are assigned in a hierarchy.
|
||||
func parseObjectIdentifier(bytes []byte) (s []int, err error) {
|
||||
if len(bytes) == 0 {
|
||||
err = SyntaxError{"zero length OBJECT IDENTIFIER"}
|
||||
return
|
||||
}
|
||||
|
||||
// In the worst case, we get two elements from the first byte (which is
|
||||
// encoded differently) and then every varint is a single byte long.
|
||||
s = make([]int, len(bytes)+1)
|
||||
|
||||
// The first varint is 40*value1 + value2:
|
||||
// According to this packing, value1 can take the values 0, 1 and 2 only.
|
||||
// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
|
||||
// then there are no restrictions on value2.
|
||||
v, offset, err := parseBase128Int(bytes, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if v < 80 {
|
||||
s[0] = v / 40
|
||||
s[1] = v % 40
|
||||
} else {
|
||||
s[0] = 2
|
||||
s[1] = v - 80
|
||||
}
|
||||
|
||||
i := 2
|
||||
for ; offset < len(bytes); i++ {
|
||||
v, offset, err = parseBase128Int(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s[i] = v
|
||||
}
|
||||
s = s[0:i]
|
||||
return
|
||||
}
|
||||
|
||||
// ENUMERATED
|
||||
|
||||
// An Enumerated is represented as a plain int.
|
||||
type Enumerated int
|
||||
|
||||
// FLAG
|
||||
|
||||
// A Flag accepts any data and is set to true if present.
|
||||
type Flag bool
|
||||
|
||||
// parseBase128Int parses a base-128 encoded int from the given offset in the
|
||||
// given byte slice. It returns the value and the new offset.
|
||||
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
|
||||
offset = initOffset
|
||||
for shifted := 0; offset < len(bytes); shifted++ {
|
||||
if shifted > 4 {
|
||||
err = StructuralError{"base 128 integer too large"}
|
||||
return
|
||||
}
|
||||
ret <<= 7
|
||||
b := bytes[offset]
|
||||
ret |= int(b & 0x7f)
|
||||
offset++
|
||||
if b&0x80 == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = SyntaxError{"truncated base 128 integer"}
|
||||
return
|
||||
}
|
||||
|
||||
// UTCTime
|
||||
|
||||
func parseUTCTime(bytes []byte) (ret time.Time, err error) {
|
||||
s := string(bytes)
|
||||
|
||||
formatStr := "0601021504Z0700"
|
||||
ret, err = time.Parse(formatStr, s)
|
||||
if err != nil {
|
||||
formatStr = "060102150405Z0700"
|
||||
ret, err = time.Parse(formatStr, s)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if serialized := ret.Format(formatStr); serialized != s {
|
||||
err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
|
||||
return
|
||||
}
|
||||
|
||||
if ret.Year() >= 2050 {
|
||||
// UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
|
||||
ret = ret.AddDate(-100, 0, 0)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
|
||||
// and returns the resulting time.
|
||||
func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
|
||||
const formatStr = "20060102150405Z0700"
|
||||
s := string(bytes)
|
||||
|
||||
if ret, err = time.Parse(formatStr, s); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if serialized := ret.Format(formatStr); serialized != s {
|
||||
err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// PrintableString
|
||||
|
||||
// parsePrintableString parses a ASN.1 PrintableString from the given byte
|
||||
// array and returns it.
|
||||
func parsePrintableString(bytes []byte) (ret string, err error) {
|
||||
for _, b := range bytes {
|
||||
if !isPrintable(b) {
|
||||
err = SyntaxError{"PrintableString contains invalid character"}
|
||||
return
|
||||
}
|
||||
}
|
||||
ret = string(bytes)
|
||||
return
|
||||
}
|
||||
|
||||
// isPrintable reports whether the given b is in the ASN.1 PrintableString set.
|
||||
func isPrintable(b byte) bool {
|
||||
return 'a' <= b && b <= 'z' ||
|
||||
'A' <= b && b <= 'Z' ||
|
||||
'0' <= b && b <= '9' ||
|
||||
'\'' <= b && b <= ')' ||
|
||||
'+' <= b && b <= '/' ||
|
||||
b == ' ' ||
|
||||
b == ':' ||
|
||||
b == '=' ||
|
||||
b == '?' ||
|
||||
// This is technically not allowed in a PrintableString.
|
||||
// However, x509 certificates with wildcard strings don't
|
||||
// always use the correct string type so we permit it.
|
||||
b == '*'
|
||||
}
|
||||
|
||||
// IA5String
|
||||
|
||||
// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
|
||||
// byte slice and returns it.
|
||||
func parseIA5String(bytes []byte) (ret string, err error) {
|
||||
for _, b := range bytes {
|
||||
if b >= 0x80 {
|
||||
err = SyntaxError{"IA5String contains invalid character"}
|
||||
return
|
||||
}
|
||||
}
|
||||
ret = string(bytes)
|
||||
return
|
||||
}
|
||||
|
||||
// T61String
|
||||
|
||||
// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
|
||||
// byte slice and returns it.
|
||||
func parseT61String(bytes []byte) (ret string, err error) {
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// UTF8String
|
||||
|
||||
// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
|
||||
// array and returns it.
|
||||
func parseUTF8String(bytes []byte) (ret string, err error) {
|
||||
if !utf8.Valid(bytes) {
|
||||
return "", errors.New("asn1: invalid UTF-8 string")
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// A RawValue represents an undecoded ASN.1 object.
|
||||
type RawValue struct {
|
||||
Class, Tag int
|
||||
IsCompound bool
|
||||
Bytes []byte
|
||||
FullBytes []byte // includes the tag and length
|
||||
}
|
||||
|
||||
// RawContent is used to signal that the undecoded, DER data needs to be
|
||||
// preserved for a struct. To use it, the first field of the struct must have
|
||||
// this type. It's an error for any of the other fields to have this type.
|
||||
type RawContent []byte
|
||||
|
||||
// Tagging
|
||||
|
||||
// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
|
||||
// into a byte slice. It returns the parsed data and the new offset. SET and
|
||||
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
|
||||
// don't distinguish between ordered and unordered objects in this code.
|
||||
func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err error) {
|
||||
offset = initOffset
|
||||
// parseTagAndLength should not be called without at least a single
|
||||
// byte to read. Thus this check is for robustness:
|
||||
if offset >= len(bytes) {
|
||||
err = errors.New("asn1: internal error in parseTagAndLength")
|
||||
return
|
||||
}
|
||||
b := bytes[offset]
|
||||
offset++
|
||||
ret.class = int(b >> 6)
|
||||
ret.isCompound = b&0x20 == 0x20
|
||||
ret.tag = int(b & 0x1f)
|
||||
|
||||
// If the bottom five bits are set, then the tag number is actually base 128
|
||||
// encoded afterwards
|
||||
if ret.tag == 0x1f {
|
||||
ret.tag, offset, err = parseBase128Int(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if offset >= len(bytes) {
|
||||
err = SyntaxError{"truncated tag or length"}
|
||||
return
|
||||
}
|
||||
b = bytes[offset]
|
||||
offset++
|
||||
if b&0x80 == 0 {
|
||||
// The length is encoded in the bottom 7 bits.
|
||||
ret.length = int(b & 0x7f)
|
||||
} else {
|
||||
// Bottom 7 bits give the number of length bytes to follow.
|
||||
numBytes := int(b & 0x7f)
|
||||
if numBytes == 0 {
|
||||
err = SyntaxError{"indefinite length found (not DER)"}
|
||||
return
|
||||
}
|
||||
ret.length = 0
|
||||
for i := 0; i < numBytes; i++ {
|
||||
if offset >= len(bytes) {
|
||||
err = SyntaxError{"truncated tag or length"}
|
||||
return
|
||||
}
|
||||
b = bytes[offset]
|
||||
offset++
|
||||
if ret.length >= 1<<23 {
|
||||
// We can't shift ret.length up without
|
||||
// overflowing.
|
||||
err = StructuralError{"length too large"}
|
||||
return
|
||||
}
|
||||
ret.length <<= 8
|
||||
ret.length |= int(b)
|
||||
if ret.length == 0 {
|
||||
// DER requires that lengths be minimal.
|
||||
err = StructuralError{"superfluous leading zeros in length"}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
|
||||
// a number of ASN.1 values from the given byte slice and returns them as a
|
||||
// slice of Go values of the given type.
|
||||
func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) {
|
||||
expectedTag, compoundType, ok := getUniversalType(elemType)
|
||||
if !ok {
|
||||
err = StructuralError{"unknown Go type for slice"}
|
||||
return
|
||||
}
|
||||
|
||||
// First we iterate over the input and count the number of elements,
|
||||
// checking that the types are correct in each case.
|
||||
numElements := 0
|
||||
for offset := 0; offset < len(bytes); {
|
||||
var t tagAndLength
|
||||
t, offset, err = parseTagAndLength(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch t.tag {
|
||||
case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
|
||||
// We pretend that various other string types are
|
||||
// PRINTABLE STRINGs so that a sequence of them can be
|
||||
// parsed into a []string.
|
||||
t.tag = tagPrintableString
|
||||
case tagGeneralizedTime, tagUTCTime:
|
||||
// Likewise, both time types are treated the same.
|
||||
t.tag = tagUTCTime
|
||||
}
|
||||
|
||||
if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
|
||||
err = StructuralError{"sequence tag mismatch"}
|
||||
return
|
||||
}
|
||||
if invalidLength(offset, t.length, len(bytes)) {
|
||||
err = SyntaxError{"truncated sequence"}
|
||||
return
|
||||
}
|
||||
offset += t.length
|
||||
numElements++
|
||||
}
|
||||
ret = reflect.MakeSlice(sliceType, numElements, numElements)
|
||||
params := fieldParameters{}
|
||||
offset := 0
|
||||
for i := 0; i < numElements; i++ {
|
||||
offset, err = parseField(ret.Index(i), bytes, offset, params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
bitStringType = reflect.TypeOf(BitString{})
|
||||
objectIdentifierType = reflect.TypeOf(ObjectIdentifier{})
|
||||
enumeratedType = reflect.TypeOf(Enumerated(0))
|
||||
flagType = reflect.TypeOf(Flag(false))
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
rawValueType = reflect.TypeOf(RawValue{})
|
||||
rawContentsType = reflect.TypeOf(RawContent(nil))
|
||||
bigIntType = reflect.TypeOf(new(big.Int))
|
||||
)
|
||||
|
||||
// invalidLength returns true iff offset + length > sliceLength, or if the
|
||||
// addition would overflow.
|
||||
func invalidLength(offset, length, sliceLength int) bool {
|
||||
return offset+length < offset || offset+length > sliceLength
|
||||
}
|
||||
|
||||
// parseField is the main parsing function. Given a byte slice and an offset
|
||||
// into the array, it will try to parse a suitable ASN.1 value out and store it
|
||||
// in the given Value.
|
||||
func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err error) {
|
||||
offset = initOffset
|
||||
fieldType := v.Type()
|
||||
|
||||
// If we have run out of data, it may be that there are optional elements at the end.
|
||||
if offset == len(bytes) {
|
||||
if !setDefaultValue(v, params) {
|
||||
err = SyntaxError{"sequence truncated"}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Deal with raw values.
|
||||
if fieldType == rawValueType {
|
||||
var t tagAndLength
|
||||
t, offset, err = parseTagAndLength(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if invalidLength(offset, t.length, len(bytes)) {
|
||||
err = SyntaxError{"data truncated"}
|
||||
return
|
||||
}
|
||||
result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]}
|
||||
offset += t.length
|
||||
v.Set(reflect.ValueOf(result))
|
||||
return
|
||||
}
|
||||
|
||||
// Deal with the ANY type.
|
||||
if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
|
||||
var t tagAndLength
|
||||
t, offset, err = parseTagAndLength(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if invalidLength(offset, t.length, len(bytes)) {
|
||||
err = SyntaxError{"data truncated"}
|
||||
return
|
||||
}
|
||||
var result interface{}
|
||||
if !t.isCompound && t.class == classUniversal {
|
||||
innerBytes := bytes[offset : offset+t.length]
|
||||
switch t.tag {
|
||||
case tagPrintableString:
|
||||
result, err = parsePrintableString(innerBytes)
|
||||
case tagIA5String:
|
||||
result, err = parseIA5String(innerBytes)
|
||||
case tagT61String:
|
||||
result, err = parseT61String(innerBytes)
|
||||
case tagUTF8String:
|
||||
result, err = parseUTF8String(innerBytes)
|
||||
case tagInteger:
|
||||
result, err = parseInt64(innerBytes)
|
||||
case tagBitString:
|
||||
result, err = parseBitString(innerBytes)
|
||||
case tagOID:
|
||||
result, err = parseObjectIdentifier(innerBytes)
|
||||
case tagUTCTime:
|
||||
result, err = parseUTCTime(innerBytes)
|
||||
case tagGeneralizedTime:
|
||||
result, err = parseGeneralizedTime(innerBytes)
|
||||
case tagOctetString:
|
||||
result = innerBytes
|
||||
default:
|
||||
// If we don't know how to handle the type, we just leave Value as nil.
|
||||
}
|
||||
}
|
||||
offset += t.length
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result != nil {
|
||||
v.Set(reflect.ValueOf(result))
|
||||
}
|
||||
return
|
||||
}
|
||||
universalTag, compoundType, ok1 := getUniversalType(fieldType)
|
||||
if !ok1 {
|
||||
err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)}
|
||||
return
|
||||
}
|
||||
|
||||
t, offset, err := parseTagAndLength(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if params.explicit {
|
||||
expectedClass := classContextSpecific
|
||||
if params.application {
|
||||
expectedClass = classApplication
|
||||
}
|
||||
if offset == len(bytes) {
|
||||
err = StructuralError{"explicit tag has no child"}
|
||||
return
|
||||
}
|
||||
if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
|
||||
if t.length > 0 {
|
||||
t, offset, err = parseTagAndLength(bytes, offset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if fieldType != flagType {
|
||||
err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
|
||||
return
|
||||
}
|
||||
v.SetBool(true)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// The tags didn't match, it might be an optional element.
|
||||
ok := setDefaultValue(v, params)
|
||||
if ok {
|
||||
offset = initOffset
|
||||
} else {
|
||||
err = StructuralError{"explicitly tagged member didn't match"}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for strings: all the ASN.1 string types map to the Go
|
||||
// type string. getUniversalType returns the tag for PrintableString
|
||||
// when it sees a string, so if we see a different string type on the
|
||||
// wire, we change the universal type to match.
|
||||
if universalTag == tagPrintableString {
|
||||
if t.class == classUniversal {
|
||||
switch t.tag {
|
||||
case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
|
||||
universalTag = t.tag
|
||||
}
|
||||
} else if params.stringType != 0 {
|
||||
universalTag = params.stringType
|
||||
}
|
||||
}
|
||||
|
||||
// Special case for time: UTCTime and GeneralizedTime both map to the
|
||||
// Go type time.Time.
|
||||
if universalTag == tagUTCTime && t.tag == tagGeneralizedTime && t.class == classUniversal {
|
||||
universalTag = tagGeneralizedTime
|
||||
}
|
||||
|
||||
if params.set {
|
||||
universalTag = tagSet
|
||||
}
|
||||
|
||||
expectedClass := classUniversal
|
||||
expectedTag := universalTag
|
||||
|
||||
if !params.explicit && params.tag != nil {
|
||||
expectedClass = classContextSpecific
|
||||
expectedTag = *params.tag
|
||||
}
|
||||
|
||||
if !params.explicit && params.application && params.tag != nil {
|
||||
expectedClass = classApplication
|
||||
expectedTag = *params.tag
|
||||
}
|
||||
|
||||
// We have unwrapped any explicit tagging at this point.
|
||||
if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
|
||||
// Tags don't match. Again, it could be an optional element.
|
||||
ok := setDefaultValue(v, params)
|
||||
if ok {
|
||||
offset = initOffset
|
||||
} else {
|
||||
err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)}
|
||||
}
|
||||
return
|
||||
}
|
||||
if invalidLength(offset, t.length, len(bytes)) {
|
||||
err = SyntaxError{"data truncated"}
|
||||
return
|
||||
}
|
||||
innerBytes := bytes[offset : offset+t.length]
|
||||
offset += t.length
|
||||
|
||||
// We deal with the structures defined in this package first.
|
||||
switch fieldType {
|
||||
case objectIdentifierType:
|
||||
newSlice, err1 := parseObjectIdentifier(innerBytes)
|
||||
v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice)))
|
||||
if err1 == nil {
|
||||
reflect.Copy(v, reflect.ValueOf(newSlice))
|
||||
}
|
||||
err = err1
|
||||
return
|
||||
case bitStringType:
|
||||
bs, err1 := parseBitString(innerBytes)
|
||||
if err1 == nil {
|
||||
v.Set(reflect.ValueOf(bs))
|
||||
}
|
||||
err = err1
|
||||
return
|
||||
case timeType:
|
||||
var time time.Time
|
||||
var err1 error
|
||||
if universalTag == tagUTCTime {
|
||||
time, err1 = parseUTCTime(innerBytes)
|
||||
} else {
|
||||
time, err1 = parseGeneralizedTime(innerBytes)
|
||||
}
|
||||
if err1 == nil {
|
||||
v.Set(reflect.ValueOf(time))
|
||||
}
|
||||
err = err1
|
||||
return
|
||||
case enumeratedType:
|
||||
parsedInt, err1 := parseInt32(innerBytes)
|
||||
if err1 == nil {
|
||||
v.SetInt(int64(parsedInt))
|
||||
}
|
||||
err = err1
|
||||
return
|
||||
case flagType:
|
||||
v.SetBool(true)
|
||||
return
|
||||
case bigIntType:
|
||||
parsedInt := parseBigInt(innerBytes)
|
||||
v.Set(reflect.ValueOf(parsedInt))
|
||||
return
|
||||
}
|
||||
switch val := v; val.Kind() {
|
||||
case reflect.Bool:
|
||||
parsedBool, err1 := parseBool(innerBytes)
|
||||
if err1 == nil {
|
||||
val.SetBool(parsedBool)
|
||||
}
|
||||
err = err1
|
||||
return
|
||||
case reflect.Int, reflect.Int32, reflect.Int64:
|
||||
if val.Type().Size() == 4 {
|
||||
parsedInt, err1 := parseInt32(innerBytes)
|
||||
if err1 == nil {
|
||||
val.SetInt(int64(parsedInt))
|
||||
}
|
||||
err = err1
|
||||
} else {
|
||||
parsedInt, err1 := parseInt64(innerBytes)
|
||||
if err1 == nil {
|
||||
val.SetInt(parsedInt)
|
||||
}
|
||||
err = err1
|
||||
}
|
||||
return
|
||||
// TODO(dfc) Add support for the remaining integer types
|
||||
case reflect.Struct:
|
||||
structType := fieldType
|
||||
|
||||
if structType.NumField() > 0 &&
|
||||
structType.Field(0).Type == rawContentsType {
|
||||
bytes := bytes[initOffset:offset]
|
||||
val.Field(0).Set(reflect.ValueOf(RawContent(bytes)))
|
||||
}
|
||||
|
||||
innerOffset := 0
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
field := structType.Field(i)
|
||||
if i == 0 && field.Type == rawContentsType {
|
||||
continue
|
||||
}
|
||||
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// We allow extra bytes at the end of the SEQUENCE because
|
||||
// adding elements to the end has been used in X.509 as the
|
||||
// version numbers have increased.
|
||||
return
|
||||
case reflect.Slice:
|
||||
sliceType := fieldType
|
||||
if sliceType.Elem().Kind() == reflect.Uint8 {
|
||||
val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes)))
|
||||
reflect.Copy(val, reflect.ValueOf(innerBytes))
|
||||
return
|
||||
}
|
||||
newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem())
|
||||
if err1 == nil {
|
||||
val.Set(newSlice)
|
||||
}
|
||||
err = err1
|
||||
return
|
||||
case reflect.String:
|
||||
var v string
|
||||
switch universalTag {
|
||||
case tagPrintableString:
|
||||
v, err = parsePrintableString(innerBytes)
|
||||
case tagIA5String:
|
||||
v, err = parseIA5String(innerBytes)
|
||||
case tagT61String:
|
||||
v, err = parseT61String(innerBytes)
|
||||
case tagUTF8String:
|
||||
v, err = parseUTF8String(innerBytes)
|
||||
case tagGeneralString:
|
||||
// GeneralString is specified in ISO-2022/ECMA-35,
|
||||
// A brief review suggests that it includes structures
|
||||
// that allow the encoding to change midstring and
|
||||
// such. We give up and pass it as an 8-bit string.
|
||||
v, err = parseT61String(innerBytes)
|
||||
default:
|
||||
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
|
||||
}
|
||||
if err == nil {
|
||||
val.SetString(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
err = StructuralError{"unsupported: " + v.Type().String()}
|
||||
return
|
||||
}
|
||||
|
||||
// canHaveDefaultValue reports whether k is a Kind that we will set a default
|
||||
// value for. (A signed integer, essentially.)
|
||||
func canHaveDefaultValue(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// setDefaultValue is used to install a default value, from a tag string, into
|
||||
// a Value. It is successful if the field was optional, even if a default value
|
||||
// wasn't provided or it failed to install it into the Value.
|
||||
func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
|
||||
if !params.optional {
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
if params.defaultValue == nil {
|
||||
return
|
||||
}
|
||||
if canHaveDefaultValue(v.Kind()) {
|
||||
v.SetInt(*params.defaultValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal parses the DER-encoded ASN.1 data structure b
|
||||
// and uses the reflect package to fill in an arbitrary value pointed at by val.
|
||||
// Because Unmarshal uses the reflect package, the structs
|
||||
// being written to must use upper case field names.
|
||||
//
|
||||
// An ASN.1 INTEGER can be written to an int, int32, int64,
|
||||
// or *big.Int (from the math/big package).
|
||||
// If the encoded value does not fit in the Go type,
|
||||
// Unmarshal returns a parse error.
|
||||
//
|
||||
// An ASN.1 BIT STRING can be written to a BitString.
|
||||
//
|
||||
// An ASN.1 OCTET STRING can be written to a []byte.
|
||||
//
|
||||
// An ASN.1 OBJECT IDENTIFIER can be written to an
|
||||
// ObjectIdentifier.
|
||||
//
|
||||
// An ASN.1 ENUMERATED can be written to an Enumerated.
|
||||
//
|
||||
// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
|
||||
//
|
||||
// An ASN.1 PrintableString or IA5String can be written to a string.
|
||||
//
|
||||
// Any of the above ASN.1 values can be written to an interface{}.
|
||||
// The value stored in the interface has the corresponding Go type.
|
||||
// For integers, that type is int64.
|
||||
//
|
||||
// An ASN.1 SEQUENCE OF x or SET OF x can be written
|
||||
// to a slice if an x can be written to the slice's element type.
|
||||
//
|
||||
// An ASN.1 SEQUENCE or SET can be written to a struct
|
||||
// if each of the elements in the sequence can be
|
||||
// written to the corresponding element in the struct.
|
||||
//
|
||||
// The following tags on struct fields have special meaning to Unmarshal:
|
||||
//
|
||||
// application specifies that a APPLICATION tag is used
|
||||
// default:x sets the default value for optional integer fields
|
||||
// explicit specifies that an additional, explicit tag wraps the implicit one
|
||||
// optional marks the field as ASN.1 OPTIONAL
|
||||
// set causes a SET, rather than a SEQUENCE type to be expected
|
||||
// tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
|
||||
//
|
||||
// If the type of the first field of a structure is RawContent then the raw
|
||||
// ASN1 contents of the struct will be stored in it.
|
||||
//
|
||||
// If the type name of a slice element ends with "SET" then it's treated as if
|
||||
// the "set" tag was set on it. This can be used with nested slices where a
|
||||
// struct tag cannot be given.
|
||||
//
|
||||
// Other ASN.1 types are not supported; if it encounters them,
|
||||
// Unmarshal returns a parse error.
|
||||
func Unmarshal(b []byte, val interface{}) (rest []byte, err error) {
|
||||
return UnmarshalWithParams(b, val, "")
|
||||
}
|
||||
|
||||
// UnmarshalWithParams allows field parameters to be specified for the
|
||||
// top-level element. The form of the params is the same as the field tags.
|
||||
func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err error) {
|
||||
v := reflect.ValueOf(val).Elem()
|
||||
offset, err := parseField(v, b, 0, parseFieldParameters(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[offset:], nil
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2009 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 asn1
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ASN.1 objects have metadata preceding them:
|
||||
// the tag: the type of the object
|
||||
// a flag denoting if this object is compound or not
|
||||
// the class type: the namespace of the tag
|
||||
// the length of the object, in bytes
|
||||
|
||||
// Here are some standard tags and classes
|
||||
|
||||
const (
|
||||
tagBoolean = 1
|
||||
tagInteger = 2
|
||||
tagBitString = 3
|
||||
tagOctetString = 4
|
||||
tagOID = 6
|
||||
tagEnum = 10
|
||||
tagUTF8String = 12
|
||||
tagSequence = 16
|
||||
tagSet = 17
|
||||
tagPrintableString = 19
|
||||
tagT61String = 20
|
||||
tagIA5String = 22
|
||||
tagUTCTime = 23
|
||||
tagGeneralizedTime = 24
|
||||
tagGeneralString = 27
|
||||
)
|
||||
|
||||
const (
|
||||
classUniversal = 0
|
||||
classApplication = 1
|
||||
classContextSpecific = 2
|
||||
classPrivate = 3
|
||||
)
|
||||
|
||||
type tagAndLength struct {
|
||||
class, tag, length int
|
||||
isCompound bool
|
||||
}
|
||||
|
||||
// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead
|
||||
// of" and "in addition to". When not specified, every primitive type has a
|
||||
// default tag in the UNIVERSAL class.
|
||||
//
|
||||
// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1
|
||||
// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT
|
||||
// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another.
|
||||
//
|
||||
// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an
|
||||
// /additional/ tag would wrap the default tag. This explicit tag will have the
|
||||
// compound flag set.
|
||||
//
|
||||
// (This is used in order to remove ambiguity with optional elements.)
|
||||
//
|
||||
// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we
|
||||
// don't support that here. We support a single layer of EXPLICIT or IMPLICIT
|
||||
// tagging with tag strings on the fields of a structure.
|
||||
|
||||
// fieldParameters is the parsed representation of tag string from a structure field.
|
||||
type fieldParameters struct {
|
||||
optional bool // true iff the field is OPTIONAL
|
||||
explicit bool // true iff an EXPLICIT tag is in use.
|
||||
application bool // true iff an APPLICATION tag is in use.
|
||||
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
|
||||
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
|
||||
stringType int // the string tag to use when marshaling.
|
||||
timeType int // the time tag to use when marshaling.
|
||||
set bool // true iff this should be encoded as a SET
|
||||
omitEmpty bool // true iff this should be omitted if empty when marshaling.
|
||||
|
||||
// Invariants:
|
||||
// if explicit is set, tag is non-nil.
|
||||
}
|
||||
|
||||
// Given a tag string with the format specified in the package comment,
|
||||
// parseFieldParameters will parse it into a fieldParameters structure,
|
||||
// ignoring unknown parts of the string.
|
||||
func parseFieldParameters(str string) (ret fieldParameters) {
|
||||
for _, part := range strings.Split(str, ",") {
|
||||
switch {
|
||||
case part == "optional":
|
||||
ret.optional = true
|
||||
case part == "explicit":
|
||||
ret.explicit = true
|
||||
if ret.tag == nil {
|
||||
ret.tag = new(int)
|
||||
}
|
||||
case part == "generalized":
|
||||
ret.timeType = tagGeneralizedTime
|
||||
case part == "utc":
|
||||
ret.timeType = tagUTCTime
|
||||
case part == "ia5":
|
||||
ret.stringType = tagIA5String
|
||||
case part == "printable":
|
||||
ret.stringType = tagPrintableString
|
||||
case part == "utf8":
|
||||
ret.stringType = tagUTF8String
|
||||
case strings.HasPrefix(part, "default:"):
|
||||
i, err := strconv.ParseInt(part[8:], 10, 64)
|
||||
if err == nil {
|
||||
ret.defaultValue = new(int64)
|
||||
*ret.defaultValue = i
|
||||
}
|
||||
case strings.HasPrefix(part, "tag:"):
|
||||
i, err := strconv.Atoi(part[4:])
|
||||
if err == nil {
|
||||
ret.tag = new(int)
|
||||
*ret.tag = i
|
||||
}
|
||||
case part == "set":
|
||||
ret.set = true
|
||||
case part == "application":
|
||||
ret.application = true
|
||||
if ret.tag == nil {
|
||||
ret.tag = new(int)
|
||||
}
|
||||
case part == "omitempty":
|
||||
ret.omitEmpty = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Given a reflected Go type, getUniversalType returns the default tag number
|
||||
// and expected compound flag.
|
||||
func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
|
||||
switch t {
|
||||
case objectIdentifierType:
|
||||
return tagOID, false, true
|
||||
case bitStringType:
|
||||
return tagBitString, false, true
|
||||
case timeType:
|
||||
return tagUTCTime, false, true
|
||||
case enumeratedType:
|
||||
return tagEnum, false, true
|
||||
case bigIntType:
|
||||
return tagInteger, false, true
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return tagBoolean, false, true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return tagInteger, false, true
|
||||
case reflect.Struct:
|
||||
return tagSequence, true, true
|
||||
case reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
return tagOctetString, false, true
|
||||
}
|
||||
if strings.HasSuffix(t.Name(), "SET") {
|
||||
return tagSet, true, true
|
||||
}
|
||||
return tagSequence, true, true
|
||||
case reflect.String:
|
||||
return tagPrintableString, false, true
|
||||
}
|
||||
return 0, false, false
|
||||
}
|
|
@ -0,0 +1,652 @@
|
|||
// Copyright 2009 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 asn1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A forkableWriter is an in-memory buffer that can be
|
||||
// 'forked' to create new forkableWriters that bracket the
|
||||
// original. After
|
||||
// pre, post := w.fork()
|
||||
// the overall sequence of bytes represented is logically w+pre+post.
|
||||
type forkableWriter struct {
|
||||
*bytes.Buffer
|
||||
pre, post *forkableWriter
|
||||
}
|
||||
|
||||
func newForkableWriter() *forkableWriter {
|
||||
return &forkableWriter{new(bytes.Buffer), nil, nil}
|
||||
}
|
||||
|
||||
func (f *forkableWriter) fork() (pre, post *forkableWriter) {
|
||||
if f.pre != nil || f.post != nil {
|
||||
panic("have already forked")
|
||||
}
|
||||
f.pre = newForkableWriter()
|
||||
f.post = newForkableWriter()
|
||||
return f.pre, f.post
|
||||
}
|
||||
|
||||
func (f *forkableWriter) Len() (l int) {
|
||||
l += f.Buffer.Len()
|
||||
if f.pre != nil {
|
||||
l += f.pre.Len()
|
||||
}
|
||||
if f.post != nil {
|
||||
l += f.post.Len()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) {
|
||||
n, err = out.Write(f.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var nn int
|
||||
|
||||
if f.pre != nil {
|
||||
nn, err = f.pre.writeTo(out)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if f.post != nil {
|
||||
nn, err = f.post.writeTo(out)
|
||||
n += nn
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalBase128Int(out *forkableWriter, n int64) (err error) {
|
||||
if n == 0 {
|
||||
err = out.WriteByte(0)
|
||||
return
|
||||
}
|
||||
|
||||
l := 0
|
||||
for i := n; i > 0; i >>= 7 {
|
||||
l++
|
||||
}
|
||||
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
o := byte(n >> uint(i*7))
|
||||
o &= 0x7f
|
||||
if i != 0 {
|
||||
o |= 0x80
|
||||
}
|
||||
err = out.WriteByte(o)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalInt64(out *forkableWriter, i int64) (err error) {
|
||||
n := int64Length(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func int64Length(i int64) (numBytes int) {
|
||||
numBytes = 1
|
||||
|
||||
for i > 127 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
for i < -128 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func marshalBigInt(out *forkableWriter, n *big.Int) (err error) {
|
||||
if n.Sign() < 0 {
|
||||
// A negative number has to be converted to two's-complement
|
||||
// form. So we'll subtract 1 and invert. If the
|
||||
// most-significant-bit isn't set then we'll need to pad the
|
||||
// beginning with 0xff in order to keep the number negative.
|
||||
nMinus1 := new(big.Int).Neg(n)
|
||||
nMinus1.Sub(nMinus1, bigOne)
|
||||
bytes := nMinus1.Bytes()
|
||||
for i := range bytes {
|
||||
bytes[i] ^= 0xff
|
||||
}
|
||||
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
|
||||
err = out.WriteByte(0xff)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = out.Write(bytes)
|
||||
} else if n.Sign() == 0 {
|
||||
// Zero is written as a single 0 zero rather than no bytes.
|
||||
err = out.WriteByte(0x00)
|
||||
} else {
|
||||
bytes := n.Bytes()
|
||||
if len(bytes) > 0 && bytes[0]&0x80 != 0 {
|
||||
// We'll have to pad this with 0x00 in order to stop it
|
||||
// looking like a negative number.
|
||||
err = out.WriteByte(0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
_, err = out.Write(bytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalLength(out *forkableWriter, i int) (err error) {
|
||||
n := lengthLength(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lengthLength(i int) (numBytes int) {
|
||||
numBytes = 1
|
||||
for i > 255 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
|
||||
b := uint8(t.class) << 6
|
||||
if t.isCompound {
|
||||
b |= 0x20
|
||||
}
|
||||
if t.tag >= 31 {
|
||||
b |= 0x1f
|
||||
err = out.WriteByte(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = marshalBase128Int(out, int64(t.tag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
b |= uint8(t.tag)
|
||||
err = out.WriteByte(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if t.length >= 128 {
|
||||
l := lengthLength(t.length)
|
||||
err = out.WriteByte(0x80 | byte(l))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = marshalLength(out, t.length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = out.WriteByte(byte(t.length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshalBitString(out *forkableWriter, b BitString) (err error) {
|
||||
paddingBits := byte((8 - b.BitLength%8) % 8)
|
||||
err = out.WriteByte(paddingBits)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = out.Write(b.Bytes)
|
||||
return
|
||||
}
|
||||
|
||||
func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) {
|
||||
if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
|
||||
return StructuralError{"invalid object identifier"}
|
||||
}
|
||||
|
||||
err = marshalBase128Int(out, int64(oid[0]*40+oid[1]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 2; i < len(oid); i++ {
|
||||
err = marshalBase128Int(out, int64(oid[i]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func marshalPrintableString(out *forkableWriter, s string) (err error) {
|
||||
b := []byte(s)
|
||||
for _, c := range b {
|
||||
if !isPrintable(c) {
|
||||
return StructuralError{"PrintableString contains invalid character"}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func marshalIA5String(out *forkableWriter, s string) (err error) {
|
||||
b := []byte(s)
|
||||
for _, c := range b {
|
||||
if c > 127 {
|
||||
return StructuralError{"IA5String contains invalid character"}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = out.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func marshalUTF8String(out *forkableWriter, s string) (err error) {
|
||||
_, err = out.Write([]byte(s))
|
||||
return
|
||||
}
|
||||
|
||||
func marshalTwoDigits(out *forkableWriter, v int) (err error) {
|
||||
err = out.WriteByte(byte('0' + (v/10)%10))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return out.WriteByte(byte('0' + v%10))
|
||||
}
|
||||
|
||||
func marshalFourDigits(out *forkableWriter, v int) (err error) {
|
||||
var bytes [4]byte
|
||||
for i := range bytes {
|
||||
bytes[3-i] = '0' + byte(v%10)
|
||||
v /= 10
|
||||
}
|
||||
_, err = out.Write(bytes[:])
|
||||
return
|
||||
}
|
||||
|
||||
func outsideUTCRange(t time.Time) bool {
|
||||
year := t.Year()
|
||||
return year < 1950 || year >= 2050
|
||||
}
|
||||
|
||||
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
|
||||
year := t.Year()
|
||||
|
||||
switch {
|
||||
case 1950 <= year && year < 2000:
|
||||
err = marshalTwoDigits(out, int(year-1900))
|
||||
case 2000 <= year && year < 2050:
|
||||
err = marshalTwoDigits(out, int(year-2000))
|
||||
default:
|
||||
return StructuralError{"cannot represent time as UTCTime"}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return marshalTimeCommon(out, t)
|
||||
}
|
||||
|
||||
func marshalGeneralizedTime(out *forkableWriter, t time.Time) (err error) {
|
||||
year := t.Year()
|
||||
if year < 0 || year > 9999 {
|
||||
return StructuralError{"cannot represent time as GeneralizedTime"}
|
||||
}
|
||||
if err = marshalFourDigits(out, year); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return marshalTimeCommon(out, t)
|
||||
}
|
||||
|
||||
func marshalTimeCommon(out *forkableWriter, t time.Time) (err error) {
|
||||
_, month, day := t.Date()
|
||||
|
||||
err = marshalTwoDigits(out, int(month))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, day)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hour, min, sec := t.Clock()
|
||||
|
||||
err = marshalTwoDigits(out, hour)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, min)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, sec)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, offset := t.Zone()
|
||||
|
||||
switch {
|
||||
case offset/60 == 0:
|
||||
err = out.WriteByte('Z')
|
||||
return
|
||||
case offset > 0:
|
||||
err = out.WriteByte('+')
|
||||
case offset < 0:
|
||||
err = out.WriteByte('-')
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
offsetMinutes := offset / 60
|
||||
if offsetMinutes < 0 {
|
||||
offsetMinutes = -offsetMinutes
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, offsetMinutes/60)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = marshalTwoDigits(out, offsetMinutes%60)
|
||||
return
|
||||
}
|
||||
|
||||
func stripTagAndLength(in []byte) []byte {
|
||||
_, offset, err := parseTagAndLength(in, 0)
|
||||
if err != nil {
|
||||
return in
|
||||
}
|
||||
return in[offset:]
|
||||
}
|
||||
|
||||
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
|
||||
switch value.Type() {
|
||||
case flagType:
|
||||
return nil
|
||||
case timeType:
|
||||
t := value.Interface().(time.Time)
|
||||
if params.timeType == tagGeneralizedTime || outsideUTCRange(t) {
|
||||
return marshalGeneralizedTime(out, t)
|
||||
} else {
|
||||
return marshalUTCTime(out, t)
|
||||
}
|
||||
case bitStringType:
|
||||
return marshalBitString(out, value.Interface().(BitString))
|
||||
case objectIdentifierType:
|
||||
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
|
||||
case bigIntType:
|
||||
return marshalBigInt(out, value.Interface().(*big.Int))
|
||||
}
|
||||
|
||||
switch v := value; v.Kind() {
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return out.WriteByte(255)
|
||||
} else {
|
||||
return out.WriteByte(0)
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return marshalInt64(out, int64(v.Int()))
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
|
||||
startingField := 0
|
||||
|
||||
// If the first element of the structure is a non-empty
|
||||
// RawContents, then we don't bother serializing the rest.
|
||||
if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
|
||||
s := v.Field(0)
|
||||
if s.Len() > 0 {
|
||||
bytes := make([]byte, s.Len())
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
bytes[i] = uint8(s.Index(i).Uint())
|
||||
}
|
||||
/* The RawContents will contain the tag and
|
||||
* length fields but we'll also be writing
|
||||
* those ourselves, so we strip them out of
|
||||
* bytes */
|
||||
_, err = out.Write(stripTagAndLength(bytes))
|
||||
return
|
||||
} else {
|
||||
startingField = 1
|
||||
}
|
||||
}
|
||||
|
||||
for i := startingField; i < t.NumField(); i++ {
|
||||
var pre *forkableWriter
|
||||
pre, out = out.fork()
|
||||
err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
case reflect.Slice:
|
||||
sliceType := v.Type()
|
||||
if sliceType.Elem().Kind() == reflect.Uint8 {
|
||||
bytes := make([]byte, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
bytes[i] = uint8(v.Index(i).Uint())
|
||||
}
|
||||
_, err = out.Write(bytes)
|
||||
return
|
||||
}
|
||||
|
||||
var fp fieldParameters
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
var pre *forkableWriter
|
||||
pre, out = out.fork()
|
||||
err = marshalField(pre, v.Index(i), fp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
case reflect.String:
|
||||
switch params.stringType {
|
||||
case tagIA5String:
|
||||
return marshalIA5String(out, v.String())
|
||||
case tagPrintableString:
|
||||
return marshalPrintableString(out, v.String())
|
||||
default:
|
||||
return marshalUTF8String(out, v.String())
|
||||
}
|
||||
}
|
||||
|
||||
return StructuralError{"unknown Go type"}
|
||||
}
|
||||
|
||||
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) {
|
||||
// If the field is an interface{} then recurse into it.
|
||||
if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
|
||||
return marshalField(out, v.Elem(), params)
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) {
|
||||
defaultValue := reflect.New(v.Type()).Elem()
|
||||
defaultValue.SetInt(*params.defaultValue)
|
||||
|
||||
if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If no default value is given then the zero value for the type is
|
||||
// assumed to be the default value. This isn't obviously the correct
|
||||
// behaviour, but it's what Go has traditionally done.
|
||||
if params.optional && params.defaultValue == nil {
|
||||
if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if v.Type() == rawValueType {
|
||||
rv := v.Interface().(RawValue)
|
||||
if len(rv.FullBytes) != 0 {
|
||||
_, err = out.Write(rv.FullBytes)
|
||||
} else {
|
||||
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = out.Write(rv.Bytes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
tag, isCompound, ok := getUniversalType(v.Type())
|
||||
if !ok {
|
||||
err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
|
||||
return
|
||||
}
|
||||
class := classUniversal
|
||||
|
||||
if params.timeType != 0 && tag != tagUTCTime {
|
||||
return StructuralError{"explicit time type given to non-time member"}
|
||||
}
|
||||
|
||||
if params.stringType != 0 && tag != tagPrintableString {
|
||||
return StructuralError{"explicit string type given to non-string member"}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case tagPrintableString:
|
||||
if params.stringType == 0 {
|
||||
// This is a string without an explicit string type. We'll use
|
||||
// a PrintableString if the character set in the string is
|
||||
// sufficiently limited, otherwise we'll use a UTF8String.
|
||||
for _, r := range v.String() {
|
||||
if r >= utf8.RuneSelf || !isPrintable(byte(r)) {
|
||||
if !utf8.ValidString(v.String()) {
|
||||
return errors.New("asn1: string not valid UTF-8")
|
||||
}
|
||||
tag = tagUTF8String
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tag = params.stringType
|
||||
}
|
||||
case tagUTCTime:
|
||||
if params.timeType == tagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) {
|
||||
tag = tagGeneralizedTime
|
||||
}
|
||||
}
|
||||
|
||||
if params.set {
|
||||
if tag != tagSequence {
|
||||
return StructuralError{"non sequence tagged as set"}
|
||||
}
|
||||
tag = tagSet
|
||||
}
|
||||
|
||||
tags, body := out.fork()
|
||||
|
||||
err = marshalBody(body, v, params)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
bodyLen := body.Len()
|
||||
|
||||
var explicitTag *forkableWriter
|
||||
if params.explicit {
|
||||
explicitTag, tags = tags.fork()
|
||||
}
|
||||
|
||||
if !params.explicit && params.tag != nil {
|
||||
// implicit tag.
|
||||
tag = *params.tag
|
||||
class = classContextSpecific
|
||||
}
|
||||
|
||||
err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if params.explicit {
|
||||
err = marshalTagAndLength(explicitTag, tagAndLength{
|
||||
class: classContextSpecific,
|
||||
tag: *params.tag,
|
||||
length: bodyLen + tags.Len(),
|
||||
isCompound: true,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal returns the ASN.1 encoding of val.
|
||||
//
|
||||
// In addition to the struct tags recognised by Unmarshal, the following can be
|
||||
// used:
|
||||
//
|
||||
// ia5: causes strings to be marshaled as ASN.1, IA5 strings
|
||||
// omitempty: causes empty slices to be skipped
|
||||
// printable: causes strings to be marshaled as ASN.1, PrintableString strings.
|
||||
// utf8: causes strings to be marshaled as ASN.1, UTF8 strings
|
||||
func Marshal(val interface{}) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
v := reflect.ValueOf(val)
|
||||
f := newForkableWriter()
|
||||
err := marshalField(f, v, fieldParameters{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = f.writeTo(&out)
|
||||
return out.Bytes(), nil
|
||||
}
|
|
@ -23,6 +23,7 @@ import (
|
|||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/nonce"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
oldx509 "github.com/letsencrypt/go/src/crypto/x509"
|
||||
jose "github.com/square/go-jose"
|
||||
)
|
||||
|
||||
|
@ -810,7 +811,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(ctx context.Context, logEvent *reques
|
|||
}
|
||||
|
||||
certificateRequest := core.CertificateRequest{Bytes: rawCSR.CSR}
|
||||
certificateRequest.CSR, err = x509.ParseCertificateRequest(rawCSR.CSR)
|
||||
certificateRequest.CSR, err = oldx509.ParseCertificateRequest(rawCSR.CSR)
|
||||
if err != nil {
|
||||
logEvent.AddError("unable to parse certificate request: %s", err)
|
||||
// TODO(jsha): Revert once #565 is closed by upgrading to Go 1.6, i.e. #1514
|
||||
|
|
Loading…
Reference in New Issue