pkcs12

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 16, 2026 License: BSD-3-Clause Imports: 26 Imported by: 0

README

package pkcs12

Documentation

import "github.com/ggpslop/go-pkcs12"

This package is forked from software.sslmate.com/src/go-pkcs12, which doesn't support a very flexible PKCS12 API. It implements exactly the same API as software.sslmate.com/src/go-pkcs12, but adds a Builder that allows the construction of an arbitrarily complex PKCS12 (multiple PrivateKeyEntries + multiple TrustedCertificateEntry + friendly names / aliases).

Note that only DER-encoded PKCS#12 files are supported, even though PKCS#12 allows BER encoding. This is because encoding/asn1 only supports DER.

Import Path

Note that although the source code and issue tracker for this package are hosted on GitHub, the import path is:

github.com/ggpslop/go-pkcs12 

Please be sure to use this path when you go get and import this package.

Example

import "github.com/ggpslop/go-pkcs12"

var password = []byte("password")

// create the pkcs12 new builder. Options object is optional.
var builder, err = pkcs12.NewBuilder(pkcs12.Modern2023, password, pkcs12.Options{
    orderFirstTrusts: true,
    prvKeyEntryLen:   2,
    trustCertLen:     1,
})
if err != nil {
    panic(err)
}

// add first private key / certificate pair.
var err = builder.SetPrivateKeyEntry(
    "first_key_cert_pair",
    privateKey1,
    leafCertificate1,
    chain1,
    nil,
)
if err != nil {
    panic(err)
}

// add second private key / certificate pair.
err = builder.SetPrivateKeyEntry(
    "second_key_cert_pair",
    privateKey2,
    leafCertificate2,
    nil,
    []byte("specificPrivateKeyPassword"),
)
if err != nil {
    panic(err)
}

// add the only trusted certificate.
err = builder.SetTrustedCertificateEntry("only_trust", trustCertificate)
if err != nil {
    panic(err)
}

// build all in memory.
pfx, err := builder.Build()
if err != nil {
    panic(err)
}

// use pfx

Report Issues / Send Patches

Open an issue or PR at https://github.com/ggpslop/go-pkcs12

Documentation

Overview

Package pkcs12. This package is forked from software.sslmate.com/src/go-pkcs12, which doesn't support a very flexible PKCS12 API.

Index

Constants

View Source
const DefaultPassword = "changeit"

DefaultPassword is the string "changeit", a commonly-used password for PKCS#12 files.

Variables

View Source
var (
	// ErrDecryption represents a failure to decrypt the input.
	ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding")

	// ErrIncorrectPassword is returned when an incorrect password is detected.
	// Usually, P12/PFX data is signed to be able to verify the password.
	ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect")
)
View Source
var Legacy = LegacyDES

Legacy encodes PKCS#12 files using weak, legacy parameters that work in a wide variety of software.

Currently, this encoder is the same as LegacyDES, but this may change in the future if another encoder is found to provide better compatibility.

Due to the weak encryption, it is STRONGLY RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files using other means.

View Source
var LegacyDES = &Encoder{
	macAlgorithm:         oidSHA1,
	certAlgorithm:        oidPBEWithSHAAnd3KeyTripleDESCBC,
	keyAlgorithm:         oidPBEWithSHAAnd3KeyTripleDESCBC,
	macIterations:        1,
	encryptionIterations: 2048,
	saltLen:              8,
	rand:                 rand.Reader,
}

LegacyDES encodes PKCS#12 files using weak algorithms that are supported by a wide variety of software. Certificates and keys are encrypted using PBE with 3DES using keys derived with 2048 iterations of HMAC-SHA-1. MACs use HMAC-SHA-1 with keys derived with 1 iteration of HMAC-SHA-1. These are the same parameters used by OpenSSL's -descert option. As of 2023, this encoder is likely to produce files that can be read by the most software.

Due to the weak encryption, it is STRONGLY RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files using other means. To create more secure PKCS#12 files, use Modern2023.

View Source
var LegacyRC2 = &Encoder{
	macAlgorithm:         oidSHA1,
	certAlgorithm:        oidPBEWithSHAAnd40BitRC2CBC,
	keyAlgorithm:         oidPBEWithSHAAnd3KeyTripleDESCBC,
	macIterations:        1,
	encryptionIterations: 2048,
	saltLen:              8,
	rand:                 rand.Reader,
}

