Skip to content

Commit

Permalink
added ability to process a PEM file via stdin (pipe)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreburgaud committed Dec 23, 2023
1 parent 8cb8039 commit 249c1f5
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 10 deletions.
41 changes: 38 additions & 3 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package cli

import (
"bufio"
"encoding/json"
"errors"
"flag"
"fmt"
"net"
"os"
"path/filepath"
"strings"

"showcert/internal/cert"
"showcert/internal/client"
Expand Down Expand Up @@ -77,7 +79,7 @@ func isFile(f string) bool {
const options = `
-h, --help Displays this help
-V, --version Displays application version
-f, --file <cert_file> Parses a local certificate file (PEM format)
-f, --file <cert_file> Parses a local certificate file (PEM format)
-v, --verify Requires certificate chain verification
--host <host:[port]> Parses a remote certificate for a given host
--cafile <PEM_file> Loads CAs from a PEM file
Expand Down Expand Up @@ -177,6 +179,7 @@ func ParseOptions() *Command {
// Execute the command from the properties of Command
func (cmd Command) Execute() error {

// Read local PEM file
if len(cmd.file) > 0 {
err := showLocalCert(cmd.file)
if err != nil {
Expand All @@ -185,15 +188,21 @@ func (cmd Command) Execute() error {
return nil
}

// Retreive a remote cert
if len(cmd.host) > 0 {
err := showRemoteCerts(cmd.verify, cmd.host, cmd.port, cmd.cafile, cmd.cadir)
if err != nil {
return err
}
return nil
}
_, _ = fmt.Fprintln(os.Stderr, "no option or argument provided")
Usage()

// read PEM from stdin
err := showStdinCert()
if err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -236,6 +245,32 @@ func showLocalCert(certFile string) error {
return nil
}

// showStdinCert trigger the command to parse stdin (assuming a PEM format) and printing human readable certs
func showStdinCert() error {
var data []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
data = append(data, scanner.Text())
}

if err := scanner.Err(); err != nil {
return err
}

certs, err := client.GetCertsFromBytes([]byte(strings.Join(data, "\n")))
if err != nil {
return err
}
chain := cert.ParseCertificates(certs, 1, 0)

j, err := buildJsonChain(chain)
if err != nil {
return err
}
fmt.Println(j)
return nil
}

// buildJsonChain creates a JSON string from one chain of certificates (one or more certificates)
func buildJsonChain(chain *cert.CertificateChain) (string, error) {
buf, err := json.MarshalIndent(chain, "", " ")
Expand Down
16 changes: 10 additions & 6 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,26 @@ func GetRemoteCerts(verify bool, host, port, cafile, cadir string) (Response, er

// GetLocalCerts builds and return an array of SSL Certificates given a valid cert file (PEM format)
func GetLocalCerts(certFile string) ([]*x509.Certificate, error) {
var certs []*x509.Certificate
// Open and read PEM file
data, err := os.ReadFile(certFile)
if err != nil {
return nil, err
}

return GetCertsFromBytes(data)
}

func GetCertsFromBytes(data []byte) ([]*x509.Certificate, error) {
var certs []*x509.Certificate

// Remove any prefix or trailing new lines
data = bytes.TrimLeft(data, "\n")
data = bytes.TrimRight(data, "\n")

if err != nil {
return nil, err
}
for {
block, rest := pem.Decode(data)
if block == nil || block.Type != "CERTIFICATE" {
_, _ = fmt.Printf("rest of PEM file content: %x", rest)
return nil, fmt.Errorf("bad PEM file %s", certFile)
return nil, fmt.Errorf("bad PEM format")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION := "0.10.1"
VERSION := "0.11.0"
APP := "showcert"
DOCKER_IMAGE := "andreburgaud" / APP
BUILD_DIR := "build"
Expand Down

0 comments on commit 249c1f5

Please sign in to comment.