Skip to content

Commit

Permalink
Add parsing for Certificate Signing Request
Browse files Browse the repository at this point in the history
Add support for parsing a CSR in the --dump flag using a --csr flag.
This flag would only read the Certificate Signing Requests in the file.
  • Loading branch information
prateeknischal committed Jan 25, 2020
1 parent 92fe6ad commit 5de2475
Show file tree
Hide file tree
Showing 8 changed files with 427 additions and 48 deletions.
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Certigo has commands to dump certificates and keystores from a file, to connect
```
usage: certigo [<flags>] <command> [<args> ...]
A command line certificate examination utility.
A command-line utility to examine and validate certificates to help with debugging SSL/TLS issues.
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
Expand All @@ -55,25 +55,29 @@ Commands:
dump [<flags>] [<file>...]
Display information about a certificate from a file/stdin.
Display information about a certificate from a file or stdin.
-f, --format=FORMAT Format of given input (PEM, DER, JCEKS, PKCS12; heuristic if missing).
-p, --password=PASSWORD Password for PKCS12/JCEKS key stores (reads from TTY if missing).
-m, --pem Write output as PEM blocks instead of human-readable format.
-j, --json Write output as machine-readable JSON format.
-d, --depth=0 Certificate chain information upto a certain depth.
-c, --csr Parse only Certificate Signing Request(s) in the file(s).
connect [<flags>] [<server:port>]
connect [<flags>] [<server[:port]>]
Connect to a server and print its certificate(s).
-n, --name=NAME Override the server name used for Server Name Indication (SNI).
--ca=CA Path to CA bundle (system default if unspecified).
--cert=CERT Client certificate chain for connecting to server (PEM).
--key=KEY Private key for client certificate, if not in same file (PEM).
-t, --start-tls=PROTOCOL Enable StartTLS protocol ('ldap', 'mysql', 'postgres', 'smtp' or 'ftp').
-t, --start-tls=PROTOCOL Enable StartTLS protocol; one of: [mysql postgres psql smtp ldap ftp imap].
--identity="certigo" With --start-tls, sets the DB user or SMTP EHLO name
--proxy=PROXY Optional URI for HTTP(s) CONNECT proxy to dial connections with
--timeout=5s Timeout for connecting to remote server (can be '5m', '1s', etc).
-m, --pem Write output as PEM blocks instead of human-readable format.
-j, --json Write output as machine-readable JSON format.
--verify Verify certificate chain.
-d, --depth=0 Certificate chain information upto a certain depth.
verify --name=NAME [<flags>] [<file>]
Expand All @@ -88,7 +92,7 @@ Commands:

### Examples

Display information about a certificate (also supports `--pem` and `--json` output):
Display information about a certificate (also supports `--pem`, `--depth` and `--json` output):

```
$ certigo dump --verbose squareup-2016.crt
Expand Down Expand Up @@ -127,6 +131,25 @@ Alternate DNS Names:
www.gosq.co
```

Display information about a certificate signing request (also supports `--depth` and `--json` output)

```
$ certigo dump --csr test.csr --verbose
** CERTIFICATE REQUEST 1 **
Signature: ECDSA-SHA256
Subject Info:
Country: IN
Province: KA
Locality: Bangalore
Organization: Certigo
Organizational Unit: InfoSec
CommonName: test.certigo.com
Email Address: a@b.c
Warnings:
Certificate Request is not in X509v3 format (version is 0)
Certificate Request doesn't have any valid DNS/URI names or IP addresses set
```

Display & validate certificates from a remote server (also supports `--start-tls`):

```
Expand Down
9 changes: 9 additions & 0 deletions lib/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ func EncodeX509ToPEM(cert *x509.Certificate, headers map[string]string) *pem.Blo
}
}

