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
|
package certificate
import (
"crypto/x509"
"encoding/pem"
"io/ioutil"
"github.com/pkg/errors"
"github.com/smallstep/cli/crypto/x509util"
"github.com/smallstep/cli/errs"
"github.com/smallstep/cli/flags"
"github.com/urfave/cli"
)
func verifyCommand() cli.Command {
return cli.Command{
Name: "verify",
Action: cli.ActionFunc(verifyAction),
Usage: `verify a certificate`,
UsageText: `**step certificate verify** <crt_file> [**--host**=<host>]
[**--roots**=<root-bundle>] [**--servername**=<servername>]`,
Description: `**step certificate verify** executes the certificate path
validation algorithm for x.509 certificates defined in RFC 5280. If the
certificate is valid this command will return '0'. If validation fails, or if
an error occurs, this command will produce a non-zero return value.
## POSITIONAL ARGUMENTS
<crt_file>
: The path to a certificate to validate.
## EXIT CODES
This command returns 0 on success and \>0 if any error occurs.
## EXAMPLES
Verify a certificate using your operating system's default root certificate bundle:
'''
$ step certificate verify ./certificate.crt
'''
Verify a remote certificate using your operating system's default root certificate bundle:
'''
$ step certificate verify https://smallstep.com
'''
Verify a certificate using a custom root certificate for path validation:
'''
$ step certificate verify ./certificate.crt --roots ./root-certificate.crt
'''
Verify a certificate using a custom list of root certificates for path validation:
'''
$ step certificate verify ./certificate.crt \
--roots "./root-certificate.crt,./root-certificate2.crt,/root-certificate3.crt"
'''
Verify a certificate using a custom directory of root certificates for path validation:
'''
$ step certificate verify ./certificate.crt --roots ./root-certificates/
'''
`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "host",
Usage: `Check whether the certificate is for the specified host.`,
},
cli.StringFlag{
Name: "roots",
Usage: `Root certificate(s) that will be used to verify the
authenticity of the remote server.
: <roots> is a case-sensitive string and may be one of:
**file**
: Relative or full path to a file. All certificates in the file will be used for path validation.
**list of files**
: Comma-separated list of relative or full file paths. Every PEM encoded certificate from each file will be used for path validation.
**directory**
: Relative or full path to a directory. Every PEM encoded certificate from each file in the directory will be used for path validation.`,
},
flags.ServerName,
},
}
}
func verifyAction(ctx *cli.Context) error {
if err := errs.NumberOfArguments(ctx, 1); err != nil {
return err
}
var (
crtFile = ctx.Args().Get(0)
host = ctx.String("host")
serverName = ctx.String("servername")
roots = ctx.String("roots")
intermediatePool = x509.NewCertPool()
rootPool *x509.CertPool
cert *x509.Certificate
)
if addr, isURL, err := trimURL(crtFile); err != nil {
return err
} else if isURL {
peerCertificates, err := getPeerCertificates(addr, serverName, roots, false)
if err != nil {
return err
}
cert = peerCertificates[0]
for _, pc := range peerCertificates {
intermediatePool.AddCert(pc)
}
} else {
crtBytes, err := ioutil.ReadFile(crtFile)
if err != nil {
return errs.FileError(err, crtFile)
}
var (
ipems []byte
block *pem.Block
)
// The first certificate PEM in the file is our leaf Certificate.
// Any certificate after the first is added to the list of Intermediate
// certificates used for path validation.
for len(crtBytes) > 0 {
block, crtBytes = pem.Decode(crtBytes)
if block == nil {
return errors.Errorf("%s contains an invalid PEM block", crtFile)
}
if block.Type != "CERTIFICATE" {
continue
}
if cert == nil {
cert, err = x509.ParseCertificate(block.Bytes)
if err != nil {
return errors.WithStack(err)
}
} else {
ipems = append(ipems, pem.EncodeToMemory(block)...)
}
}
if cert == nil {
return errors.Errorf("%s contains no PEM certificate blocks", crtFile)
}
if len(ipems) > 0 && !intermediatePool.AppendCertsFromPEM(ipems) {
return errors.Errorf("failure creating intermediate list from certificate '%s'", crtFile)
}
}
if roots != "" {
var err error
rootPool, err = x509util.ReadCertPool(roots)
if err != nil {
errors.Wrapf(err, "failure to load root certificate pool from input path '%s'", roots)
}
}
opts := x509.VerifyOptions{
DNSName: host,
Roots: rootPool,
Intermediates: intermediatePool,
}
if _, err := cert.Verify(opts); err != nil {
return errors.Wrapf(err, "failed to verify certificate")
}
return nil
}
|