LegacyRC2 encodes PKCS#12 files using weak algorithms that were traditionally used in PKCS#12 files, including those produced by OpenSSL before 3.0.0, go-pkcs12 before 0.3.0, and Java when keystore.pkcs12.legacy is defined. Specifically, certificates are encrypted using PBE with RC2, and keys are encrypted using PBE with 3DES, using keys derived with 2048 iterations of HMAC-SHA-1. MACs use HMAC-SHA-1 with keys derived with 1 iteration of HMAC-SHA-1.

Due to the weak encryption, it is STRONGLY RECOMMENDED that you use DefaultPassword when encoding PKCS#12 files using this encoder, and protect the PKCS#12 files using other means.

By default, OpenSSL 3 can't decode PKCS#12 files created using this encoder. For better compatibility, use LegacyDES. For better security, use Modern2023.

View Source
var Modern = Modern2023

Modern encodes PKCS#12 files using modern, robust parameters.

Currently, this encoder is the same as Modern2023, but this may change in the future to keep up with modern practices.

View Source
var Modern2023 = &Encoder{
	macAlgorithm:         oidSHA256,
	certAlgorithm:        oidPBES2,
	keyAlgorithm:         oidPBES2,
	macIterations:        2048,
	encryptionIterations: 2048,
	saltLen:              16,
	rand:                 rand.Reader,
}

Modern2023 encodes PKCS#12 files using algorithms that are considered modern as of 2023. Private keys and certificates are encrypted using PBES2 with PBKDF2-HMAC-SHA-256 and AES-256-CBC. The MAC algorithm is HMAC-SHA-2. These are the same algorithms used by OpenSSL 3 (by default), Java 20 (by default), and Windows Server 2019 (when "stronger" is used).

Files produced with this encoder can be read by OpenSSL 1.1.1 and higher, Java 12 and higher, and Windows Server 2019 and higher.

For passwords, it is RECOMMENDED that you do one of the following: 1) Use DefaultPassword and protect the file using other means, or 2) Use a high-entropy password, such as one generated with `openssl rand -hex 16`.

You SHOULD NOT use a lower-entropy password with this encoder because the number of KDF iterations is only 2048 and doesn't provide meaningful protection against brute-forcing. You can increase the number of iterations using Encoder.WithIterations, but as https://neilmadden.blog/2023/01/09/on-pbkdf2-iterations/ explains, this doesn't help as much as you think.

View Source
var Modern2026 = &Encoder{
	macAlgorithm:         oidPBMAC1,
	certAlgorithm:        oidPBES2,
	keyAlgorithm:         oidPBES2,
	macIterations:        2048,
	encryptionIterations: 2048,
	saltLen:              16,
	rand:                 rand.Reader,
}

Modern2026 encodes PKCS#12 files using algorithms that are considered modern as of 2026. Private keys and certificates are encrypted using PBES2 with PBKDF2-HMAC-SHA-256 and AES-256-CBC. The MAC algorithm is PBMAC1 with PBKDF2-HMAC-SHA-256 and HMAC-SHA256.

Files produced with this encoder can be read by OpenSSL 3.4.0 and higher and Java 26 and higher.

For passwords, it is RECOMMENDED that you do one of the following: 1) Use DefaultPassword and protect the file using other means, or 2) Use a high-entropy password, such as one generated with `openssl rand -hex 16`.

You SHOULD NOT use a lower-entropy password with this encoder because the number of KDF iterations is only 2048 and doesn't provide meaningful protection against brute-forcing. You can increase the number of iterations using Encoder.WithIterations, but as https://neilmadden.blog/2023/01/09/on-pbkdf2-iterations/ explains, this doesn't help as much as you think.

View Source
var Passwordless = &Encoder{
	macAlgorithm:  nil,
	certAlgorithm: nil,
	keyAlgorithm:  nil,
	rand:          rand.Reader,
}

Passwordless encodes PKCS#12 files without any encryption or MACs. A lot of software has trouble reading such files, so it's probably only useful for creating Java trust stores using Encoder.EncodeTrustStore or Encoder.EncodeTrustStoreEntries.

When using this encoder, you MUST specify an empty password.

Functions

func Decode

func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error)

Decode extracts a certificate and private key from pfxData, which must be a DER-encoded PKCS#12 file. This function assumes that there is only one certificate and only one private key in the pfxData. Since PKCS#12 files often contain more than one certificate, you probably want to use DecodeChain instead.

func DecodeChain

func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, err error)

DecodeChain extracts a certificate, a CA certificate chain, and private key from pfxData, which must be a DER-encoded PKCS#12 file. This function assumes that there is at least one certificate and only one private key in the pfxData. The first certificate is assumed to be the leaf certificate, and subsequent certificates, if any, are assumed to comprise the CA certificate chain.

