Skip to content

Instantly share code, notes, and snippets.

@nathabonfim59
Last active November 23, 2023 06:35
Show Gist options
  • Save nathabonfim59/5ec538d7f89ca582fd7bb218952bd8d0 to your computer and use it in GitHub Desktop.
Save nathabonfim59/5ec538d7f89ca582fd7bb218952bd8d0 to your computer and use it in GitHub Desktop.
How to parse pkcs11 certificate using golang

How to parse pkcs11 (PFX) certificate using golang

The error

If you're getting the error in golang when using the crypto/pkcs11 package, here is what you need to do:

Error decoding pfx: pkcs12: error reading P12 data: asn1: syntax error: indefinite length found (not DER)

Here is a good explanation of why this error happens. Steps to fix it below.

Converting certificate to a fixed length

#!/bin/bash
# Script to convert old certificates (<2048 bits) 
# to the new format compatible with OpenSSL 3.0+
# REF: https://stackoverflow.com/a/72600724/8313069

# Displays help
show_help() {
  echo "Usage: $0 -i input_file -o output_file"
  echo "  -i input file in .pfx or .p12 format"  
  echo "  -o output converted file"
  echo "Requires OpenSSL 3.0+"
  exit 1
}

# Reads parameters
while getopts ":i:o:" opt; do
  case $opt in
    i) infile="$OPTARG"
    ;;
    o) outfile="$OPTARG"
    ;;
    \?) show_help
    ;;
  esac
done

# Checks if input and output informed
if [ -z "$infile" ] || [ -z "$outfile" ]; then
  echo "Error: it is necessary to inform input and output file"
  show_help
fi

# Checks OpenSSL version
openssl_version=$(openssl version | grep -oP 'OpenSSL \K\d+\.\d+\.\d+' | head -n1)
if dpkg --compare-versions "$openssl_version" "lt" "3.0"; then
  echo "OpenSSL version $openssl_version not supported, requires 3.0+"
  exit 1
fi

# Converts the certificate
intermediate=$(mktemp)
openssl pkcs12 -in "$infile" -nodes -provider legacy -provider default > "$intermediate"
openssl pkcs12 -in "$intermediate" -export -out "$outfile"
rm "$intermediate"

echo "Certificate successfully converted to $outfile"

Further errors

Using the code below as reference. The default crypt library does not support the algorithm correctly. You'll get an error like:

INFO[0000] Error decoding pfx: pkcs12: unknown digest algorithm: 2.16.840.1.101.3.4.2.1
package main

import (
	"io/ioutil"
	log "github.com/sirupsen/logrus"
	"golang.org/x/crypto/pkcs12"
)

func main() {
	pfxBytes, err := ioutil.ReadFile("/tmp/cert.pfx")

	if err != nil {
		log.Info("Error reading file: ", err)
	}

	log.Info("File content: ", string(pfxBytes))

	pfxPrivateKey, pfxCertificate, err := pkcs12.Decode(pfxBytes, "")

	log.Info("PFX private key: ", pfxPrivateKey)
	log.Info("PFX certificate: ", pfxCertificate)

	if err != nil {
		log.Info("Error decoding pfx: ", err)
	}
}

To fix this, you'll need to use a third party library that supports it. The best and most well-maitained I've found is software.sslmate.com/src/go-pkcs12.

Final code + some tweaks

Here I've fixed the code and added some tweaks like password verification.

package main

import (
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"software.sslmate.com/src/go-pkcs12"
)

func main() {
	pfxBytes, err := ioutil.ReadFile("/tmp/cert.pfx")

	if err != nil {
		log.Info("Error reading file: ", err)
	}

	log.Info("File content: ", string(pfxBytes))

	pfxPrivateKey, pfxCertificate, err := pkcs12.Decode(pfxBytes, "")

	log.Info("PFX private key: ", pfxPrivateKey)
	log.Info("PFX certificate: ", pfxCertificate)

	if err != nil {
		log.Info("Error decoding pfx: ", err)

		var isPasswordError bool = false
		isPasswordError = err == pkcs12.ErrIncorrectPassword

		log.Info("Is password error: ", isPasswordError)
	}
}

Personal note

I've spent wayy too much time on this. Diagnosing the problem and trying to figure out how it all worked.

The code I've posted here is MIT. Feel free to use it however you want.

If you have a contribution or comment, put it down below, I'll update the gist as soon as I have time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment