From 6f4f86da79025f5e8d9f394e95e7db1778a10415 Mon Sep 17 00:00:00 2001 From: wangtiga Date: Thu, 9 Nov 2023 23:29:44 +0800 Subject: [PATCH] tidy code not finished yet --- cmd/grpcurl/grpcurl.go | 100 +++++++--- grpcurl.go | 68 +------ internal/certigo/lib/certs.go | 361 +++++++++++++++++++++++++--------- 3 files changed, 335 insertions(+), 194 deletions(-) diff --git a/cmd/grpcurl/grpcurl.go b/cmd/grpcurl/grpcurl.go index cf5ae0d..6b0fbcf 100644 --- a/cmd/grpcurl/grpcurl.go +++ b/cmd/grpcurl/grpcurl.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/fullstorydev/grpcurl/internal/certigo/lib" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/grpcreflect" "google.golang.org/grpc" @@ -64,18 +65,21 @@ var ( cacert = flags.String("cacert", "", prettify(` File containing trusted root certificates for verifying the server. Ignored if -insecure is specified.`)) + pCACertFormat = flags.String("cacert-format", string(lib.CertKeyFormatPEM), prettify(` + cacert Format of given input (PEM, DER; heuristic if missing).`)) cert = flags.String("cert", "", prettify(` File containing client certificate (public key), to present to the server. Not valid with -plaintext option. Must also provide -key option - when use PEM certificate file.`)) - certTypeString = flags.String("cert-type", "", prettify(` - Client certificate file type. (PEM/P12)`)) - certType = grpcurl.CertTypePEM - pass = flags.String("pass", "", prettify(` + when use PEM/DER certificate file.`)) + pCertFormat = flags.String("cert-format", string(lib.CertKeyFormatPEM), prettify(` + cert Format of given input (PEM, DER, PKCS12; heuristic if missing).`)) + pass = flags.String("pass", "", prettify(` Pass phrase for the key`)) key = flags.String("key", "", prettify(` File containing client private key, to present to the server. Not valid with -plaintext option. Must also provide -cert option.`)) + pKeyFormat = flags.String("key-format", string(lib.CertKeyFormatPEM), prettify(` + key Format of given input (PEM, DER; heuristic if missing).`)) // ALTS Options usealts = flags.Bool("alts", false, prettify(` @@ -294,17 +298,9 @@ func main() { // default behavior is to use tls usetls := !*plaintext && !*usealts - - //// converto to CertificateFileType - //if len(*certTypeString) == 0 { - // certType = grpcurl.CertTypePEM // default PEM - //} else if strings.EqualFold(*certTypeString, "PEM") { - // certType = grpcurl.CertTypePEM - //} else if strings.EqualFold(*certTypeString, "P12") { - // certType = grpcurl.CertTypeP12 - //} else { - // fail(nil, "The -cert-type argument must be PEM or P12.") - //} + cacertFormat := lib.NewCertificateKeyFormat(*pCACertFormat) + certFormat := lib.NewCertificateKeyFormat(*pCertFormat) + keyFormat := lib.NewCertificateKeyFormat(*pKeyFormat) // Do extra validation on arguments and figure out what user asked us to do. if *connectTimeout < 0 { @@ -332,21 +328,61 @@ func main() { fail(nil, "The -key argument can only be used with TLS.") } - //switch certType { - //case grpcurl.CertTypePEM: - // if (*key == "") != (*cert == "") { - // fail(nil, "The -cert and -key arguments must be used together and both be present when -cert-type is PEM.") - // } - //case grpcurl.CertTypeP12: - // if *key != "" { - // fail(nil, "The -key arguments must not be used when -cert-type is P12.") - // } - // if *cert == "" { - // fail(nil, "The -cert arguments must be used when -cert-type is P12.") - // } - //default: - // fail(nil, "Not support cert type %v.", certType) - //} + if usetls { + if *cacert != "" { + if cacertFormat.IsNone() { + guessFormat, err := lib.GuessFormatForFile(*cacert, "") + if err != nil { + fail(nil, "Fail to guess file format of -key err: %s", err) + } + cacertFormat.Set(guessFormat) + } + switch cacertFormat { + case lib.CertKeyFormatPEM, lib.CertKeyFormatDER: + // do nothing + default: + fail(nil, "The -cacert-format %s not support.", keyFormat) + } + } + if *cert != "" { + if certFormat.IsNone() { + guessFormat, err := lib.GuessFormatForFile(*cert, "") + if err != nil { + fail(nil, "Fail to guess file format of -cert err: %s", err) + } + certFormat.Set(guessFormat) + } + + switch certFormat { + case lib.CertKeyFormatPEM, lib.CertKeyFormatDER: + if *cert == "" || *key == "" { + fail(nil, "The -cert and -key arguments must be used together and both be present.") + } + case lib.CertKeyFormatPKCS12: + // do nothing + default: + fail(nil, "The -cert-format %s not support.", certFormat) + } + } + if *key != "" { + if keyFormat.IsNone() { + guessFormat, err := lib.GuessFormatForFile(*key, "") + if err != nil { + fail(nil, "Fail to guess file format of -key err: %s", err) + } + keyFormat.Set(guessFormat) + } + switch keyFormat { + case lib.CertKeyFormatPEM, lib.CertKeyFormatDER: + if *cert == "" || *key == "" { + fail(nil, "The -cert and -key arguments must be used together and both be present.") + } + default: + fail(nil, "The -key-format %s not support.", keyFormat) + } + } + + } if *altsHandshakerServiceAddress != "" && !*usealts { fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.") @@ -482,7 +518,7 @@ func main() { } creds = alts.NewClientCreds(clientOptions) } else if usetls { - tlsConf, err := grpcurl.ClientTLSConfigV2(*insecure, *cacert, *cert, *key, certType, *pass) + tlsConf, err := lib.ClientTLSConfigV2(*insecure, *cacert, cacertFormat, *cert, certFormat, *key, keyFormat, *pass) if err != nil { fail(err, "Failed to create TLS config") } diff --git a/grpcurl.go b/grpcurl.go index d852cdc..d0ee535 100644 --- a/grpcurl.go +++ b/grpcurl.go @@ -526,78 +526,12 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF return credentials.NewTLS(tlsConf), nil } -type CertificateType int - -const ( - // The certificate file contains PEM encoded data - CertTypePEM CertificateType = 1 - // The certificate file contains PFX data describing PKCS#12. - CertTypeP12 CertificateType = 2 -) - // ClientTLSConfig builds transport-layer config for a gRPC client using the // given properties. If cacertFile is blank, only standard trusted certs are used to // verify the server certs. If clientCertFile is blank, the client will not use a client // certificate. If clientCertFile is not blank then clientKeyFile must not be blank. func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) { - return ClientTLSConfigV2(insecureSkipVerify, cacertFile, clientCertFile, clientKeyFile, CertTypePEM, "") -} - -// ClientTLSConfigV2 builds transport-layer config for a gRPC client using the -// given properties. Support certificate file both PEM and P12. -func ClientTLSConfigV2(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string, clientCertType CertificateType, clientPass string) (*tls.Config, error) { - var tlsConf tls.Config - - if clientCertFile != "" { - // Load the client certificates from disk - clientCertFormat := "" - var pemBuf bytes.Buffer - err := lib.ReadAsPEMEx(clientCertFile, clientCertFormat, clientPass, func(block *pem.Block, format string) error { - return pem.Encode(&pemBuf, block) - }) - if err != nil { - return nil, fmt.Errorf("could not load client cert: %v", err) - } - pemBytes := pemBuf.Bytes() - pemKeyBytes := pemBytes - - if clientKeyFile != "" { - var pemKeyBuf bytes.Buffer - err := lib.ReadAsPEMEx(clientKeyFile, clientCertFormat, clientPass, func(block *pem.Block, format string) error { - return pem.Encode(&pemKeyBuf, block) - }) - if err != nil { - return nil, fmt.Errorf("could not load client key: %v", err) - } - pemKeyBytes = pemKeyBuf.Bytes() - } - - certificate, err := tls.X509KeyPair(pemBytes, pemKeyBytes) - if err != nil { - return nil, fmt.Errorf("could not load client key pair: %v", err) - } - tlsConf.Certificates = []tls.Certificate{certificate} - } - - if insecureSkipVerify { - tlsConf.InsecureSkipVerify = true - } else if cacertFile != "" { - // Create a certificate pool from the certificate authority - certPool := x509.NewCertPool() - ca, err := ioutil.ReadFile(cacertFile) - if err != nil { - return nil, fmt.Errorf("could not read ca certificate: %v", err) - } - - // Append the certificates from the CA - if ok := certPool.AppendCertsFromPEM(ca); !ok { - return nil, errors.New("failed to append ca certs") - } - - tlsConf.RootCAs = certPool - } - - return &tlsConf, nil + return lib.ClientTLSConfigV2(insecureSkipVerify, cacertFile, lib.CertKeyFormatPEM, clientCertFile, lib.CertKeyFormatPEM, clientKeyFile, lib.CertKeyFormatPEM, "") } func inputFiles(fileNames []string) ([]*os.File, error) { diff --git a/internal/certigo/lib/certs.go b/internal/certigo/lib/certs.go index e21e7c1..e7284af 100644 --- a/internal/certigo/lib/certs.go +++ b/internal/certigo/lib/certs.go @@ -22,6 +22,7 @@ import ( "crypto" "crypto/ecdsa" "crypto/rsa" + "crypto/tls" "crypto/x509" "encoding/binary" "encoding/pem" @@ -50,6 +51,7 @@ const ( var fileExtToFormat = map[string]string{ ".pem": "PEM", ".crt": "PEM", + ".cer": "PEM", ".p7b": "PEM", ".p7c": "PEM", ".p12": "PKCS12", @@ -84,28 +86,166 @@ func errorFromErrors(errs []error) error { 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, string) error) error { - var 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()) - } - - err = readCertsFromStream(reader, file.Name(), format, password, callback) - if err != nil { - errs = append(errs, err) - } +func NewCertificateKeyFormat(fileFormat string) CertificateKeyFormat { + fileFormat = strings.ToUpper(fileFormat) + switch fileFormat { + case "": + return CertKeyFormatNONE + case "PEM": + return CertKeyFormatPEM + case "DER": + return CertKeyFormatDER + case "PKCS12", "P12": + return CertKeyFormatPKCS12 + default: + return CertKeyFormatNONE } - return errorFromErrors(errs) } -func ReadAsPEMEx(filename string, format string, password string, callback func(*pem.Block, string) error) error { +type CertificateKeyFormat string + +const ( + CertKeyFormatNONE CertificateKeyFormat = "" + // The file contains plain-text PEM data + CertKeyFormatPEM CertificateKeyFormat = "PEM" + // The file contains X.509 DER encoded data + CertKeyFormatDER CertificateKeyFormat = "DER" + // The file contains JCEKS keystores + CertKeyFormatJCEKS CertificateKeyFormat = "JCEKS" + // The file contains PFX data describing PKCS#12 + CertKeyFormatPKCS12 CertificateKeyFormat = "PKCS12" +) + +func (f *CertificateKeyFormat) Set(fileFormat string) { + *f = NewCertificateKeyFormat(fileFormat) +} + +func (f CertificateKeyFormat) IsNone() bool { + return f == CertKeyFormatNONE +} + +func (f *CertificateKeyFormat) SetPEM() { + *f = CertKeyFormatPEM +} + +func (f CertificateKeyFormat) IsPEM() bool { + return f == CertKeyFormatPEM +} + +func (f CertificateKeyFormat) IsDER() bool { + return f == CertKeyFormatDER +} + +func (f CertificateKeyFormat) IsPKCS12() bool { + return f == CertKeyFormatPKCS12 +} + +// ClientTLSConfigV2 builds transport-layer config for a gRPC client using the +// given properties. Support certificate file both PEM and P12. +func ClientTLSConfigV2(insecureSkipVerify bool, cacertFile string, cacertFormat CertificateKeyFormat, clientCertFile string, certFormat CertificateKeyFormat, clientKeyFile string, keyFormat CertificateKeyFormat, clientPass string) (*tls.Config, error) { + var tlsConf tls.Config + + if clientCertFile != "" { + // Load the client certificates + pemCertBytes, err := readAsPEMEx2(clientCertFile, string(certFormat), clientPass) + if err != nil { + return nil, fmt.Errorf("could not load client cert: %v", err) + } + pemKeyBytes := pemCertBytes // allow clientCertFile include both certificate and key file (JCEKS/PKCS12/PEM) + + // Load the client key + if clientKeyFile != "" { + pemBytes, err := readAsPEMEx2(clientKeyFile, string(keyFormat), clientPass) + if err != nil { + return nil, fmt.Errorf("could not load client key: %v", err) + } + pemKeyBytes = pemBytes + } + + // Load tls.Certificate + certificate, err := tls.X509KeyPair(pemCertBytes, pemKeyBytes) + if err != nil { + return nil, fmt.Errorf("could not load client key pair: %v", err) + } + tlsConf.Certificates = []tls.Certificate{certificate} + } + + if insecureSkipVerify { + tlsConf.InsecureSkipVerify = true + } else if cacertFile != "" { + // Create a certificate pool from the certificate authority + certPool := x509.NewCertPool() + pemCACertBytes, err := readAsPEMEx2(cacertFile, string(cacertFormat), "") + if err != nil { + return nil, fmt.Errorf("could not load cacert : %v", err) + } + + // Append the certificates from the CA + if ok := certPool.AppendCertsFromPEM(pemCACertBytes); !ok { + return nil, errors.New("failed to append ca certs") + } + + tlsConf.RootCAs = certPool + } + + return &tlsConf, nil +} + +func GuessFormatForFile(filename, format string) (string, error) { + // Second, attempt to guess based on extension + guess, ok := fileExtToFormat[strings.ToLower(filepath.Ext(filename))] + if ok { + return guess, nil + } + + file, err := os.Open(filename) + if err != nil { + return "", fmt.Errorf("unable to open file: %s\n", err) + } + defer file.Close() + reader := bufio.NewReaderSize(file, 4) + + // Third, attempt to guess based on first 4 bytes of input + data, err := reader.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...) + // TODO start with 'Certificate' + 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 "", nil +} + +func readAsPEMEx2(filename string, format string, password string) ([]byte, error) { + var pembuf bytes.Buffer + err := readAsPEMEx(filename, format, "", func(block *pem.Block, format string) error { + return pem.Encode(&pembuf, block) + }) + if err != nil { + return nil, fmt.Errorf("could not load client cert: %v", err) + } + return pembuf.Bytes(), nil +} + +func readAsPEMEx(filename string, format string, password string, callback func(*pem.Block, string) error) error { rawFile, err := os.Open(filename) if err != nil { return fmt.Errorf("unable to open file: %s\n", err) @@ -114,9 +254,39 @@ func ReadAsPEMEx(filename string, format string, password string, callback func( passwordFunc := func(promet string) string { return password } - return ReadAsPEM([]io.Reader{rawFile}, format, passwordFunc, callback) + + reader := bufio.NewReaderSize(rawFile, 4) + format, err = formatForFile(reader, "", format) + if err != nil { + return fmt.Errorf("unable to guess format for input stream") + } + + return readCertsFromStream(reader, "", format, passwordFunc, callback) } +// // 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, string) error) error { +// var 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()) +// } +// +// err = readCertsFromStream(reader, file.Name(), format, password, callback) +// if err != nil { +// errs = append(errs, err) +// } +// } +// return errorFromErrors(errs) +// } +// + // 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 @@ -138,76 +308,76 @@ func ReadAsPEM(readers []io.Reader, format string, password func(string) string, 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, string, error) 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, try adding --format flag", file.Name()) - } - - err = readCertsFromStream(reader, file.Name(), format, password, pemToX509(callback)) - if err != nil { - errs = append(errs, err) - } - } - 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, string, error) error) error { - errs := []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") - } - - err = readCertsFromStream(reader, "", format, password, pemToX509(callback)) - if err != nil { - errs = append(errs, err) - } - } - return errorFromErrors(errs) -} - -func pemToX509(callback func(*x509.Certificate, string, error) error) func(*pem.Block, string) error { - return func(block *pem.Block, format string) error { - switch block.Type { - case "CERTIFICATE": - cert, err := x509.ParseCertificate(block.Bytes) - return callback(cert, format, err) - case "PKCS7": - certs, err := pkcs7.ExtractCertificates(block.Bytes) - if err == nil { - for _, cert := range certs { - return callback(cert, format, nil) - } - } else { - return callback(nil, format, err) - } - case "CERTIFICATE REQUEST": - fmt.Println("warning: certificate requests are not supported") - } - return nil - } -} - -func ReadCertsFromStream(reader io.Reader, filename string, format string, password string, callback func(*pem.Block, string) error) error { - passwordFunc := func(promet string) string { - return password - } - return readCertsFromStream(reader, filename, format, passwordFunc, callback) -} +//// 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, string, error) 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, try adding --format flag", file.Name()) +// } +// +// err = readCertsFromStream(reader, file.Name(), format, password, pemToX509(callback)) +// if err != nil { +// errs = append(errs, err) +// } +// } +// 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, string, error) error) error { +// errs := []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") +// } +// +// err = readCertsFromStream(reader, "", format, password, pemToX509(callback)) +// if err != nil { +// errs = append(errs, err) +// } +// } +// return errorFromErrors(errs) +//} +// +//func pemToX509(callback func(*x509.Certificate, string, error) error) func(*pem.Block, string) error { +// return func(block *pem.Block, format string) error { +// switch block.Type { +// case "CERTIFICATE": +// cert, err := x509.ParseCertificate(block.Bytes) +// return callback(cert, format, err) +// case "PKCS7": +// certs, err := pkcs7.ExtractCertificates(block.Bytes) +// if err == nil { +// for _, cert := range certs { +// return callback(cert, format, nil) +// } +// } else { +// return callback(nil, format, err) +// } +// case "CERTIFICATE REQUEST": +// fmt.Println("warning: certificate requests are not supported") +// } +// return nil +// } +//} +// +//func ReadCertsFromStream(reader io.Reader, filename string, format string, password string, callback func(*pem.Block, string) error) error { +// passwordFunc := func(promet string) string { +// return password +// } +// return readCertsFromStream(reader, filename, format, passwordFunc, callback) +//} // 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, string) error) error { @@ -237,7 +407,7 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw x509Certs, err0 := x509.ParseCertificates(data) if err0 == nil { for _, cert := range x509Certs { - err := callback(EncodeX509ToPEM(cert, headers), format) + err := callback(encodeX509ToPEM(cert, headers), format) if err != nil { return err } @@ -279,7 +449,7 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw } for _, alias := range keyStore.ListCerts() { cert, _ := keyStore.GetCert(alias) - err := callback(EncodeX509ToPEM(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})), format) + err := callback(encodeX509ToPEM(cert, mergeHeaders(headers, map[string]string{nameHeader: alias})), format) if err != nil { return err } @@ -302,7 +472,7 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw } for _, cert := range certs { - if err = callback(EncodeX509ToPEM(cert, mergedHeaders), format); err != nil { + if err = callback(encodeX509ToPEM(cert, mergedHeaders), format); err != nil { return err } } @@ -323,8 +493,8 @@ func mergeHeaders(baseHeaders, extraHeaders map[string]string) (headers map[stri return } -// EncodeX509ToPEM converts an X.509 certificate into a PEM block for output. -func EncodeX509ToPEM(cert *x509.Certificate, headers map[string]string) *pem.Block { +// 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, @@ -392,6 +562,7 @@ func formatForFile(file *bufio.Reader, filename, format string) (string, error) } if magic == 0x2D2D2D2D || magic == 0x434f4e4e { // Starts with '----' or 'CONN' (what s_client prints...) + // TODO start with 'Certificate' return "PEM", nil } if magic&0xFFFF0000 == 0x30820000 {