11package certificate
22
33import (
4- "bytes"
54 "crypto/x509"
65 "encoding/json"
76 "encoding/pem"
@@ -12,10 +11,10 @@ import (
1211 "github.com/pkg/errors"
1312 "github.com/smallstep/certinfo"
1413 "github.com/smallstep/cli/flags"
15- "github.com/smallstep/cli/utils"
1614 zx509 "github.com/smallstep/zcrypto/x509"
1715 "github.com/urfave/cli"
1816 "go.step.sm/cli-utils/errs"
17+ "go.step.sm/crypto/pemutil"
1918)
2019
2120func inspectCommand () cli.Command {
@@ -26,7 +25,7 @@ func inspectCommand() cli.Command {
2625 UsageText : `**step certificate inspect** <crt-file>
2726[**--bundle**] [**--short**] [**--format**=<format>] [**--roots**=<root-bundle>]
2827[**--servername**=<servername>]` ,
29- Description : `**step certificate inspect** prints the details of the
28+ Description : `**step certificate inspect** prints the details of the
3029certificate or CSR in a human- or machine-readable format. Beware: Local certificates
3130are never verified. Always verify a certificate (using **step certificate verify**)
3231before relying on the output of this command.
@@ -206,9 +205,6 @@ func inspectAction(ctx *cli.Context) error {
206205 return errs .IncompatibleFlagWithFlag (ctx , "short" , "format json" )
207206 }
208207
209- var block * pem.Block
210- var blocks []* pem.Block
211-
212208 switch addr , isURL , err := trimURL (crtFile ); {
213209 case err != nil :
214210 return err
@@ -217,67 +213,35 @@ func inspectAction(ctx *cli.Context) error {
217213 if err != nil {
218214 return err
219215 }
220- for _ , crt := range peerCertificates {
221- blocks = append (blocks , & pem.Block {
222- Type : "CERTIFICATE" ,
223- Bytes : crt .Raw ,
224- })
225- }
216+ return inspectCertificates (ctx , peerCertificates , os .Stdout )
226217 default : // is not URL
227- crtBytes , err := utils .ReadFile (crtFile )
228- if err != nil {
229- return errs .FileError (err , crtFile )
230- }
231- if bytes .Contains (crtBytes , []byte ("-----BEGIN " )) {
232- for len (crtBytes ) > 0 {
233- block , crtBytes = pem .Decode (crtBytes )
234- if block == nil {
235- break
236- }
237- if bundle && block .Type != "CERTIFICATE" {
238- return errors .Errorf ("certificate bundle %q contains an unexpected PEM block of type %q\n \n expected type: CERTIFICATE" ,
239- crtFile , block .Type )
240- }
241- blocks = append (blocks , block )
218+ var pemError * pemutil.InvalidPEMError
219+ crts , err := pemutil .ReadCertificateBundle (crtFile )
220+ switch {
221+ case errors .As (err , & pemError ) && pemError .Type == pemutil .PEMTypeCertificate :
222+ csr , err := pemutil .ReadCertificateRequest (crtFile )
223+ if err != nil {
224+ return errors .Errorf ("file %s does not contain any valid CERTIFICATE or CERTIFICATE REQUEST blocks" , crtFile )
242225 }
243- } else {
244- if block = derToPemBlock (crtBytes ); block == nil {
245- return errors .Errorf ("%q contains an invalid PEM block" , crtFile )
226+ return inspectCertificateRequest (ctx , csr , os .Stdout )
227+ case err != nil :
228+ return err
229+ default :
230+ if bundle {
231+ return inspectCertificates (ctx , crts , os .Stdout )
246232 }
247- blocks = append (blocks , block )
248- }
249-
250- // prevent index out of range errors
251- if len (blocks ) == 0 {
252- return fmt .Errorf ("%q does not contain valid PEM blocks" , crtFile )
233+ return inspectCertificates (ctx , crts [:1 ], os .Stdout )
253234 }
254235 }
255-
256- // Keep the first one if !bundle
257- if ! bundle {
258- blocks = []* pem.Block {blocks [0 ]}
259- }
260-
261- switch blocks [0 ].Type {
262- case "CERTIFICATE" :
263- return inspectCertificates (ctx , blocks , os .Stdout )
264- case "CERTIFICATE REQUEST" , "NEW CERTIFICATE REQUEST" : // only one is supported
265- return inspectCertificateRequest (ctx , blocks [0 ])
266- default :
267- return errors .Errorf ("Invalid PEM type in %q. Expected [CERTIFICATE|CERTIFICATE REQUEST] but got %q)" , crtFile , block .Type )
268- }
269236}
270237
271- func inspectCertificates (ctx * cli.Context , blocks []* pem.Block , w io.Writer ) error {
238+ func inspectCertificates (ctx * cli.Context , crts []* x509.Certificate , w io.Writer ) error {
239+ var err error
272240 format , short := ctx .String ("format" ), ctx .Bool ("short" )
273241 switch format {
274242 case "text" :
275243 var text string
276- for _ , block := range blocks {
277- crt , err := x509 .ParseCertificate (block .Bytes )
278- if err != nil {
279- return errors .WithStack (err )
280- }
244+ for _ , crt := range crts {
281245 if short {
282246 if text , err = certinfo .CertificateShortText (crt ); err != nil {
283247 return err
@@ -292,16 +256,16 @@ func inspectCertificates(ctx *cli.Context, blocks []*pem.Block, w io.Writer) err
292256 return nil
293257 case "json" :
294258 var v interface {}
295- if len (blocks ) == 1 {
296- zcrt , err := zx509 .ParseCertificate (blocks [0 ].Bytes )
259+ if len (crts ) == 1 {
260+ zcrt , err := zx509 .ParseCertificate (crts [0 ].Raw )
297261 if err != nil {
298262 return errors .WithStack (err )
299263 }
300264 v = struct { * zx509.Certificate }{zcrt }
301265 } else {
302266 var zcrts []* zx509.Certificate
303- for _ , block := range blocks {
304- zcrt , err := zx509 .ParseCertificate (block . Bytes )
267+ for _ , crt := range crts {
268+ zcrt , err := zx509 .ParseCertificate (crt . Raw )
305269 if err != nil {
306270 return errors .WithStack (err )
307271 }
@@ -317,8 +281,8 @@ func inspectCertificates(ctx *cli.Context, blocks []*pem.Block, w io.Writer) err
317281 }
318282 return nil
319283 case "pem" :
320- for _ , block := range blocks {
321- err := pem .Encode (w , block )
284+ for _ , crt := range crts {
285+ err := pem .Encode (w , & pem. Block { Type : "CERTIFICATE" , Bytes : crt . Raw } )
322286 if err != nil {
323287 return errors .WithStack (err )
324288 }
@@ -329,15 +293,12 @@ func inspectCertificates(ctx *cli.Context, blocks []*pem.Block, w io.Writer) err
329293 }
330294}
331295
332- func inspectCertificateRequest (ctx * cli.Context , block * pem.Block ) error {
296+ func inspectCertificateRequest (ctx * cli.Context , csr * x509.CertificateRequest , w io.Writer ) error {
297+ var err error
333298 format , short := ctx .String ("format" ), ctx .Bool ("short" )
334299 switch format {
335300 case "text" :
336301 var text string
337- csr , err := x509 .ParseCertificateRequest (block .Bytes )
338- if err != nil {
339- return errors .WithStack (err )
340- }
341302 if short {
342303 text , err = certinfo .CertificateRequestShortText (csr )
343304 if err != nil {
@@ -349,35 +310,26 @@ func inspectCertificateRequest(ctx *cli.Context, block *pem.Block) error {
349310 return err
350311 }
351312 }
352- fmt .Print ( text )
313+ fmt .Fprint ( w , text )
353314 return nil
354315 case "json" :
355- zcsr , err := zx509 .ParseCertificateRequest (block . Bytes )
316+ zcsr , err := zx509 .ParseCertificateRequest (csr . Raw )
356317 if err != nil {
357318 return errors .WithStack (err )
358319 }
359- b , err := json .MarshalIndent (struct {
360- * zx509.CertificateRequest
361- }{zcsr }, "" , " " )
320+ enc := json .NewEncoder (w )
321+ enc .SetIndent ("" , " " )
322+ if err := enc .Encode (zcsr ); err != nil {
323+ return errors .WithStack (err )
324+ }
325+ return nil
326+ case "pem" :
327+ err := pem .Encode (w , & pem.Block {Type : "CERTIFICATE REQUEST" , Bytes : csr .Raw })
362328 if err != nil {
363329 return errors .WithStack (err )
364330 }
365- os .Stdout .Write (b )
366331 return nil
367332 default :
368333 return errs .InvalidFlagValue (ctx , "format" , format , "text, json" )
369334 }
370335}
371-
372- // derToPemBlock attempts to parse the ASN.1 data as a certificate or a
373- // certificate request, returning a pem.Block of the one that succeeds. Returns
374- // nil if it cannot parse the data.
375- func derToPemBlock (b []byte ) * pem.Block {
376- if _ , err := x509 .ParseCertificate (b ); err == nil {
377- return & pem.Block {Type : "CERTIFICATE" , Bytes : b }
378- }
379- if _ , err := x509 .ParseCertificateRequest (b ); err == nil {
380- return & pem.Block {Type : "CERTIFICATE REQUEST" , Bytes : b }
381- }
382- return nil
383- }
0 commit comments