forked from grantae/certinfo
-
Notifications
You must be signed in to change notification settings - Fork 9
/
certformat.go
204 lines (188 loc) · 4.97 KB
/
certformat.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package certinfo
import (
"bytes"
"crypto/dsa"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"fmt"
"net"
"net/url"
"strconv"
"time"
)
// formatBuffer is a helper to write using sprintf.
type formatBuffer struct {
bytes.Buffer
}
// Writef writes a string formated using fmt.Sprintf.
func (b *formatBuffer) Writef(format string, args ...interface{}) (int, error) {
return b.Buffer.WriteString(fmt.Sprintf(format, args...))
}
type certificateShort struct {
Type string
PublicKeyAlgorithm string
SerialNumber string
Subject string
Issuer string
SANs []string
Provisioner *provisioner
NotBefore time.Time
NotAfter time.Time
}
type provisioner struct {
ID string
Name string
}
func newCertificateShort(cert *x509.Certificate) *certificateShort {
var typ string
if cert.IsCA {
if cert.CheckSignatureFrom(cert) == nil {
typ = "Root CA"
} else {
typ = "Intermediate CA"
}
} else {
typ = "TLS"
}
return &certificateShort{
Type: typ,
PublicKeyAlgorithm: getPublicKeyAlgorithm(cert.PublicKeyAlgorithm, cert.PublicKey),
SerialNumber: abbreviated(cert.SerialNumber.String()),
Subject: cert.Subject.CommonName,
Issuer: cert.Issuer.CommonName,
SANs: getSANs(cert.Subject.CommonName, cert.DNSNames, cert.IPAddresses, cert.EmailAddresses, cert.URIs),
Provisioner: getProvisioner(cert),
NotBefore: cert.NotBefore,
NotAfter: cert.NotAfter,
}
}
// String returns the certificateShort formated as a string.
func (c *certificateShort) String() string {
var buf formatBuffer
buf.Writef("X.509v3 %s Certificate (%s) [Serial: %s]\n", c.Type, c.PublicKeyAlgorithm, c.SerialNumber)
sans := c.SANs
if c.Subject != "" {
sans = append([]string{c.Subject}, sans...)
}
if len(sans) == 0 {
buf.Writef(" Subject: \n")
} else {
for i, s := range sans {
if i == 0 {
buf.Writef(" Subject: %s\n", s)
} else {
buf.Writef(" %s\n", s)
}
}
}
buf.Writef(" Issuer: %s\n", c.Issuer)
if c.Provisioner != nil {
if c.Provisioner.ID == "" {
buf.Writef(" Provisioner: %s\n", c.Provisioner.Name)
} else {
buf.Writef(" Provisioner: %s [ID: %s]\n", c.Provisioner.Name, c.Provisioner.ID)
}
}
buf.Writef(" Valid from: %s\n", c.NotBefore.Format(time.RFC3339))
buf.Writef(" to: %s\n", c.NotAfter.Format(time.RFC3339))
return buf.String()
}
type certificateRequestShort struct {
PublicKeyAlgorithm string
Subject string
SANs []string
}
func newCertificateRequestShort(cr *x509.CertificateRequest) *certificateRequestShort {
return &certificateRequestShort{
PublicKeyAlgorithm: getPublicKeyAlgorithm(cr.PublicKeyAlgorithm, cr.PublicKey),
Subject: cr.Subject.CommonName,
SANs: getSANs(cr.Subject.CommonName, cr.DNSNames, cr.IPAddresses, cr.EmailAddresses, cr.URIs),
}
}
// String returns the certificateShort formated as a string.
func (c *certificateRequestShort) String() string {
var buf formatBuffer
buf.Writef("X.509v3 Certificate Signing Request (%s)\n", c.PublicKeyAlgorithm)
sans := c.SANs
if c.Subject != "" {
sans = append([]string{c.Subject}, sans...)
}
if len(sans) == 0 {
buf.Writef(" Subject: \n")
} else {
for i, s := range sans {
if i == 0 {
buf.Writef(" Subject: %s\n", s)
} else {
buf.Writef(" %s\n", s)
}
}
}
return buf.String()
}
func getSANs(commonName string, dnsNames []string, ipAddresses []net.IP, emailAddresses []string, uris []*url.URL) []string {
var sans []string
for _, s := range dnsNames {
if s != commonName {
sans = append(sans, s)
}
}
for _, ip := range ipAddresses {
if s := ip.String(); s != commonName {
sans = append(sans, s)
}
}
for _, s := range emailAddresses {
if s != commonName {
sans = append(sans, s)
}
}
for _, uri := range uris {
if s := uri.String(); s != commonName {
sans = append(sans, s)
}
}
return sans
}
func getProvisioner(cert *x509.Certificate) *provisioner {
for _, ext := range cert.Extensions {
if ext.Id.Equal(oidStepProvisioner) {
val := &stepProvisioner{}
rest, err := asn1.Unmarshal(ext.Value, val)
if err != nil || len(rest) > 0 {
return nil
}
return &provisioner{
ID: abbreviated(string(val.CredentialID)),
Name: string(val.Name),
}
}
}
return nil
}
func getPublicKeyAlgorithm(algorithm x509.PublicKeyAlgorithm, key interface{}) string {
var params string
switch pk := key.(type) {
case *ecdsa.PublicKey:
params = pk.Curve.Params().Name
case *rsa.PublicKey:
params = strconv.Itoa(pk.Size() * 8)
case *dsa.PublicKey:
params = strconv.Itoa(pk.Q.BitLen())
case ed25519.PublicKey:
params = strconv.Itoa(len(pk) * 8)
default:
params = "unknown"
}
return fmt.Sprintf("%s %s", algorithm, params)
}
func abbreviated(s string) string {
l := len(s)
if l <= 8 {
return s
}
return s[:4] + "..." + s[l-4:]
}