mirror of
https://github.com/square/certigo.git
synced 2024-04-21 12:32:40 +00:00
Merge pull request #99 from square/cs/lib
Factor out useful functions into library
This commit is contained in:
+342
@@ -0,0 +1,342 @@
|
||||
/*-
|
||||
* Copyright 2016 Square Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/square/certigo/jceks"
|
||||
"github.com/square/certigo/pkcs7"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
const (
|
||||
// nameHeader is the PEM header field for the friendly name/alias of the key in the key store.
|
||||
nameHeader = "friendlyName"
|
||||
|
||||
// fileHeader is the origin file where the key came from (as in file on disk).
|
||||
fileHeader = "originFile"
|
||||
)
|
||||
|
||||
var fileExtToFormat = map[string]string{
|
||||
".pem": "PEM",
|
||||
".crt": "PEM",
|
||||
".p7b": "PEM",
|
||||
".p7c": "PEM",
|
||||
".p12": "PKCS12",
|
||||
".pfx": "PKCS12",
|
||||
".jceks": "JCEKS",
|
||||
".jks": "JCEKS", // Only partially supported
|
||||
".der": "DER",
|
||||
}
|
||||
|
||||
var badSignatureAlgorithms = [...]x509.SignatureAlgorithm{
|
||||
x509.MD2WithRSA,
|
||||
x509.MD5WithRSA,
|
||||
x509.SHA1WithRSA,
|
||||
x509.DSAWithSHA1,
|
||||
x509.ECDSAWithSHA1,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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())
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, file.Name(), format, password, callback)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadAsPEM 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 ReadAsPEM(readers []io.Reader, format string, password func(string) string, callback func(*pem.Block)) error {
|
||||
for _, r := range readers {
|
||||
reader := bufio.NewReaderSize(r, 4)
|
||||
format, err := formatForFile(reader, "", format)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to guess format for input stream")
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, "", format, password, callback)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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())
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, file.Name(), format, password, pemToX509(callback))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
for _, r := range readers {
|
||||
reader := bufio.NewReaderSize(r, 4)
|
||||
format, err := formatForFile(reader, "", format)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to guess format for input stream")
|
||||
}
|
||||
|
||||
readCertsFromStream(reader, "", format, password, pemToX509(callback))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pemToX509(callback func(*x509.Certificate)) func(*pem.Block) {
|
||||
return func(block *pem.Block) {
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err == nil {
|
||||
callback(cert)
|
||||
}
|
||||
case "PKCS7":
|
||||
certs, err := pkcs7.ExtractCertificates(block.Bytes)
|
||||
if err == nil {
|
||||
for _, cert := range certs {
|
||||
callback(cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readCertsFromStream takes some input and converts it to PEM blocks.
|
||||
func readCertsFromStream(reader io.Reader, filename string, format string, password func(string) string, callback func(*pem.Block)) error {
|
||||
headers := map[string]string{}
|
||||
if filename != "" && filename != os.Stdin.Name() {
|
||||
headers[fileHeader] = filename
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "PEM":
|
||||
scanner := pemScanner(reader)
|
||||
for scanner.Scan() {
|
||||
block, _ := pem.Decode(scanner.Bytes())
|
||||
block.Headers = mergeHeaders(block.Headers, headers)
|
||||
callback(block)
|
||||
}
|
||||
case "DER":
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading input: %s\n", err)
|
||||
}
|
||||
x509Certs, err := x509.ParseCertificates(data)
|
||||
if err == nil {
|
||||
for _, cert := range x509Certs {
|
||||
callback(EncodeX509ToPEM(cert, headers))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
p7bBlocks, err := pkcs7.ParseSignedData(data)
|
||||
if err == nil {
|
||||
for _, block := range p7bBlocks {
|
||||
callback(pkcs7ToPem(block, headers))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error parsing certificates from DER data\n")
|
||||
case "PKCS12":
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading input: %s\n", err)
|
||||
}
|
||||
blocks, err := pkcs12.ToPEM(data, password(""))
|
||||
if err != nil || len(blocks) == 0 {
|
||||
fmt.Fprint(os.Stderr, "keystore appears to be empty or password was incorrect\n")
|
||||
}
|
||||
for _, block := range blocks {
|
||||
block.Headers = mergeHeaders(block.Headers, headers)
|
||||
callback(block)
|
||||
}
|
||||
case "JCEKS":
|
||||
keyStore, err := jceks.LoadFromReader(reader, []byte(password("")))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing keystore: %s\n", err)
|
||||
}
|
||||
for _, alias := range keyStore.ListCerts() {
|
||||
cert, _ := keyStore.GetCert(alias)
|
||||
callback(EncodeX509ToPEM(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})))
|
||||
}
|
||||
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)
|
||||
}
|
||||
block, err := keyToPem(key, mergeHeaders(headers, map[string]string{nameHeader: alias}))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading key: %s\n", err)
|
||||
}
|
||||
callback(block)
|
||||
for _, cert := range certs {
|
||||
callback(EncodeX509ToPEM(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})))
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown file type: %s\n", format)
|
||||
}
|
||||
|
||||
func mergeHeaders(baseHeaders, extraHeaders map[string]string) (headers map[string]string) {
|
||||
headers = map[string]string{}
|
||||
for k, v := range baseHeaders {
|
||||
headers[k] = v
|
||||
}
|
||||
for k, v := range extraHeaders {
|
||||
headers[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeX509ToPEM converts an X.509 certificate into a PEM block for output.
|
||||
func EncodeX509ToPEM(cert *x509.Certificate, headers map[string]string) *pem.Block {
|
||||
return &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
Headers: headers,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a PKCS7 envelope into a PEM block for output.
|
||||
func pkcs7ToPem(block *pkcs7.SignedDataEnvelope, headers map[string]string) *pem.Block {
|
||||
return &pem.Block{
|
||||
Type: "PKCS7",
|
||||
Bytes: block.Raw,
|
||||
Headers: headers,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a key into one or more PEM blocks for output.
|
||||
func keyToPem(key crypto.PrivateKey, headers map[string]string) (*pem.Block, error) {
|
||||
switch k := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(k),
|
||||
Headers: headers,
|
||||
}, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
raw, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling key: %s\n", reflect.TypeOf(key))
|
||||
}
|
||||
return &pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: raw,
|
||||
Headers: headers,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown key type: %s\n", reflect.TypeOf(key))
|
||||
}
|
||||
|
||||
// formatForFile returns the file format (either from flags or
|
||||
// based on file extension).
|
||||
func formatForFile(file *bufio.Reader, filename, format string) (string, error) {
|
||||
// First, honor --format flag we got from user
|
||||
if format != "" {
|
||||
return format, nil
|
||||
}
|
||||
|
||||
// Second, attempt to guess based on extension
|
||||
guess, ok := fileExtToFormat[strings.ToLower(filepath.Ext(filename))]
|
||||
if ok {
|
||||
return guess, nil
|
||||
}
|
||||
|
||||
// Third, attempt to guess based on first 4 bytes of input
|
||||
data, err := file.Peek(4)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to read file: %s\n", err)
|
||||
}
|
||||
|
||||
// Heuristics for guessing -- best effort.
|
||||
magic := binary.BigEndian.Uint32(data)
|
||||
if magic == 0xCECECECE || magic == 0xFEEDFEED {
|
||||
// JCEKS/JKS files always start with this prefix
|
||||
return "JCEKS", nil
|
||||
}
|
||||
if magic == 0x2D2D2D2D || magic == 0x434f4e4e {
|
||||
// Starts with '----' or 'CONN' (what s_client prints...)
|
||||
return "PEM", nil
|
||||
}
|
||||
if magic&0xFFFF0000 == 0x30820000 {
|
||||
// Looks like the input is DER-encoded, so it's either PKCS12 or X.509.
|
||||
if magic&0x0000FF00 == 0x0300 {
|
||||
// Probably X.509
|
||||
return "DER", nil
|
||||
}
|
||||
return "PKCS12", nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unable to guess file format")
|
||||
}
|
||||
|
||||
// pemScanner will return a bufio.Scanner that splits the input
|
||||
// from the given reader into PEM blocks.
|
||||
func pemScanner(reader io.Reader) *bufio.Scanner {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
|
||||
block, rest := pem.Decode(data)
|
||||
if block != nil {
|
||||
size := len(data) - len(rest)
|
||||
return size, data[:size], nil
|
||||
}
|
||||
|
||||
return 0, nil, nil
|
||||
})
|
||||
|
||||
return scanner
|
||||
}
|
||||
+38
-16
@@ -14,13 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@@ -68,11 +70,15 @@ type certWithName struct {
|
||||
cert *x509.Certificate
|
||||
}
|
||||
|
||||
func createSimpleCertificateFromX509(block *pem.Block) simpleCertificate {
|
||||
func (c certWithName) MarshalJSON() ([]byte, error) {
|
||||
out := createSimpleCertificate(c.name, c.cert)
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
func createSimpleCertificateFromX509(block *pem.Block) (simpleCertificate, error) {
|
||||
raw, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading cert: %s", err)
|
||||
os.Exit(1)
|
||||
return simpleCertificate{}, fmt.Errorf("error reading cert: %s", err)
|
||||
}
|
||||
|
||||
cert := certWithName{cert: raw}
|
||||
@@ -83,14 +89,34 @@ func createSimpleCertificateFromX509(block *pem.Block) simpleCertificate {
|
||||
cert.file = val
|
||||
}
|
||||
|
||||
return createSimpleCertificate(cert)
|
||||
return createSimpleCertificate(cert.name, cert.cert), nil
|
||||
}
|
||||
|
||||
// EncodeX509ToJSON encodes an X.509 certificate into a JSON string.
|
||||
func EncodeX509ToJSON(cert *x509.Certificate) []byte {
|
||||
out := createSimpleCertificate("", cert)
|
||||
raw, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
// EncodeX509ToObject encodes an X.509 certificate into a JSON-serializable object.
|
||||
func EncodeX509ToObject(cert *x509.Certificate) interface{} {
|
||||
return createSimpleCertificate("", cert)
|
||||
}
|
||||
|
||||
// EncodeX509ToText encodes an X.509 certificate into human-readable text.
|
||||
func EncodeX509ToText(cert *x509.Certificate) []byte {
|
||||
return displayCert(createSimpleCertificate("", cert))
|
||||
}
|
||||
|
||||
// displayCert takes in a parsed certificate object
|
||||
// (for jceks certs, blank otherwise), and prints out relevant
|
||||
// information. Start and end dates are colored based on whether or not
|
||||
// the certificate is expired, not expired, or close to expiring.
|
||||
func displayCert(cert simpleCertificate) {
|
||||
func displayCert(cert simpleCertificate) []byte {
|
||||
funcMap := template.FuncMap{
|
||||
"certStart": certStart,
|
||||
"certEnd": certEnd,
|
||||
@@ -106,11 +132,15 @@ func displayCert(cert simpleCertificate) {
|
||||
// Should never happen
|
||||
panic(err)
|
||||
}
|
||||
err = t.Execute(os.Stdout, cert)
|
||||
var buffer bytes.Buffer
|
||||
w := bufio.NewWriter(&buffer)
|
||||
err = t.Execute(w, cert)
|
||||
if err != nil {
|
||||
// Should never happen
|
||||
panic(err)
|
||||
}
|
||||
w.Flush()
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -185,14 +215,6 @@ func certEnd(end time.Time) string {
|
||||
}
|
||||
}
|
||||
|
||||
var badSignatureAlgorithms = []x509.SignatureAlgorithm{
|
||||
x509.MD2WithRSA,
|
||||
x509.MD5WithRSA,
|
||||
x509.SHA1WithRSA,
|
||||
x509.DSAWithSHA1,
|
||||
x509.ECDSAWithSHA1,
|
||||
}
|
||||
|
||||
func redify(text string) string {
|
||||
return red.SprintfFunc()("%s", text)
|
||||
}
|
||||
+54
-42
@@ -1,4 +1,20 @@
|
||||
package main
|
||||
/*-
|
||||
* Copyright 2016 Square Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -81,6 +97,7 @@ type nameConstraints struct {
|
||||
PermittedDNSDomains []string `json:"permitted_dns_domains,omitempty"`
|
||||
}
|
||||
|
||||
// simpleCertificate is a JSON-representable certificate metadata holder.
|
||||
type simpleCertificate struct {
|
||||
Alias string `json:"alias,omitempty"`
|
||||
SerialNumber string `json:"serial"`
|
||||
@@ -88,8 +105,8 @@ type simpleCertificate struct {
|
||||
NotAfter time.Time `json:"not_after"`
|
||||
SignatureAlgorithm simpleSigAlg `json:"signature_algorithm"`
|
||||
IsSelfSigned bool `json:"is_self_signed"`
|
||||
Subject simplePkixName `json:"subject"`
|
||||
Issuer simplePkixName `json:"issuer"`
|
||||
Subject simplePKIXName `json:"subject"`
|
||||
Issuer simplePKIXName `json:"issuer"`
|
||||
BasicConstraints *basicConstraints `json:"basic_constraints,omitempty"`
|
||||
NameConstraints *nameConstraints `json:"name_constraints,omitempty"`
|
||||
KeyUsage simpleKeyUsage `json:"key_usage,omitempty"`
|
||||
@@ -101,7 +118,7 @@ type simpleCertificate struct {
|
||||
PEM string `json:"pem,omitempty"`
|
||||
}
|
||||
|
||||
type simplePkixName struct {
|
||||
type simplePKIXName struct {
|
||||
Name pkix.Name
|
||||
KeyID []byte
|
||||
}
|
||||
@@ -111,53 +128,48 @@ type simpleExtKeyUsage x509.ExtKeyUsage
|
||||
|
||||
type simpleSigAlg x509.SignatureAlgorithm
|
||||
|
||||
type simpleResult struct {
|
||||
Certificates []simpleCertificate `json:"certificates"`
|
||||
VerifyResult *simpleVerification `json:"verify_result,omitempty"`
|
||||
}
|
||||
|
||||
func createSimpleCertificate(c certWithName) simpleCertificate {
|
||||
func createSimpleCertificate(name string, cert *x509.Certificate) simpleCertificate {
|
||||
out := simpleCertificate{
|
||||
Alias: c.name,
|
||||
SerialNumber: c.cert.SerialNumber.String(),
|
||||
NotBefore: c.cert.NotBefore,
|
||||
NotAfter: c.cert.NotAfter,
|
||||
SignatureAlgorithm: simpleSigAlg(c.cert.SignatureAlgorithm),
|
||||
IsSelfSigned: isSelfSigned(c.cert),
|
||||
Subject: simplePkixName{
|
||||
Name: c.cert.Subject,
|
||||
KeyID: c.cert.SubjectKeyId,
|
||||
Alias: name,
|
||||
SerialNumber: cert.SerialNumber.String(),
|
||||
NotBefore: cert.NotBefore,
|
||||
NotAfter: cert.NotAfter,
|
||||
SignatureAlgorithm: simpleSigAlg(cert.SignatureAlgorithm),
|
||||
IsSelfSigned: IsSelfSigned(cert),
|
||||
Subject: simplePKIXName{
|
||||
Name: cert.Subject,
|
||||
KeyID: cert.SubjectKeyId,
|
||||
},
|
||||
Issuer: simplePkixName{
|
||||
Name: c.cert.Issuer,
|
||||
KeyID: c.cert.AuthorityKeyId,
|
||||
Issuer: simplePKIXName{
|
||||
Name: cert.Issuer,
|
||||
KeyID: cert.AuthorityKeyId,
|
||||
},
|
||||
KeyUsage: simpleKeyUsage(c.cert.KeyUsage),
|
||||
AltDNSNames: c.cert.DNSNames,
|
||||
AltIPAddresses: c.cert.IPAddresses,
|
||||
EmailAddresses: c.cert.EmailAddresses,
|
||||
Warnings: certWarnings(c.cert),
|
||||
PEM: string(pem.EncodeToMemory(certToPem(c.cert, nil))),
|
||||
KeyUsage: simpleKeyUsage(cert.KeyUsage),
|
||||
AltDNSNames: cert.DNSNames,
|
||||
AltIPAddresses: cert.IPAddresses,
|
||||
EmailAddresses: cert.EmailAddresses,
|
||||
Warnings: certWarnings(cert),
|
||||
PEM: string(pem.EncodeToMemory(EncodeX509ToPEM(cert, nil))),
|
||||
}
|
||||
|
||||
if c.cert.BasicConstraintsValid {
|
||||
if cert.BasicConstraintsValid {
|
||||
out.BasicConstraints = &basicConstraints{
|
||||
IsCA: c.cert.IsCA,
|
||||
IsCA: cert.IsCA,
|
||||
}
|
||||
if c.cert.MaxPathLen > 0 || c.cert.MaxPathLenZero {
|
||||
out.BasicConstraints.MaxPathLen = &c.cert.MaxPathLen
|
||||
if cert.MaxPathLen > 0 || cert.MaxPathLenZero {
|
||||
out.BasicConstraints.MaxPathLen = &cert.MaxPathLen
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.cert.PermittedDNSDomains) > 0 {
|
||||
if len(cert.PermittedDNSDomains) > 0 {
|
||||
out.NameConstraints = &nameConstraints{
|
||||
Critical: c.cert.PermittedDNSDomainsCritical,
|
||||
PermittedDNSDomains: c.cert.PermittedDNSDomains,
|
||||
Critical: cert.PermittedDNSDomainsCritical,
|
||||
PermittedDNSDomains: cert.PermittedDNSDomains,
|
||||
}
|
||||
}
|
||||
|
||||
simpleEku := []simpleExtKeyUsage{}
|
||||
for _, eku := range c.cert.ExtKeyUsage {
|
||||
for _, eku := range cert.ExtKeyUsage {
|
||||
simpleEku = append(simpleEku, simpleExtKeyUsage(eku))
|
||||
}
|
||||
out.ExtKeyUsage = simpleEku
|
||||
@@ -165,12 +177,7 @@ func createSimpleCertificate(c certWithName) simpleCertificate {
|
||||
return out
|
||||
}
|
||||
|
||||
func (c certWithName) MarshalJSON() ([]byte, error) {
|
||||
out := createSimpleCertificate(c)
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
func (p simplePkixName) MarshalJSON() ([]byte, error) {
|
||||
func (p simplePKIXName) MarshalJSON() ([]byte, error) {
|
||||
out := map[string]interface{}{}
|
||||
|
||||
if p.Name.CommonName != "" {
|
||||
@@ -322,3 +329,8 @@ func algWarnings(cert *x509.Certificate) (warnings []string) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// IsSelfSigned returns true iff the given certificate has a valid self-signature.
|
||||
func IsSelfSigned(cert *x509.Certificate) bool {
|
||||
return cert.CheckSignatureFrom(cert) == nil
|
||||
}
|
||||
@@ -17,28 +17,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
|
||||
"github.com/square/certigo/jceks"
|
||||
"github.com/square/certigo/pkcs7"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
"github.com/square/certigo/lib"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
@@ -63,26 +52,10 @@ var (
|
||||
verifyFile = verify.Arg("file", "Certificate file to dump (or stdin if not specified).").ExistingFile()
|
||||
verifyName = verify.Flag("name", "Server name to verify certificate against.").Required().String()
|
||||
verifyCaPath = verify.Flag("ca", "Path to CA bundle (system default if unspecified).").ExistingFile()
|
||||
verifyType = verify.Flag("format", "Format of given input (PEM, DER, JCEKS, PKCS12; heuristic if missing).").String()
|
||||
verifyJSON = verify.Flag("json", "Write output as machine-readable JSON format.").Bool()
|
||||
)
|
||||
|
||||
const (
|
||||
nameHeader = "friendlyName"
|
||||
fileHeader = "originFile"
|
||||
)
|
||||
|
||||
var fileExtToFormat = map[string]string{
|
||||
".pem": "PEM",
|
||||
".crt": "PEM",
|
||||
".p7b": "PEM",
|
||||
".p7c": "PEM",
|
||||
".p12": "PKCS12",
|
||||
".pfx": "PKCS12",
|
||||
".jceks": "JCEKS",
|
||||
".jks": "JCEKS", // Only partially supported
|
||||
".der": "DER",
|
||||
}
|
||||
|
||||
func main() {
|
||||
app.Version("1.4.0")
|
||||
|
||||
@@ -96,36 +69,24 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
readCerts(files, func(block *pem.Block) {
|
||||
if *dumpPem {
|
||||
if *dumpPem {
|
||||
lib.ReadAsPEMFromFiles(files, *dumpType, readPassword, func(block *pem.Block) {
|
||||
block.Headers = nil
|
||||
pem.Encode(os.Stdout, block)
|
||||
return
|
||||
}
|
||||
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
result.Certificates = append(result.Certificates, createSimpleCertificateFromX509(block))
|
||||
case "PKCS7":
|
||||
certs, err := pkcs7.ExtractCertificates(block.Bytes)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing PKCS7 block: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, cert := range certs {
|
||||
result.Certificates = append(result.Certificates, createSimpleCertificate(certWithName{cert: cert}))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if *dumpJSON {
|
||||
blob, _ := json.Marshal(result)
|
||||
fmt.Println(string(blob))
|
||||
})
|
||||
} else {
|
||||
for i, cert := range result.Certificates {
|
||||
fmt.Printf("** CERTIFICATE %d **\n", i+1)
|
||||
displayCert(cert)
|
||||
fmt.Printf("\n\n")
|
||||
lib.ReadAsX509FromFiles(files, *dumpType, readPassword, func(cert *x509.Certificate) {
|
||||
result.Certificates = append(result.Certificates, cert)
|
||||
})
|
||||
|
||||
if *dumpJSON {
|
||||
blob, _ := json.Marshal(result)
|
||||
fmt.Println(string(blob))
|
||||
} else {
|
||||
for i, cert := range result.Certificates {
|
||||
fmt.Printf("** CERTIFICATE %d **\n", i+1)
|
||||
fmt.Printf("%s\n\n", lib.EncodeX509ToText(cert))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,9 +103,9 @@ func main() {
|
||||
defer conn.Close()
|
||||
for _, cert := range conn.ConnectionState().PeerCertificates {
|
||||
if *connectPem {
|
||||
pem.Encode(os.Stdout, certToPem(cert, nil))
|
||||
pem.Encode(os.Stdout, lib.EncodeX509ToPEM(cert, nil))
|
||||
} else {
|
||||
result.Certificates = append(result.Certificates, createSimpleCertificate(certWithName{cert: cert}))
|
||||
result.Certificates = append(result.Certificates, cert)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +126,7 @@ func main() {
|
||||
} else if !*connectPem {
|
||||
for i, cert := range result.Certificates {
|
||||
fmt.Printf("** CERTIFICATE %d **\n", i+1)
|
||||
displayCert(cert)
|
||||
fmt.Print("\n\n")
|
||||
fmt.Printf("%s\n\n", lib.EncodeX509ToText(cert))
|
||||
}
|
||||
printVerifyResult(*result.VerifyResult)
|
||||
}
|
||||
@@ -175,23 +135,8 @@ func main() {
|
||||
defer file.Close()
|
||||
|
||||
chain := []*x509.Certificate{}
|
||||
readCerts([]*os.File{file}, func(block *pem.Block) {
|
||||
switch block.Type {
|
||||
case "CERTIFICATE":
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading cert: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
chain = append(chain, cert)
|
||||
case "PKCS7":
|
||||
certs, err := pkcs7.ExtractCertificates(block.Bytes)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing PKCS7 block: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
chain = append(chain, certs...)
|
||||
}
|
||||
lib.ReadAsX509FromFiles([]*os.File{file}, *verifyType, readPassword, func(cert *x509.Certificate) {
|
||||
chain = append(chain, cert)
|
||||
})
|
||||
|
||||
verifyResult := verifyChain(chain, *verifyName, *verifyCaPath)
|
||||
@@ -237,20 +182,7 @@ func inputFiles(fileNames []string) []*os.File {
|
||||
return files
|
||||
}
|
||||
|
||||
func readCerts(files []*os.File, callback func(*pem.Block)) {
|
||||
for _, file := range files {
|
||||
reader := bufio.NewReaderSize(file, 4)
|
||||
format, ok := formatForFile(reader, file.Name(), *dumpType)
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "unable to guess file type (for file %s)\n", file.Name())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
readCertsFromFile(reader, file.Name(), format, callback)
|
||||
}
|
||||
}
|
||||
|
||||
func readPassword(prompt string) string {
|
||||
func readPassword(alias string) string {
|
||||
if *dumpPassword != "" {
|
||||
return *dumpPassword
|
||||
}
|
||||
@@ -263,7 +195,12 @@ func readPassword(prompt string) string {
|
||||
defer tty.Close()
|
||||
}
|
||||
|
||||
tty.WriteString(prompt)
|
||||
tty.WriteString("Enter password")
|
||||
if alias != "" {
|
||||
tty.WriteString(fmt.Sprintf(" for entry [%s]", alias))
|
||||
}
|
||||
tty.WriteString(": ")
|
||||
|
||||
password, err := terminal.ReadPassword(int(tty.Fd()))
|
||||
tty.WriteString("\n")
|
||||
if err != nil {
|
||||
@@ -273,202 +210,3 @@ func readPassword(prompt string) string {
|
||||
|
||||
return strings.TrimSuffix(string(password), "\n")
|
||||
}
|
||||
|
||||
// formatForFile returns the file format (either from flags or
|
||||
// based on file extension).
|
||||
func formatForFile(file *bufio.Reader, filename, format string) (string, bool) {
|
||||
// First, honor --format flag we got from user
|
||||
if format != "" {
|
||||
return format, true
|
||||
}
|
||||
|
||||
// Second, attempt to guess based on extension
|
||||
guess, ok := fileExtToFormat[strings.ToLower(filepath.Ext(filename))]
|
||||
if ok {
|
||||
return guess, true
|
||||
}
|
||||
|
||||
// Third, attempt to guess based on first 4 bytes of input
|
||||
data, err := file.Peek(4)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Heuristics for guessing -- best effort.
|
||||
magic := binary.BigEndian.Uint32(data)
|
||||
if magic == 0xCECECECE || magic == 0xFEEDFEED {
|
||||
// JCEKS/JKS files always start with this prefix
|
||||
return "JCEKS", true
|
||||
}
|
||||
if magic == 0x2D2D2D2D || magic == 0x434f4e4e {
|
||||
// Starts with '----' or 'CONN' (what s_client prints...)
|
||||
return "PEM", true
|
||||
}
|
||||
if magic&0xFFFF0000 == 0x30820000 {
|
||||
// Looks like the input is DER-encoded, so it's either PKCS12 or X.509.
|
||||
if magic&0x0000FF00 == 0x0300 {
|
||||
// Probably X.509
|
||||
return "DER", true
|
||||
}
|
||||
return "PKCS12", true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// pemScanner will return a bufio.Scanner that splits the input
|
||||
// from the given reader into PEM blocks.
|
||||
func pemScanner(reader io.Reader) *bufio.Scanner {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
|
||||
block, rest := pem.Decode(data)
|
||||
if block != nil {
|
||||
size := len(data) - len(rest)
|
||||
return size, data[:size], nil
|
||||
}
|
||||
|
||||
return 0, nil, nil
|
||||
})
|
||||
|
||||
return scanner
|
||||
}
|
||||
|
||||
// readCertsFromFile takes some input and converts it to PEM blocks.
|
||||
func readCertsFromFile(reader io.Reader, filename string, format string, callback func(*pem.Block)) {
|
||||
headers := map[string]string{}
|
||||
if filename != "" && filename != os.Stdin.Name() {
|
||||
headers[fileHeader] = filename
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "PEM":
|
||||
scanner := pemScanner(reader)
|
||||
for scanner.Scan() {
|
||||
block, _ := pem.Decode(scanner.Bytes())
|
||||
block.Headers = mergeHeaders(block.Headers, headers)
|
||||
callback(block)
|
||||
}
|
||||
case "DER":
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading input: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
x509Certs, err := x509.ParseCertificates(data)
|
||||
if err == nil {
|
||||
for _, cert := range x509Certs {
|
||||
callback(certToPem(cert, headers))
|
||||
}
|
||||
return
|
||||
}
|
||||
p7bBlocks, err := pkcs7.ParseSignedData(data)
|
||||
if err == nil {
|
||||
for _, block := range p7bBlocks {
|
||||
callback(pkcs7ToPem(block, headers))
|
||||
}
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "error parsing certificates from DER data\n")
|
||||
os.Exit(1)
|
||||
case "PKCS12":
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading input: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
password := readPassword("Enter password: ")
|
||||
blocks, err := pkcs12.ToPEM(data, password)
|
||||
if err != nil || len(blocks) == 0 {
|
||||
fmt.Fprint(os.Stderr, "keystore appears to be empty or password was incorrect\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, block := range blocks {
|
||||
block.Headers = mergeHeaders(block.Headers, headers)
|
||||
callback(block)
|
||||
}
|
||||
case "JCEKS":
|
||||
password := readPassword("Enter password: ")
|
||||
keyStore, err := jceks.LoadFromReader(reader, []byte(password))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing keystore: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, alias := range keyStore.ListCerts() {
|
||||
cert, _ := keyStore.GetCert(alias)
|
||||
callback(certToPem(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})))
|
||||
}
|
||||
for _, alias := range keyStore.ListPrivateKeys() {
|
||||
password := readPassword(fmt.Sprintf("Enter password for alias [%s]: ", alias))
|
||||
key, certs, err := keyStore.GetPrivateKeyAndCerts(alias, []byte(password))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing keystore: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
callback(keyToPem(key, mergeHeaders(headers, map[string]string{nameHeader: alias})))
|
||||
for _, cert := range certs {
|
||||
callback(certToPem(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})))
|
||||
}
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown file type: %s\n", format)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeHeaders(baseHeaders, extraHeaders map[string]string) (headers map[string]string) {
|
||||
headers = map[string]string{}
|
||||
for k, v := range baseHeaders {
|
||||
headers[k] = v
|
||||
}
|
||||
for k, v := range extraHeaders {
|
||||
headers[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Convert an X.509 cert into a PEM block for output.
|
||||
func certToPem(cert *x509.Certificate, headers map[string]string) *pem.Block {
|
||||
return &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
Headers: headers,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a PKCS7 envelope into a PEM block for output.
|
||||
func pkcs7ToPem(block *pkcs7.SignedDataEnvelope, headers map[string]string) *pem.Block {
|
||||
return &pem.Block{
|
||||
Type: "PKCS7",
|
||||
Bytes: block.Raw,
|
||||
Headers: headers,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a key into one or more PEM blocks for output.
|
||||
func keyToPem(key crypto.PrivateKey, headers map[string]string) *pem.Block {
|
||||
switch k := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(k),
|
||||
Headers: headers,
|
||||
}
|
||||
case *ecdsa.PrivateKey:
|
||||
raw, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error marshaling key: %s\n", reflect.TypeOf(key))
|
||||
os.Exit(1)
|
||||
}
|
||||
return &pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: raw,
|
||||
Headers: headers,
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown key type: %s\n", reflect.TypeOf(key))
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,17 +18,51 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/square/certigo/lib"
|
||||
)
|
||||
|
||||
var (
|
||||
green = color.New(color.Bold, color.FgGreen)
|
||||
yellow = color.New(color.Bold, color.FgYellow)
|
||||
red = color.New(color.Bold, color.FgRed)
|
||||
)
|
||||
|
||||
var algoName = [...]string{
|
||||
x509.MD2WithRSA: "MD2-RSA",
|
||||
x509.MD5WithRSA: "MD5-RSA",
|
||||
x509.SHA1WithRSA: "SHA1-RSA",
|
||||
x509.SHA256WithRSA: "SHA256-RSA",
|
||||
x509.SHA384WithRSA: "SHA384-RSA",
|
||||
x509.SHA512WithRSA: "SHA512-RSA",
|
||||
x509.DSAWithSHA1: "DSA-SHA1",
|
||||
x509.DSAWithSHA256: "DSA-SHA256",
|
||||
x509.ECDSAWithSHA1: "ECDSA-SHA1",
|
||||
x509.ECDSAWithSHA256: "ECDSA-SHA256",
|
||||
x509.ECDSAWithSHA384: "ECDSA-SHA384",
|
||||
x509.ECDSAWithSHA512: "ECDSA-SHA512",
|
||||
}
|
||||
|
||||
var badSignatureAlgorithms = [...]x509.SignatureAlgorithm{
|
||||
x509.MD2WithRSA,
|
||||
x509.MD5WithRSA,
|
||||
x509.SHA1WithRSA,
|
||||
x509.DSAWithSHA1,
|
||||
x509.ECDSAWithSHA1,
|
||||
}
|
||||
|
||||
type simpleVerifyCert struct {
|
||||
Name string `json:"name"`
|
||||
IsSelfSigned bool `json:"is_self_signed"`
|
||||
SignatureAlgorithm simpleSigAlg `json:"signature_algorithm"`
|
||||
PEM string `json:"pem"`
|
||||
Name string `json:"name"`
|
||||
IsSelfSigned bool `json:"is_self_signed"`
|
||||
PEM string `json:"pem"`
|
||||
signatureAlgorithm x509.SignatureAlgorithm
|
||||
}
|
||||
|
||||
type simpleVerification struct {
|
||||
@@ -36,6 +70,25 @@ type simpleVerification struct {
|
||||
Chains [][]simpleVerifyCert `json:"chains"`
|
||||
}
|
||||
|
||||
type simpleResult struct {
|
||||
Certificates []*x509.Certificate `json:"certificates"`
|
||||
VerifyResult *simpleVerification `json:"verify_result,omitempty"`
|
||||
}
|
||||
|
||||
func (s simpleResult) MarshalJSON() ([]byte, error) {
|
||||
certs := make([]interface{}, len(s.Certificates))
|
||||
for i, c := range s.Certificates {
|
||||
certs[i] = lib.EncodeX509ToObject(c)
|
||||
}
|
||||
|
||||
out := map[string]interface{}{}
|
||||
out["certificates"] = certs
|
||||
if s.VerifyResult != nil {
|
||||
out["verify_result"] = s.VerifyResult
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
func caBundle(caPath string) *x509.CertPool {
|
||||
if caPath == "" {
|
||||
return nil
|
||||
@@ -74,14 +127,13 @@ func verifyChain(certs []*x509.Certificate, dnsName, caPath string) simpleVerifi
|
||||
return result
|
||||
}
|
||||
|
||||
//green.Printf("Server certificates appear to be valid (found %d chains):\n", len(chains))
|
||||
for _, chain := range chains {
|
||||
aChain := []simpleVerifyCert{}
|
||||
for _, cert := range chain {
|
||||
aCert := simpleVerifyCert{
|
||||
IsSelfSigned: isSelfSigned(cert),
|
||||
SignatureAlgorithm: simpleSigAlg(cert.SignatureAlgorithm),
|
||||
PEM: string(pem.EncodeToMemory(certToPem(cert, nil))),
|
||||
IsSelfSigned: lib.IsSelfSigned(cert),
|
||||
signatureAlgorithm: cert.SignatureAlgorithm,
|
||||
PEM: string(pem.EncodeToMemory(lib.EncodeX509ToPEM(cert, nil))),
|
||||
}
|
||||
|
||||
if cert.Subject.CommonName != "" {
|
||||
@@ -89,6 +141,7 @@ func verifyChain(certs []*x509.Certificate, dnsName, caPath string) simpleVerifi
|
||||
} else {
|
||||
aCert.Name = fmt.Sprintf("Serial #%s", cert.SerialNumber.String())
|
||||
}
|
||||
|
||||
aChain = append(aChain, aCert)
|
||||
}
|
||||
result.Chains = append(result.Chains, aChain)
|
||||
@@ -102,7 +155,7 @@ func fmtCert(cert simpleVerifyCert) string {
|
||||
name += green.SprintfFunc()(" [self-signed]")
|
||||
}
|
||||
for _, alg := range badSignatureAlgorithms {
|
||||
if x509.SignatureAlgorithm(cert.SignatureAlgorithm) == alg {
|
||||
if cert.signatureAlgorithm == alg {
|
||||
name += red.SprintfFunc()(" [%s]", algString(alg))
|
||||
break
|
||||
}
|
||||
@@ -110,6 +163,13 @@ func fmtCert(cert simpleVerifyCert) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func algString(algo x509.SignatureAlgorithm) string {
|
||||
if 0 < algo && int(algo) < len(algoName) {
|
||||
return algoName[algo]
|
||||
}
|
||||
return strconv.Itoa(int(algo))
|
||||
}
|
||||
|
||||
func printVerifyResult(result simpleVerification) {
|
||||
if result.Error != "" {
|
||||
red.Printf("Failed to verify certificate chain:\n")
|
||||
@@ -126,7 +186,3 @@ func printVerifyResult(result simpleVerification) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isSelfSigned(cert *x509.Certificate) bool {
|
||||
return cert.CheckSignatureFrom(cert) == nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user