/
generate_cross_signed_ca.go
137 lines (118 loc) · 4.56 KB
/
generate_cross_signed_ca.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2018 Jeremy Rand.
//
// Based on https://golang.org/src/crypto/x509/x509.go ,
// Copyright 2009 The Go Authors.
// This file is part of crosssignnameconstraint.
//
// crosssignnameconstraint is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// crosssignnameconstraint is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with crosssignnameconstraint. If not, see
// <https://www.gnu.org/licenses/>.
package crosssignnameconstraint
import (
"crypto"
"crypto/rand"
"encoding/asn1"
"errors"
"fmt"
"math/big"
)
// These are modified from the x509 package; they store any field that isn't
// replaced by cross-signing as an asn1.RawValue.
type certificate struct {
Raw asn1.RawContent
TBSCertificate tbsCertificate
SignatureAlgorithm asn1.RawValue
SignatureValue asn1.BitString
}
type tbsCertificate struct {
Raw asn1.RawContent
Version asn1.RawValue `asn1:"optional,explicit,tag:0"`
SerialNumber *big.Int // Replaced by cross-signing
SignatureAlgorithm asn1.RawValue // Replaced by cross-signing
Issuer asn1.RawValue // Replaced by cross-signing
Validity asn1.RawValue
Subject asn1.RawValue
PublicKey asn1.RawValue
UniqueId asn1.RawValue `asn1:"optional,tag:1"` // nolint: golint
SubjectUniqueId asn1.RawValue `asn1:"optional,tag:2"` // nolint: golint
Extensions []asn1.RawValue `asn1:"optional,explicit,tag:3"`
}
// Returns cert, error
// nolint: lll
func generateCrossSignedCA(originalDERBytes []byte, intermediateDERBytes []byte, intermediatePrivateKey interface{}) ([]byte, error) {
// Based on x509.ParseCertificate
var originalCert certificate
restOriginal, err := asn1.Unmarshal(originalDERBytes, &originalCert)
if err != nil {
return nil, fmt.Errorf("Couldn't unmarshal original certificate: %s", err)
}
if len(restOriginal) > 0 {
return nil, fmt.Errorf("Trailing data in original certificate: %s", asn1.SyntaxError{Msg: "trailing data"})
}
// Based on x509.ParseCertificate
var intermediateCertASN1 certificate
restIntermediate, err := asn1.Unmarshal(intermediateDERBytes, &intermediateCertASN1)
if err != nil {
return nil, fmt.Errorf("Couldn't unmarshal intermediate certificate: %s", err)
}
if len(restIntermediate) > 0 {
return nil, fmt.Errorf("Trailing data in intermediate certificate: %s", asn1.SyntaxError{Msg: "trailing data"})
}
// Based on CreateCertificate
key, ok := intermediatePrivateKey.(crypto.Signer)
if !ok {
return nil, errors.New("x509: certificate private key does not implement crypto.Signer")
}
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, fmt.Errorf("failed to generate serial number: %s", err)
}
hashFunc := crypto.SHA256
c := tbsCertificate{
Version: originalCert.TBSCertificate.Version,
SerialNumber: serialNumber,
SignatureAlgorithm: intermediateCertASN1.TBSCertificate.SignatureAlgorithm,
Issuer: intermediateCertASN1.TBSCertificate.Subject,
Validity: originalCert.TBSCertificate.Validity,
Subject: originalCert.TBSCertificate.Subject,
PublicKey: originalCert.TBSCertificate.PublicKey,
Extensions: originalCert.TBSCertificate.Extensions,
// TODO: Look into UniqueId and SubjectUniqueId.
}
tbsCertContents, err := asn1.Marshal(c)
if err != nil {
return nil, fmt.Errorf("failed to marshal tbsCertificate: %s", err)
}
c.Raw = tbsCertContents
h := hashFunc.New()
h.Write(tbsCertContents) // nolint: errcheck, gas, gosec
digest := h.Sum(nil)
var signerOpts crypto.SignerOpts // nolint: megacheck
signerOpts = hashFunc
var signature []byte
signature, err = key.Sign(rand.Reader, digest, signerOpts)
if err != nil {
return nil, fmt.Errorf("failed to sign cert digest: %s", err)
}
outputDER, err := asn1.Marshal(certificate{
nil,
c,
intermediateCertASN1.SignatureAlgorithm,
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
if err != nil {
return nil, fmt.Errorf("failed to marshal signed certificate: %s", err)
}
return outputDER, nil
}