1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package x509tools
18
19 import (
20 "crypto/ecdsa"
21 "crypto/rsa"
22 "crypto/x509"
23 "encoding/asn1"
24 "fmt"
25 "io"
26 "time"
27 )
28
29 var keyUsageNames = map[x509.KeyUsage]string{
30 x509.KeyUsageDigitalSignature: "digitalSignature",
31 x509.KeyUsageContentCommitment: "nonRepudiation",
32 x509.KeyUsageKeyEncipherment: "keyEncipherment",
33 x509.KeyUsageDataEncipherment: "dataEncipherment",
34 x509.KeyUsageKeyAgreement: "keyAgreement",
35 x509.KeyUsageCertSign: "keyCertSign",
36 x509.KeyUsageCRLSign: "cRLSign",
37 x509.KeyUsageEncipherOnly: "encipherOnly",
38 x509.KeyUsageDecipherOnly: "decipherOnly",
39 }
40
41 var extKeyUsageNames = map[x509.ExtKeyUsage]string{
42 x509.ExtKeyUsageAny: "any",
43 x509.ExtKeyUsageServerAuth: "serverAuth",
44 x509.ExtKeyUsageClientAuth: "clientAuth",
45 x509.ExtKeyUsageCodeSigning: "codeSigning",
46 x509.ExtKeyUsageEmailProtection: "emailProtection",
47 x509.ExtKeyUsageIPSECEndSystem: "ipsecEndSystem",
48 x509.ExtKeyUsageIPSECTunnel: "ipsecTunnel",
49 x509.ExtKeyUsageIPSECUser: "ipsecUser",
50 x509.ExtKeyUsageTimeStamping: "timeStamping",
51 x509.ExtKeyUsageOCSPSigning: "OCSPSigning",
52
53 x509.ExtKeyUsageMicrosoftServerGatedCrypto: "msServerGatedCrypto",
54 x509.ExtKeyUsageNetscapeServerGatedCrypto: "nsServerGatedCrypto",
55 x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "msCodeCom",
56 x509.ExtKeyUsageMicrosoftKernelCodeSigning: "msKernCode",
57 }
58
59 var knownExtensions = []asn1.ObjectIdentifier{
60 asn1.ObjectIdentifier{2, 5, 29, 14},
61 asn1.ObjectIdentifier{2, 5, 29, 15},
62 asn1.ObjectIdentifier{2, 5, 29, 37},
63 asn1.ObjectIdentifier{2, 5, 29, 35},
64 asn1.ObjectIdentifier{2, 5, 29, 19},
65 asn1.ObjectIdentifier{2, 5, 29, 17},
66 asn1.ObjectIdentifier{2, 5, 29, 32},
67 asn1.ObjectIdentifier{2, 5, 29, 30},
68 asn1.ObjectIdentifier{2, 5, 29, 31},
69 asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 1},
70 asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1},
71 asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2},
72 }
73
74
75 func FprintCertificate(w io.Writer, cert *x509.Certificate) {
76 fmt.Fprintln(w, "Version:", cert.Version)
77 if cert.SerialNumber.BitLen() > 63 {
78 fmt.Fprintf(w, "Serial: 0x%x\n", cert.SerialNumber)
79 } else {
80 fmt.Fprintf(w, "Serial: %d (0x%x)\n", cert.SerialNumber, cert.SerialNumber)
81 }
82 fmt.Fprintln(w, "Subject:", FormatSubject(cert))
83 fmt.Fprintln(w, "Issuer: ", FormatIssuer(cert))
84 fmt.Fprintln(w, "Valid: ", cert.NotBefore)
85 fmt.Fprintln(w, "Expires:", cert.NotAfter)
86 fmt.Fprintln(w, "Period: ", subDate(cert.NotAfter, cert.NotBefore))
87 switch k := cert.PublicKey.(type) {
88 case *rsa.PublicKey:
89 n := fmt.Sprintf("%x", k.N)
90 fmt.Fprintf(w, "Pub key: RSA bits=%d e=%d n=%s...%s\n", k.N.BitLen(), k.E, n[:8], n[len(n)-8:])
91 case *ecdsa.PublicKey:
92 p := k.Params()
93 x := fmt.Sprintf("%x", k.X)
94 y := fmt.Sprintf("%x", k.Y)
95 fmt.Fprintf(w, "Public key: ECDSA bits=%d name=%s x=%s... y=...%s\n", p.BitSize, p.Name, x[:8], y[len(y)-8:])
96 default:
97 fmt.Fprintf(w, "Public key: %T\n", k)
98 }
99 fmt.Fprintln(w, "Sig alg:", cert.SignatureAlgorithm)
100 fmt.Fprintln(w, "Extensions:")
101
102 printSAN(w, cert)
103
104 if cert.BasicConstraintsValid {
105 cons := fmt.Sprintf("isCA=%t", cert.IsCA)
106 if cert.MaxPathLenZero {
107 cons += " MaxPathLen=0"
108 } else if cert.MaxPathLen > 0 {
109 cons += fmt.Sprintf(" MaxPathLen=%d", cert.MaxPathLen)
110 }
111 fmt.Fprintln(w, " Basic constraints: "+cons)
112 }
113
114 printNameConstraints(w, cert)
115
116 usage := ""
117 for n, name := range keyUsageNames {
118 if cert.KeyUsage&n != 0 {
119 usage += ", " + name
120 }
121 }
122 if usage != "" {
123 fmt.Fprintln(w, " Key Usage:", usage[2:])
124 }
125
126 usage = ""
127 for _, u := range cert.ExtKeyUsage {
128 name := extKeyUsageNames[u]
129 if name == "" {
130 name = fmt.Sprintf("%d", u)
131 }
132 usage += ", " + name
133 }
134 for _, u := range cert.UnknownExtKeyUsage {
135 usage += ", " + u.String()
136 }
137 if usage != "" {
138 fmt.Fprintln(w, " Extended key usage:", usage[2:])
139 }
140
141 if len(cert.SubjectKeyId) != 0 {
142 fmt.Fprintf(w, " Subject key ID: %x\n", cert.SubjectKeyId)
143 }
144 if len(cert.AuthorityKeyId) != 0 {
145 fmt.Fprintf(w, " Authority key ID: %x\n", cert.AuthorityKeyId)
146 }
147
148 if len(cert.OCSPServer) != 0 {
149 fmt.Fprintln(w, " OCSP Servers:")
150 for _, s := range cert.OCSPServer {
151 fmt.Fprintln(w, " ", s)
152 }
153 }
154 if len(cert.IssuingCertificateURL) != 0 {
155 fmt.Fprintln(w, " Issuing authority URLs:")
156 for _, s := range cert.IssuingCertificateURL {
157 fmt.Fprintln(w, " ", s)
158 }
159 }
160
161 if len(cert.CRLDistributionPoints) != 0 {
162 fmt.Fprintln(w, " CRL Distribution Points:")
163 for _, s := range cert.CRLDistributionPoints {
164 fmt.Fprintln(w, " ", s)
165 }
166 }
167
168 if len(cert.PolicyIdentifiers) != 0 {
169 fmt.Fprintln(w, " Policy Identifiers:")
170 for _, s := range cert.PolicyIdentifiers {
171 fmt.Fprintln(w, " ", s.String())
172 }
173 }
174
175 for _, ex := range cert.Extensions {
176 if knownExtension(ex.Id) {
177 continue
178 }
179 critical := ""
180 if ex.Critical {
181 critical = " (critical)"
182 }
183 fmt.Fprintf(w, " Extension %s%s: %x\n", ex.Id, critical, ex.Value)
184 }
185 }
186
187 func knownExtension(id asn1.ObjectIdentifier) bool {
188 for _, known := range knownExtensions {
189 if known.Equal(id) {
190 return true
191 }
192 }
193 return false
194 }
195
196 const (
197 durYear = 8766 * time.Hour
198 yearSlop = 2 * 24 * time.Hour
199 )
200
201
202 func subDate(end, start time.Time) string {
203 approx := "~"
204 dur := end.Sub(start)
205 switch {
206 case dur >= durYear-yearSlop:
207 years := int((dur + yearSlop) / durYear)
208 if start.AddDate(years, 0, 0).Equal(end) {
209 approx = ""
210 }
211 if years > 1 {
212 return fmt.Sprintf("%s%d years", approx, years)
213 }
214 return approx + "1 year"
215 case dur >= 24*time.Hour:
216 days := int(dur / (24 * time.Hour))
217 if start.AddDate(0, 0, days).Equal(end) {
218 approx = ""
219 }
220 if days > 1 {
221 return fmt.Sprintf("%s%d days", approx, days)
222 }
223 return approx + "1 day"
224 default:
225 return dur.String()
226 }
227 }
228
229 func printSAN(w io.Writer, cert *x509.Certificate) {
230 if len(cert.DNSNames) != 0 || len(cert.EmailAddresses) != 0 || len(cert.IPAddresses) != 0 || len(cert.URIs) != 0 {
231 fmt.Fprintln(w, " Subject alternate names:")
232 for _, s := range cert.DNSNames {
233 fmt.Fprintln(w, " dns:"+s)
234 }
235 for _, s := range cert.EmailAddresses {
236 fmt.Fprintln(w, " email:"+s)
237 }
238 for _, s := range cert.IPAddresses {
239 fmt.Fprintln(w, " ip:"+s.String())
240 }
241 for _, s := range cert.URIs {
242 fmt.Fprintln(w, " uri:"+s.String())
243 }
244 }
245 }
246
247 func printNameConstraints(w io.Writer, cert *x509.Certificate) {
248 if len(cert.PermittedDNSDomains) != 0 || len(cert.ExcludedDNSDomains) != 0 || len(cert.PermittedIPRanges) != 0 || len(cert.ExcludedIPRanges) != 0 || len(cert.PermittedEmailAddresses) != 0 || len(cert.ExcludedEmailAddresses) != 0 || len(cert.PermittedURIDomains) != 0 || len(cert.ExcludedURIDomains) != 0 {
249 fmt.Fprintln(w, " Name constraints:")
250 for _, s := range cert.PermittedDNSDomains {
251 fmt.Fprintln(w, " Permitted DNS domain:", s)
252 }
253 for _, s := range cert.ExcludedDNSDomains {
254 fmt.Fprintln(w, " Excluded DNS domain:", s)
255 }
256 for _, s := range cert.PermittedIPRanges {
257 fmt.Fprintln(w, " Permitted IP range:", s)
258 }
259 for _, s := range cert.ExcludedIPRanges {
260 fmt.Fprintln(w, " Excluded IP range:", s)
261 }
262 for _, s := range cert.PermittedEmailAddresses {
263 fmt.Fprintln(w, " Permitted Email Addresses:", s)
264 }
265 for _, s := range cert.ExcludedEmailAddresses {
266 fmt.Fprintln(w, " Excluded Email Addresses:", s)
267 }
268 for _, s := range cert.PermittedURIDomains {
269 fmt.Fprintln(w, " Permitted URI domain:", s)
270 }
271 for _, s := range cert.ExcludedURIDomains {
272 fmt.Fprintln(w, " Excluded URI domain:", s)
273 }
274 }
275 }
276
View as plain text