tidy code not finished yet

This commit is contained in:
wangtiga 2023-11-09 23:29:44 +08:00
parent 8060fffba3
commit 6f4f86da79
3 changed files with 335 additions and 194 deletions

View File

@ -14,6 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/fullstorydev/grpcurl/internal/certigo/lib"
"github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/grpcreflect" "github.com/jhump/protoreflect/grpcreflect"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -64,18 +65,21 @@ var (
cacert = flags.String("cacert", "", prettify(` cacert = flags.String("cacert", "", prettify(`
File containing trusted root certificates for verifying the server. File containing trusted root certificates for verifying the server.
Ignored if -insecure is specified.`)) 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(` cert = flags.String("cert", "", prettify(`
File containing client certificate (public key), to present to the File containing client certificate (public key), to present to the
server. Not valid with -plaintext option. Must also provide -key option server. Not valid with -plaintext option. Must also provide -key option
when use PEM certificate file.`)) when use PEM/DER certificate file.`))
certTypeString = flags.String("cert-type", "", prettify(` pCertFormat = flags.String("cert-format", string(lib.CertKeyFormatPEM), prettify(`
Client certificate file type. (PEM/P12)`)) cert Format of given input (PEM, DER, PKCS12; heuristic if missing).`))
certType = grpcurl.CertTypePEM pass = flags.String("pass", "", prettify(`
pass = flags.String("pass", "", prettify(`
Pass phrase for the key`)) Pass phrase for the key`))
key = flags.String("key", "", prettify(` key = flags.String("key", "", prettify(`
File containing client private key, to present to the server. Not valid File containing client private key, to present to the server. Not valid
with -plaintext option. Must also provide -cert option.`)) 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 // ALTS Options
usealts = flags.Bool("alts", false, prettify(` usealts = flags.Bool("alts", false, prettify(`
@ -294,17 +298,9 @@ func main() {
// default behavior is to use tls // default behavior is to use tls
usetls := !*plaintext && !*usealts usetls := !*plaintext && !*usealts
cacertFormat := lib.NewCertificateKeyFormat(*pCACertFormat)
//// converto to CertificateFileType certFormat := lib.NewCertificateKeyFormat(*pCertFormat)
//if len(*certTypeString) == 0 { keyFormat := lib.NewCertificateKeyFormat(*pKeyFormat)
// 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.")
//}
// Do extra validation on arguments and figure out what user asked us to do. // Do extra validation on arguments and figure out what user asked us to do.
if *connectTimeout < 0 { if *connectTimeout < 0 {
@ -332,21 +328,61 @@ func main() {
fail(nil, "The -key argument can only be used with TLS.") fail(nil, "The -key argument can only be used with TLS.")
} }
//switch certType { if usetls {
//case grpcurl.CertTypePEM: if *cacert != "" {
// if (*key == "") != (*cert == "") { if cacertFormat.IsNone() {
// fail(nil, "The -cert and -key arguments must be used together and both be present when -cert-type is PEM.") guessFormat, err := lib.GuessFormatForFile(*cacert, "")
// } if err != nil {
//case grpcurl.CertTypeP12: fail(nil, "Fail to guess file format of -key err: %s", err)
// if *key != "" { }
// fail(nil, "The -key arguments must not be used when -cert-type is P12.") cacertFormat.Set(guessFormat)
// } }
// if *cert == "" { switch cacertFormat {
// fail(nil, "The -cert arguments must be used when -cert-type is P12.") case lib.CertKeyFormatPEM, lib.CertKeyFormatDER:
// } // do nothing
//default: default:
// fail(nil, "Not support cert type %v.", certType) 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 { if *altsHandshakerServiceAddress != "" && !*usealts {
fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.") fail(nil, "The -alts-handshaker-service argument must be used with the -alts argument.")
@ -482,7 +518,7 @@ func main() {
} }
creds = alts.NewClientCreds(clientOptions) creds = alts.NewClientCreds(clientOptions)
} else if usetls { } 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 { if err != nil {
fail(err, "Failed to create TLS config") fail(err, "Failed to create TLS config")
} }

View File

@ -526,78 +526,12 @@ func ClientTransportCredentials(insecureSkipVerify bool, cacertFile, clientCertF
return credentials.NewTLS(tlsConf), nil 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 // ClientTLSConfig builds transport-layer config for a gRPC client using the
// given properties. If cacertFile is blank, only standard trusted certs are used to // 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 // 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. // certificate. If clientCertFile is not blank then clientKeyFile must not be blank.
func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) { func ClientTLSConfig(insecureSkipVerify bool, cacertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) {
return ClientTLSConfigV2(insecureSkipVerify, cacertFile, clientCertFile, clientKeyFile, CertTypePEM, "") return lib.ClientTLSConfigV2(insecureSkipVerify, cacertFile, lib.CertKeyFormatPEM, clientCertFile, lib.CertKeyFormatPEM, clientKeyFile, lib.CertKeyFormatPEM, "")
}
// 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
} }
func inputFiles(fileNames []string) ([]*os.File, error) { func inputFiles(fileNames []string) ([]*os.File, error) {

View File

@ -22,6 +22,7 @@ import (
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/binary" "encoding/binary"
"encoding/pem" "encoding/pem"
@ -50,6 +51,7 @@ const (
var fileExtToFormat = map[string]string{ var fileExtToFormat = map[string]string{
".pem": "PEM", ".pem": "PEM",
".crt": "PEM", ".crt": "PEM",
".cer": "PEM",
".p7b": "PEM", ".p7b": "PEM",
".p7c": "PEM", ".p7c": "PEM",
".p12": "PKCS12", ".p12": "PKCS12",
@ -84,28 +86,166 @@ func errorFromErrors(errs []error) error {
return errors.New(buffer.String()) return errors.New(buffer.String())
} }
// ReadAsPEMFromFiles will read PEM blocks from the given set of inputs. Input func NewCertificateKeyFormat(fileFormat string) CertificateKeyFormat {
// data may be in plain-text PEM files, DER-encoded certificates or PKCS7 fileFormat = strings.ToUpper(fileFormat)
// envelopes, or PKCS12/JCEKS keystores. All inputs will be converted to PEM switch fileFormat {
// blocks and passed to the callback. case "":
func ReadAsPEMFromFiles(files []*os.File, format string, password func(string) string, callback func(*pem.Block, string) error) error { return CertKeyFormatNONE
var errs []error case "PEM":
for _, file := range files { return CertKeyFormatPEM
reader := bufio.NewReaderSize(file, 4) case "DER":
format, err := formatForFile(reader, file.Name(), format) return CertKeyFormatDER
if err != nil { case "PKCS12", "P12":
return fmt.Errorf("unable to guess file type for file %s", file.Name()) return CertKeyFormatPKCS12
} default:
return CertKeyFormatNONE
err = readCertsFromStream(reader, file.Name(), format, password, callback)
if err != nil {
errs = append(errs, err)
}
} }
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) rawFile, err := os.Open(filename)
if err != nil { if err != nil {
return fmt.Errorf("unable to open file: %s\n", err) 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 { passwordFunc := func(promet string) string {
return password 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 // 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 // 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 // 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) return errorFromErrors(errs)
} }
// ReadAsX509FromFiles will read X.509 certificates from the given set of //// ReadAsX509FromFiles will read X.509 certificates from the given set of
// inputs. Input data may be in plain-text PEM files, DER-encoded certificates //// 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 //// 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. //// 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 { //func ReadAsX509FromFiles(files []*os.File, format string, password func(string) string, callback func(*x509.Certificate, string, error) error) error {
errs := []error{} // errs := []error{}
for _, file := range files { // for _, file := range files {
reader := bufio.NewReaderSize(file, 4) // reader := bufio.NewReaderSize(file, 4)
format, err := formatForFile(reader, file.Name(), format) // format, err := formatForFile(reader, file.Name(), format)
if err != nil { // if err != nil {
return fmt.Errorf("unable to guess file type for file %s, try adding --format flag", file.Name()) // 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)) // err = readCertsFromStream(reader, file.Name(), format, password, pemToX509(callback))
if err != nil { // if err != nil {
errs = append(errs, err) // errs = append(errs, err)
} // }
} // }
return errorFromErrors(errs) // return errorFromErrors(errs)
} //}
//
// ReadAsX509 will read X.509 certificates from the given set of inputs. Input //// 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 //// 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 //// envelopes, or PKCS12/JCEKS keystores. All inputs will be converted to X.509
// certificates (private keys are skipped) and passed to the callback. //// 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 { //func ReadAsX509(readers []io.Reader, format string, password func(string) string, callback func(*x509.Certificate, string, error) error) error {
errs := []error{} // errs := []error{}
for _, r := range readers { // for _, r := range readers {
reader := bufio.NewReaderSize(r, 4) // reader := bufio.NewReaderSize(r, 4)
format, err := formatForFile(reader, "", format) // format, err := formatForFile(reader, "", format)
if err != nil { // if err != nil {
return fmt.Errorf("unable to guess format for input stream") // return fmt.Errorf("unable to guess format for input stream")
} // }
//
err = readCertsFromStream(reader, "", format, password, pemToX509(callback)) // err = readCertsFromStream(reader, "", format, password, pemToX509(callback))
if err != nil { // if err != nil {
errs = append(errs, err) // errs = append(errs, err)
} // }
} // }
return errorFromErrors(errs) // return errorFromErrors(errs)
} //}
//
func pemToX509(callback func(*x509.Certificate, string, error) error) func(*pem.Block, string) error { //func pemToX509(callback func(*x509.Certificate, string, error) error) func(*pem.Block, string) error {
return func(block *pem.Block, format string) error { // return func(block *pem.Block, format string) error {
switch block.Type { // switch block.Type {
case "CERTIFICATE": // case "CERTIFICATE":
cert, err := x509.ParseCertificate(block.Bytes) // cert, err := x509.ParseCertificate(block.Bytes)
return callback(cert, format, err) // return callback(cert, format, err)
case "PKCS7": // case "PKCS7":
certs, err := pkcs7.ExtractCertificates(block.Bytes) // certs, err := pkcs7.ExtractCertificates(block.Bytes)
if err == nil { // if err == nil {
for _, cert := range certs { // for _, cert := range certs {
return callback(cert, format, nil) // return callback(cert, format, nil)
} // }
} else { // } else {
return callback(nil, format, err) // return callback(nil, format, err)
} // }
case "CERTIFICATE REQUEST": // case "CERTIFICATE REQUEST":
fmt.Println("warning: certificate requests are not supported") // fmt.Println("warning: certificate requests are not supported")
} // }
return nil // return nil
} // }
} //}
//
func ReadCertsFromStream(reader io.Reader, filename string, format string, password string, callback func(*pem.Block, string) error) error { //func ReadCertsFromStream(reader io.Reader, filename string, format string, password string, callback func(*pem.Block, string) error) error {
passwordFunc := func(promet string) string { // passwordFunc := func(promet string) string {
return password // return password
} // }
return readCertsFromStream(reader, filename, format, passwordFunc, callback) // return readCertsFromStream(reader, filename, format, passwordFunc, callback)
} //}
// readCertsFromStream takes some input and converts it to PEM blocks. // 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 { 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) x509Certs, err0 := x509.ParseCertificates(data)
if err0 == nil { if err0 == nil {
for _, cert := range x509Certs { for _, cert := range x509Certs {
err := callback(EncodeX509ToPEM(cert, headers), format) err := callback(encodeX509ToPEM(cert, headers), format)
if err != nil { if err != nil {
return err return err
} }
@ -279,7 +449,7 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw
} }
for _, alias := range keyStore.ListCerts() { for _, alias := range keyStore.ListCerts() {
cert, _ := keyStore.GetCert(alias) 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 { if err != nil {
return err return err
} }
@ -302,7 +472,7 @@ func readCertsFromStream(reader io.Reader, filename string, format string, passw
} }
for _, cert := range certs { for _, cert := range certs {
if err = callback(EncodeX509ToPEM(cert, mergedHeaders), format); err != nil { if err = callback(encodeX509ToPEM(cert, mergedHeaders), format); err != nil {
return err return err
} }
} }
@ -323,8 +493,8 @@ func mergeHeaders(baseHeaders, extraHeaders map[string]string) (headers map[stri
return return
} }
// EncodeX509ToPEM converts an X.509 certificate into a PEM block for output. // encodeX509ToPEM converts an X.509 certificate into a PEM block for output.
func EncodeX509ToPEM(cert *x509.Certificate, headers map[string]string) *pem.Block { func encodeX509ToPEM(cert *x509.Certificate, headers map[string]string) *pem.Block {
return &pem.Block{ return &pem.Block{
Type: "CERTIFICATE", Type: "CERTIFICATE",
Bytes: cert.Raw, Bytes: cert.Raw,
@ -392,6 +562,7 @@ func formatForFile(file *bufio.Reader, filename, format string) (string, error)
} }
if magic == 0x2D2D2D2D || magic == 0x434f4e4e { if magic == 0x2D2D2D2D || magic == 0x434f4e4e {
// Starts with '----' or 'CONN' (what s_client prints...) // Starts with '----' or 'CONN' (what s_client prints...)
// TODO start with 'Certificate'
return "PEM", nil return "PEM", nil
} }
if magic&0xFFFF0000 == 0x30820000 { if magic&0xFFFF0000 == 0x30820000 {