mirror of
https://github.com/square/certigo.git
synced 2024-04-21 12:32:40 +00:00
Merge pull request #138 from square/cs/better-errors
Better error handling in certigo dump command
This commit is contained in:
+67
-31
@@ -18,12 +18,14 @@ package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -65,21 +67,42 @@ var badSignatureAlgorithms = [...]x509.SignatureAlgorithm{
|
||||
x509.ECDSAWithSHA1,
|
||||
}
|
||||
|
||||
func errorFromErrors(errs []error) error {
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(errs) == 1 {
|
||||
return errs[0]
|
||||
}
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.WriteString("encountered multiple errors:\n")
|
||||
for _, err := range errs {
|
||||
buffer.WriteString("* ")
|
||||
buffer.WriteString(strings.TrimSuffix(err.Error(), "\n"))
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
return errors.New(buffer.String())
|
||||
}
|
||||
|
||||
// ReadAsPEMFromFiles will read PEM blocks from the given set of inputs. Input
|
||||
// data may be in plain-text PEM files, DER-encoded certificates or PKCS7
|
||||
// envelopes, or PKCS12/JCEKS keystores. All inputs will be converted to PEM
|
||||
// blocks and passed to the callback.
|
||||
func ReadAsPEMFromFiles(files []*os.File, format string, password func(string) string, callback func(*pem.Block)) error {
|
||||
errs := []error{}
|
||||
for _, file := range files {
|
||||
reader := bufio.NewReaderSize(file, 4)
|
||||
format, err := formatForFile(reader, file.Name(), format)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to guess file type (for file %s)\n", file.Name())
|
||||
return fmt.Errorf("unable to guess file type for file %s, try adding --format flag", file.Name())
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, file.Name(), format, password, callback)
|
||||
err = readCertsFromStream(reader, file.Name(), format, password, callback)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return errorFromErrors(errs)
|
||||
}
|
||||
|
||||
// ReadAsPEM will read PEM blocks from the given set of inputs. Input data may
|
||||
@@ -87,6 +110,7 @@ func ReadAsPEMFromFiles(files []*os.File, format string, password func(string) s
|
||||
// PKCS12/JCEKS keystores. All inputs will be converted to PEM blocks and
|
||||
// passed to the callback.
|
||||
func ReadAsPEM(readers []io.Reader, format string, password func(string) string, callback func(*pem.Block)) error {
|
||||
errs := []error{}
|
||||
for _, r := range readers {
|
||||
reader := bufio.NewReaderSize(r, 4)
|
||||
format, err := formatForFile(reader, "", format)
|
||||
@@ -94,34 +118,41 @@ func ReadAsPEM(readers []io.Reader, format string, password func(string) string,
|
||||
return fmt.Errorf("unable to guess format for input stream")
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, "", format, password, callback)
|
||||
err = readCertsFromStream(reader, "", format, password, callback)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return errorFromErrors(errs)
|
||||
}
|
||||
|
||||
// ReadAsX509FromFiles will read X.509 certificates from the given set of
|
||||
// inputs. Input data may be in plain-text PEM files, DER-encoded certificates
|
||||
// or PKCS7 envelopes, or PKCS12/JCEKS keystores. All inputs will be converted
|
||||
// to X.509 certificates (private keys are skipped) and passed to the callback.
|
||||
func ReadAsX509FromFiles(files []*os.File, format string, password func(string) string, callback func(*x509.Certificate)) error {
|
||||
func ReadAsX509FromFiles(files []*os.File, format string, password func(string) string, callback func(*x509.Certificate, error)) error {
|
||||
errs := []error{}
|
||||
for _, file := range files {
|
||||
reader := bufio.NewReaderSize(file, 4)
|
||||
format, err := formatForFile(reader, file.Name(), format)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to guess file type (for file %s)", file.Name())
|
||||
return fmt.Errorf("unable to guess file type for file %s, try adding --format flag", file.Name())
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, file.Name(), format, password, pemToX509(callback))
|
||||
err = readCertsFromStream(reader, file.Name(), format, password, pemToX509(callback))
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errorFromErrors(errs)
|
||||
}
|
||||
|
||||
// ReadAsX509 will read X.509 certificates from the given set of inputs. Input
|
||||
// data may be in plain-text PEM files, DER-encoded certificates or PKCS7
|
||||
// envelopes, or PKCS12/JCEKS keystores. All inputs will be converted to X.509
|
||||
// certificates (private keys are skipped) and passed to the callback.
|
||||
func ReadAsX509(readers []io.Reader, format string, password func(string) string, callback func(*x509.Certificate)) error {
|
||||
func ReadAsX509(readers []io.Reader, format string, password func(string) string, callback func(*x509.Certificate, error)) error {
|
||||
errs := []error{}
|
||||
for _, r := range readers {
|
||||
reader := bufio.NewReaderSize(r, 4)
|
||||
format, err := formatForFile(reader, "", format)
|
||||
@@ -129,26 +160,28 @@ func ReadAsX509(readers []io.Reader, format string, password func(string) string
|
||||
return fmt.Errorf("unable to guess format for input stream")
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, "", format, password, pemToX509(callback))
|
||||
err = readCertsFromStream(reader, "", format, password, pemToX509(callback))
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return errorFromErrors(errs)
|
||||
}
|
||||
|
||||
func pemToX509(callback func(*x509.Certificate)) func(*pem.Block) {
|
||||
func pemToX509(callback func(*x509.Certificate, error)) func(*pem.Block) {
|
||||
return func(block *pem.Block) {
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err == nil {
|
||||
callback(cert)
|
||||
}
|
||||
callback(cert, err)
|
||||
case "PKCS7":
|
||||
certs, err := pkcs7.ExtractCertificates(block.Bytes)
|
||||
if err == nil {
|
||||
for _, cert := range certs {
|
||||
callback(cert)
|
||||
callback(cert, nil)
|
||||
}
|
||||
} else {
|
||||
callback(nil, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +194,7 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw
|
||||
headers[fileHeader] = filename
|
||||
}
|
||||
|
||||
switch format {
|
||||
switch strings.TrimSpace(format) {
|
||||
case "PEM":
|
||||
scanner := pemScanner(reader)
|
||||
for scanner.Scan() {
|
||||
@@ -169,30 +202,31 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw
|
||||
block.Headers = mergeHeaders(block.Headers, headers)
|
||||
callback(block)
|
||||
}
|
||||
return nil
|
||||
case "DER":
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading input: %s\n", err)
|
||||
return fmt.Errorf("unable to read input: %s\n", err)
|
||||
}
|
||||
x509Certs, err := x509.ParseCertificates(data)
|
||||
if err == nil {
|
||||
x509Certs, err0 := x509.ParseCertificates(data)
|
||||
if err0 == nil {
|
||||
for _, cert := range x509Certs {
|
||||
callback(EncodeX509ToPEM(cert, headers))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
p7bBlocks, err := pkcs7.ParseSignedData(data)
|
||||
if err == nil {
|
||||
p7bBlocks, err1 := pkcs7.ParseSignedData(data)
|
||||
if err1 == nil {
|
||||
for _, block := range p7bBlocks {
|
||||
callback(pkcs7ToPem(block, headers))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error parsing certificates from DER data\n")
|
||||
return fmt.Errorf("unable to parse certificates from DER data\n* X.509 parser gave: %s\n* PKCS7 parser gave: %s\n", err0, err1)
|
||||
case "PKCS12":
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading input: %s\n", err)
|
||||
return fmt.Errorf("unable to read input: %s\n", err)
|
||||
}
|
||||
blocks, err := pkcs12.ToPEM(data, password(""))
|
||||
if err != nil || len(blocks) == 0 {
|
||||
@@ -202,10 +236,11 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw
|
||||
block.Headers = mergeHeaders(block.Headers, headers)
|
||||
callback(block)
|
||||
}
|
||||
return nil
|
||||
case "JCEKS":
|
||||
keyStore, err := jceks.LoadFromReader(reader, []byte(password("")))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing keystore: %s\n", err)
|
||||
return fmt.Errorf("unable to parse keystore: %s\n", err)
|
||||
}
|
||||
for _, alias := range keyStore.ListCerts() {
|
||||
cert, _ := keyStore.GetCert(alias)
|
||||
@@ -214,19 +249,20 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw
|
||||
for _, alias := range keyStore.ListPrivateKeys() {
|
||||
key, certs, err := keyStore.GetPrivateKeyAndCerts(alias, []byte(password(alias)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing keystore: %s\n", err)
|
||||
return fmt.Errorf("unable to parse keystore: %s\n", err)
|
||||
}
|
||||
block, err := keyToPem(key, mergeHeaders(headers, map[string]string{nameHeader: alias}))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading key: %s\n", err)
|
||||
return fmt.Errorf("problem reading key: %s\n", err)
|
||||
}
|
||||
callback(block)
|
||||
for _, cert := range certs {
|
||||
callback(EncodeX509ToPEM(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown file type: %s\n", format)
|
||||
return fmt.Errorf("unknown file type '%s'\n", format)
|
||||
}
|
||||
|
||||
func mergeHeaders(baseHeaders, extraHeaders map[string]string) (headers map[string]string) {
|
||||
|
||||
@@ -83,14 +83,19 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
var err error
|
||||
if *dumpPem {
|
||||
lib.ReadAsPEMFromFiles(files, *dumpType, readPassword, func(block *pem.Block) {
|
||||
err = lib.ReadAsPEMFromFiles(files, *dumpType, readPassword, func(block *pem.Block) {
|
||||
block.Headers = nil
|
||||
pem.Encode(os.Stdout, block)
|
||||
})
|
||||
} else {
|
||||
lib.ReadAsX509FromFiles(files, *dumpType, readPassword, func(cert *x509.Certificate) {
|
||||
result.Certificates = append(result.Certificates, cert)
|
||||
err = lib.ReadAsX509FromFiles(files, *dumpType, readPassword, func(cert *x509.Certificate, err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing block: %s\n", strings.TrimSuffix(err.Error(), "\n"))
|
||||
} else {
|
||||
result.Certificates = append(result.Certificates, cert)
|
||||
}
|
||||
})
|
||||
|
||||
if *dumpJSON {
|
||||
@@ -103,10 +108,16 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %s\n", strings.TrimSuffix(err.Error(), "\n"))
|
||||
os.Exit(1)
|
||||
} else if len(result.Certificates) == 0 && !*dumpPem {
|
||||
fmt.Fprintf(os.Stderr, "warning: no certificates found in input\n")
|
||||
}
|
||||
|
||||
case connect.FullCommand(): // Get certs by connecting to a server
|
||||
if connectStartTLS == nil && connectIdentity != nil {
|
||||
fmt.Fprintln(os.Stderr, "--identity can only be used with --start-tls")
|
||||
fmt.Fprintln(os.Stderr, "error: --identity can only be used with --start-tls")
|
||||
os.Exit(1)
|
||||
}
|
||||
connState, cri, err := starttls.GetConnectionState(*connectStartTLS, *connectName, *connectTo, *connectIdentity, *connectCert, *connectKey, *connectTimeout)
|
||||
@@ -154,8 +165,12 @@ func main() {
|
||||
defer file.Close()
|
||||
|
||||
chain := []*x509.Certificate{}
|
||||
lib.ReadAsX509FromFiles([]*os.File{file}, *verifyType, readPassword, func(cert *x509.Certificate) {
|
||||
chain = append(chain, cert)
|
||||
lib.ReadAsX509FromFiles([]*os.File{file}, *verifyType, readPassword, func(cert *x509.Certificate, err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing block: %s\n", strings.TrimSuffix(err.Error(), "\n"))
|
||||
} else {
|
||||
chain = append(chain, cert)
|
||||
}
|
||||
})
|
||||
|
||||
verifyResult := verifyChain(chain, *verifyName, *verifyCaPath)
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ type SignedDataEnvelope struct {
|
||||
// Refer to RFC 2315, Section 9.1 for definition of this type.
|
||||
type SignedData struct {
|
||||
Version int
|
||||
DigestAlgorithms []asn1.ObjectIdentifier `asn1:"set"`
|
||||
DigestAlgorithms []asn1.RawValue `asn1:"set"`
|
||||
ContentInfo asn1.RawValue
|
||||
Certificates []asn1.RawValue `asn1:"tag:0,optional,set"`
|
||||
RevocationLists []asn1.RawValue `asn1:"tag:1,optional,set"`
|
||||
|
||||
Reference in New Issue
Block a user