func DecodeTrustStore

func DecodeTrustStore(pfxData []byte, password string) (certs []*x509.Certificate, err error)

DecodeTrustStore extracts the certificates from pfxData, which must be a DER-encoded PKCS#12 file containing exclusively certificates with attribute 2.16.840.1.113894.746875.1.1, which is used by Java to designate a trust anchor.

If the password argument is empty, DecodeTrustStore will decode either password-less PKCS#12 files (i.e. those without encryption) or files with a literal empty password.

func Encode deprecated

func Encode(rand io.Reader, privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error)

Encode is equivalent to LegacyRC2.WithRand(rand).Encode. See Encoder.Encode and LegacyRC2 for details.

Deprecated: for the same behavior, use LegacyRC2.Encode; for better compatibility, use Legacy.Encode; for better security, use Modern.Encode.

func EncodeTrustStore deprecated

func EncodeTrustStore(rand io.Reader, certs []*x509.Certificate, password string) (pfxData []byte, err error)

EncodeTrustStore is equivalent to LegacyRC2.WithRand(rand).EncodeTrustStore. See Encoder.EncodeTrustStore and LegacyRC2 for details.

Deprecated: for the same behavior, use LegacyRC2.EncodeTrustStore; to generate passwordless trust stores, use Passwordless.EncodeTrustStore.

func EncodeTrustStoreEntries deprecated

func EncodeTrustStoreEntries(rand io.Reader, entries []TrustStoreEntry, password string) (pfxData []byte, err error)

EncodeTrustStoreEntries is equivalent to LegacyRC2.WithRand(rand).EncodeTrustStoreEntries. See Encoder.EncodeTrustStoreEntries and LegacyRC2 for details.

Deprecated: for the same behavior, use LegacyRC2.EncodeTrustStoreEntries; to generate passwordless trust stores, use Passwordless.EncodeTrustStoreEntries.

func ToPEM deprecated

func ToPEM(pfxData []byte, password string) ([]*pem.Block, error)

ToPEM converts all "safe bags" contained in pfxData to PEM blocks.