// EncodeX509CSRToPEM converts an X.509 certificate request to a PEM block for output
func EncodeX509CSRToPEM(csr *x509.CertificateRequest, headers map[string]string) *pem.Block {
return &pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr.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{
Expand Down
153 changes: 124 additions & 29 deletions lib/display.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,62 @@ Email Addresses:
Warnings:{{range .Warnings}}
{{. | redify}}{{end}}{{end}}`

var csrLayout = `
{{- if .Version}}{{.Version}}
{{end -}}
Subject:
{{wrapWith .Width "\n\t" (.Subject.Name | printShortName)}}
{{- if .AltDNSNames}}
DNS Names:
{{wrapWith .Width "\n\t" (join ", " .AltDNSNames)}}{{end}}
{{- if .AltIPAddresses}}
{{wrapWith .Width "\n\t" (join ", " .AltIPAdresses)}}{{end}}
{{- if .URINames}}
URI Names:
{{wrapWith .Width "\n\t" (join ", " .URINames)}}{{end}}
{{- if .EmailAddresses}}
Email Addresses:
{{wrapWith .Width "\n\t" (join ", " .EmailAddresses)}}{{end}}
{{- if .Warnings}}
Warnings:{{range .Warnings}}
{{. | redify}}{{end}}{{end}}`

var verboseCSRLayout = `
{{- define "PkixName" -}}
{{- range .Names}}
{{ .Type | oidName }}: {{ .Value }}
{{- end -}}
{{end -}}
Signature: {{.SignatureAlgorithm | highlightAlgorithm}}
Subject Info:
{{- template "PkixName" .Subject.Name}}
{{- if .AltDNSNames}}
{{- range .Extensions}}
{{ .Id | oidName}}: {{.Value}}
{{- end -}}
DNS Names:
{{wrapWith .Width "\n\t" (join ", " .AltDNSNames)}}
{{- end}}
{{- if .AltIPAddresses}}
IP Addresses:
{{wrapWith .Width "\n\t" (join ", " .AltIPAddresses)}}
{{- end}}
{{- if .URINames}}
URI Names:
{{wrapWith .Width "\n\t" (join ", " .URINames)}}
{{- end}}
{{- if .EmailAddresses}}
Email Addresses:
{{wrapWith .Width "\n\t" (join ", " .EmailAddresses)}}
{{- end}}
{{- if .Warnings}}
Warnings:
{{- range .Warnings}}
{{. | redify}}
{{- end}}
{{- end}}`

type certWithName struct {
name string
file string
Expand Down Expand Up @@ -200,52 +256,91 @@ func EncodeX509ToObject(cert *x509.Certificate) interface{} {
}

// EncodeX509ToText encodes an X.509 certificate into human-readable text.
func EncodeX509ToText(cert *x509.Certificate, terminalWidth int, verbose bool) []byte {
c := createSimpleCertificate("", cert)
c.Width = terminalWidth - 8 /* Need some margin for tab */

return displayCert(c, verbose)
func EncodeX509ToText(obj interface{}, terminalWidth int, verbose bool) []byte {
switch obj.(type) {
case *x509.Certificate:
obj, ok := obj.(*x509.Certificate)
if ok {
c := createSimpleCertificate("", obj)
c.Width = terminalWidth - 8 /* Need some margin for tab */
return displayCert(c, verbose)
}
case *x509.CertificateRequest:
obj, ok := obj.(*x509.CertificateRequest)
if ok {
c := createSimpleCertificateRequest("", obj)
c.Width = terminalWidth - 8 /* Need some margin for tab */
return displayCert(c, verbose)
}
}
// should never happen
return nil
}

// 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, verbose bool) []byte {
func displayCert(obj interface{}, verbose bool) []byte {
// Use template functions from sprig, but add some extras
funcMap := sprig.TxtFuncMap()
var t *template.Template
var err error

extras := template.FuncMap{
"certStart": certStart,
"certEnd": certEnd,
"redify": redify,
"highlightAlgorithm": highlightAlgorithm,
"hexify": hexify,
"keyUsage": keyUsage,
"extKeyUsage": extKeyUsage,
"oidName": oidName,
"oidShort": oidShort,
"printShortName": PrintShortName,
"printCommonName": PrintCommonName,
}
for k, v := range extras {
funcMap[k] = v
}
switch obj.(type) {
case simpleCertificate:
extras := template.FuncMap{
"certStart": certStart,
"certEnd": certEnd,
"redify": redify,
"highlightAlgorithm": highlightAlgorithm,
"hexify": hexify,
"keyUsage": keyUsage,
"extKeyUsage": extKeyUsage,
"oidName": oidName,
"oidShort": oidShort,
"printShortName": PrintShortName,
"printCommonName": PrintCommonName,
}
for k, v := range extras {
funcMap[k] = v
}

t := template.New("Cert template").Funcs(funcMap)
var err error
if verbose {
t, err = t.Parse(verboseLayout)
} else {
t, err = t.Parse(layout)
t = template.New("Cert template").Funcs(funcMap)
if verbose {
t, err = t.Parse(verboseLayout)
} else {
t, err = t.Parse(layout)
}
case simpleCertificateRequest:
extras := template.FuncMap{
"redify": redify,
"highlightAlgorithm": highlightAlgorithm,
"hexify": hexify,
"oidName": oidName,
"oidShort": oidShort,
"printShortName": PrintShortName,
"printCommonName": PrintCommonName,
}
for k, v := range extras {
funcMap[k] = v
}

t = template.New("Cert Request template").Funcs(funcMap)
if verbose {
t, err = t.Parse(verboseCSRLayout)
} else {
t, err = t.Parse(csrLayout)
}
}

if err != nil {
// Should never happen
panic(err)
}
var buffer bytes.Buffer
w := bufio.NewWriter(&buffer)
err = t.Execute(w, cert)
err = t.Execute(w, obj)
if err != nil {
// Should never happen
panic(err)
Expand Down
Loading

0 comments on commit 5de2475

Please sign in to comment.