Deprecated: ToPEM creates invalid PEM blocks (private keys are encoded as raw RSA or EC private keys rather than PKCS#8 despite being labeled "PRIVATE KEY"). To decode a PKCS#12 file, use DecodeChain instead, and use the encoding/pem package to convert to PEM if necessary.

Types

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

func NewBuilder

func NewBuilder(enc *Encoder, password []byte, opt Options) (Builder, error)

NewBuilder creates a new PKCS12 builder with a main builder password, used across all the PKCS12 object, except when other passwords are provided. The password is copied, so it can be destroyed without problems.

The Options are optional. They can be used to preallocate some memory and to decide the build order (first trusted, then key/cert pairs, or vice versa).

func (*Builder) Build

func (s *Builder) Build() (pfxData []byte, err error)

Build the entire PKCS12 object in memory. Erase the internal encoded copy of the password regardless of the result.

func (*Builder) Reset

func (s *Builder) Reset(password []byte) error

Reset the internal state of the builder, so it can be used again for a new PKCS12. The password is copied, so it can be destroyed without problems.

func (*Builder) SetPrivateKeyEntry

func (s *Builder) SetPrivateKeyEntry(
	friendlyName string,
	privateKey any,
	certificate *x509.Certificate,
	caCerts []*x509.Certificate,
	password []byte,
) error

SetPrivateKeyEntry set a certificate with its private key and an optional chain of trust, with a custom friendly name (alias). Private Key and Certificate share the same localKeyId and friendlyName.

Password is optional: if not provided, the builder main password will be used. If it's provided, the encoded version created at run time will be erased after the end of this method.

Does not check the consistency of the private key with the public key.

func (*Builder) SetTrustedCertificateEntry

func (s *Builder) SetTrustedCertificateEntry(friendlyName string, certificate *x509.Certificate) error

SetTrustedCertificateEntry set a trusted certificate with a custom friendly name (alias).

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

An Encoder contains methods for encoding PKCS#12 files. This package defines several different Encoders with different parameters. An Encoder is safe for concurrent use by multiple goroutines.

func (*Encoder) Encode

func (enc *Encoder) Encode(privateKey interface{}, certificate *x509.Certificate, caCerts []*x509.Certificate, password string) (pfxData []byte, err error)

Encode produces pfxData containing one private key (privateKey), an end-entity certificate (certificate), and any number of CA certificates (caCerts).

The pfxData is encrypted and authenticated with keys derived from the provided password.

Encode emulates the behavior of OpenSSL's PKCS12_create: it creates two SafeContents: one that's encrypted with the certificate encryption algorithm and contains the certificates, and another that is unencrypted and contains the private key shrouded with the key encryption algorithm. The private key bag and the end-entity certificate bag have the LocalKeyId attribute set to the SHA-1 fingerprint of the end-entity certificate.

func (*Encoder) EncodeTrustStore

func (enc *Encoder) EncodeTrustStore(certs []*x509.Certificate, password string) (pfxData []byte, err error)

EncodeTrustStore produces pfxData containing any number of CA certificates (certs) to be trusted. The certificates will be marked with a special OID that allow it to be used as a Java TrustStore in Java 1.8 and newer.

EncodeTrustStore creates a single SafeContents that's optionally encrypted and contains the certificates.

The Subject of the certificates are used as the Friendly Names (Aliases) within the resulting pfxData. If certificates share a Subject, then the resulting Friendly Names (Aliases) will be identical, which Java may treat as the same entry when used as a Java TrustStore, e.g. with `keytool`. To customize the Friendly Names, use EncodeTrustStoreEntries.

func (*Encoder) EncodeTrustStoreEntries

func (enc *Encoder) EncodeTrustStoreEntries(entries []TrustStoreEntry, password string) (pfxData []byte, err error)

EncodeTrustStoreEntries produces pfxData containing any number of CA certificates (entries) to be trusted. The certificates will be marked with a special OID that allow it to be used as a Java TrustStore in Java 1.8 and newer.

This is identical to Encoder.EncodeTrustStore, but also allows for setting specific Friendly Names (Aliases) to be used per certificate, by specifying a slice of TrustStoreEntry.

If the same Friendly Name is used for more than one certificate, then the resulting Friendly Names (Aliases) in the pfxData will be identical, which Java may treat as the same entry when used as a Java TrustStore, e.g. with `keytool`.

EncodeTrustStoreEntries creates a single SafeContents that's optionally encrypted and contains the certificates.

func (Encoder) WithIterations

func (enc Encoder) WithIterations(iterations int) *Encoder

WithIterations creates a new Encoder identical to enc except that it will use the given number of KDF iterations for deriving the MAC and encryption keys.

Note that even with a large number of iterations, a weak password can still be brute-forced in much less time than it would take to brute-force a high-entropy encrytion key. For the best security, don't worry about the number of iterations and just use a high-entropy password (e.g. one generated with `openssl rand -hex 16`). See https://neilmadden.blog/2023/01/09/on-pbkdf2-iterations/ for more detail.

Panics if iterations is less than 1.

func (Encoder) WithRand

func (enc Encoder) WithRand(rand io.Reader) *Encoder

WithRand creates a new Encoder identical to enc except that it will use the given io.Reader for its random number generator instead of crypto/rand.Reader.

type KeyCertPair added in v0.3.0

type KeyCertPair struct {
	Cert    *x509.Certificate
	CaCerts []*x509.Certificate
	PrvKey  any
}

type NotImplementedError

type NotImplementedError string

NotImplementedError indicates that the input is not currently supported.

func (NotImplementedError) Error

func (e NotImplementedError) Error() string

type Options added in v0.2.0

type Options struct {
	OrderFirstTrusts bool
	PrvKeyEntryLen   uint8
	TrustCertLen     uint8
}

type Password added in v0.3.0

type Password struct {
	FriendlyName string
	Pass         []byte
	// contains filtered or unexported fields
}

type Store added in v0.3.0

type Store struct {
	Pairs  []*KeyCertPair
	Trusts []*x509.Certificate
}

func DecodeAll added in v0.3.0

func DecodeAll(pfxData []byte, mainPassword []byte, passwords []Password) (Store, error)

DecodeAll extracts, from pfxData (which must be a DER-encoded PKCS#12 file)

  • key/certificate pairs with their CA certificate chains (reconstructed dynamically);
  • trusted certificates exclusively with attribute 2.16.840.1.113894.746875.1.1, which is used by Java to designate a trust anchor.

If the mainPassword argument is empty, it will decode either password-less PKCS#12 files (i.e. those without encryption) or files with a literal empty password.

'passwords' argument is needed when there are PKCS#8 shrouded Private Keys encrypted with a different password.

type TrustStoreEntry

type TrustStoreEntry struct {
	Cert         *x509.Certificate
	FriendlyName string
}

TrustStoreEntry represents an entry in a Java TrustStore.

Directories

Path Synopsis
internal
rc2
Package rc2 implements the RC2 cipher
Package rc2 implements the RC2 cipher

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL