pkcs8

package
v0.26.1 Latest Latest
Warning

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

Go to latest
Published: Mar 6, 2024 License: MIT, MIT Imports: 20 Imported by: 0

README

OpenSSL can generate private keys in both "traditional format" and PKCS#8 format. Newer applications are advised to use more secure PKCS#8 format. Go standard crypto package provides a function to parse private key in PKCS#8 format. There is a limitation to this function. It can only handle unencrypted PKCS#8 private keys. To use this function, the user has to save the private key in file without encryption, which is a bad practice to leave private keys unprotected on file systems. In addition, Go standard package lacks the functions to convert RSA/ECDSA private keys into PKCS#8 format.

pkcs8 package fills the gap here. It implements functions to process private keys in PKCS#8 format, as defined in RFC5208 and RFC5958. It can handle both unencrypted PKCS#8 PrivateKeyInfo format and EncryptedPrivateKeyInfo format with PKCS#5 (v2.0) algorithms.

Credits

This is a fork of youmark/pkcs8, and we added support for ShangMi.

Documentation

Overview

Package pkcs8 implements functions to parse and convert private keys in PKCS#8 format with ShangMi(SM) support, as defined in RFC5208 and RFC5958.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultOpts = &Opts{
	Cipher: pkcs.AES256CBC,
	KDFOpts: PBKDF2Opts{
		SaltSize:       8,
		IterationCount: 10000,
		HMACHash:       SHA256,
	},
}

DefaultOpts are the default options for encrypting a key if none are given. The defaults can be changed by the library user.

Functions

func ConvertPrivateKeyToPKCS8

func ConvertPrivateKeyToPKCS8(priv any, v ...[]byte) ([]byte, error)

ConvertPrivateKeyToPKCS8 converts the private key into PKCS#8 format. To encrypt the private key, the password of []byte type should be provided as the second parameter.

func MarshalPrivateKey

func MarshalPrivateKey(priv any, password []byte, opts *Opts) ([]byte, error)

MarshalPrivateKey encodes a private key into DER-encoded PKCS#8 with the given options. Password can be nil.

Example
package main

import (
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"

	"github.com/emmansun/gmsm/pkcs"
	"github.com/emmansun/gmsm/pkcs8"
	"github.com/emmansun/gmsm/sm2"
)

func main() {
	// real private key should be from secret storage, or generate directly
	privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
	d := new(big.Int).SetBytes(privKey)
	testkey := new(sm2.PrivateKey)
	testkey.Curve = sm2.P256()
	testkey.D = d
	testkey.PublicKey.X, testkey.PublicKey.Y = testkey.ScalarBaseMult(testkey.D.Bytes())

	password := []byte("Password1")
	opts := &pkcs8.Opts{
		Cipher: pkcs.SM4CBC,
		KDFOpts: pkcs8.PBKDF2Opts{
			SaltSize: 16, IterationCount: 16, HMACHash: pkcs8.SM3,
		},
	}
	// generate der bytes
	der, err := pkcs8.MarshalPrivateKey(testkey, password, opts)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from MarshalPrivateKey: %s\n", err)
		return
	}

	// encode der bytes to pem
	block := &pem.Block{Bytes: der, Type: "ENCRYPTED PRIVATE KEY"}
	pemContent := string(pem.EncodeToMemory(block))
	fmt.Printf("%v\n", pemContent)
}
Output:

Example (WithoutPassword)
package main

import (
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"

	"github.com/emmansun/gmsm/pkcs8"
	"github.com/emmansun/gmsm/sm2"
)

func main() {
	// real private key should be from secret storage, or generate directly
	privKey, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
	d := new(big.Int).SetBytes(privKey)
	testkey := new(sm2.PrivateKey)
	testkey.Curve = sm2.P256()
	testkey.D = d
	testkey.PublicKey.X, testkey.PublicKey.Y = testkey.ScalarBaseMult(testkey.D.Bytes())

	// generate der bytes
	der, err := pkcs8.MarshalPrivateKey(testkey, nil, nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from MarshalPrivateKey: %s\n", err)
		return
	}

	// encode der bytes to pem
	block := &pem.Block{Bytes: der, Type: "PRIVATE KEY"}
	pemContent := string(pem.EncodeToMemory(block))
	fmt.Printf("%v\n", pemContent)
}
Output:

Example (WithoutPasswordSM9MasterSignKey)
package main

import (
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"

	"github.com/emmansun/gmsm/pkcs8"

	"github.com/emmansun/gmsm/sm9"
	"golang.org/x/crypto/cryptobyte"
)

func main() {
	// real private key should be from secret storage, or generate directly
	kb, _ := hex.DecodeString("0130E78459D78545CB54C587E02CF480CE0B66340F319F348A1D5B1F2DC5F4")
	var b cryptobyte.Builder
	b.AddASN1BigInt(new(big.Int).SetBytes(kb))
	kb, _ = b.Bytes()
	testkey := new(sm9.SignMasterPrivateKey)
	err := testkey.UnmarshalASN1(kb)
	if err != nil {
		panic(err)
	}

	// generate der bytes
	der, err := pkcs8.MarshalPrivateKey(testkey, nil, nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from MarshalPrivateKey: %s\n", err)
		return
	}

	// encode der bytes to pem
	block := &pem.Block{Bytes: der, Type: "SM9 SIGN PRIVATE KEY"}
	pemContent := string(pem.EncodeToMemory(block))
	fmt.Printf("%v\n", pemContent)
}
Output:

func ParsePKCS8PrivateKey

func ParsePKCS8PrivateKey(der []byte, v ...[]byte) (any, error)

ParsePKCS8PrivateKey parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

Example
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIH2MGEGCSqGSIb3DQEFDTBUMDQGCSqGSIb3DQEFDDAnBBDa6ckWJNP3QBD7MIF8
4nVqAgEQAgEQMA0GCSqBHM9VAYMRAgUAMBwGCCqBHM9VAWgCBBDMUgr+5Y/XN2g9
mPGiISzGBIGQytwK98/ET4WrS0H7AsUri6FTqztrzAvgzFl3+s9AsaYtUlzE3EzE
x6RWxo8kpKO2yj0a/Jh9WZCD4XAcoZ9aMopiWlOdpXJr/iQlMGdirCYIoF37lHMc
jZHNffmk4ii7NxCfjrzpiFq4clYsNMXeSEnq1tuOEur4kYcjHYSIFc9bPG656a60
+SIJsJuPFi0f
-----END ENCRYPTED PRIVATE KEY-----`
	password := []byte("Password1")
	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, password)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParsePKCS8PrivateKey: %s\n", err)
		return
	}
	if pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok
Example (WithoutPassword)
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbFoKCy7tPL7D5PEl
K/4OKMUEoca/GZnuuwr57w+ObIWhRANCAASDVuZCpA69GNKbo1MvvZ87vujwJ8P2
85pbovhwNp+ZiJgfXv5V0cXN9sDvKwcIR6FPf99CcqjfCcRC8wWK+Uuh
-----END PRIVATE KEY-----`
	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParsePKCS8PrivateKey: %s\n", err)
		return
	}
	if pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok

func ParsePKCS8PrivateKeyECDSA

func ParsePKCS8PrivateKeyECDSA(der []byte, v ...[]byte) (*ecdsa.PrivateKey, error)

ParsePKCS8PrivateKeyECDSA parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

func ParsePKCS8PrivateKeyRSA

func ParsePKCS8PrivateKeyRSA(der []byte, v ...[]byte) (*rsa.PrivateKey, error)

ParsePKCS8PrivateKeyRSA parses encrypted/unencrypted private keys in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

func ParsePKCS8PrivateKeySM2

func ParsePKCS8PrivateKeySM2(der []byte, v ...[]byte) (*sm2.PrivateKey, error)

ParsePKCS8PrivateKeySM2 parses encrypted/unencrypted SM2 private key in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

Example
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIH2MGEGCSqGSIb3DQEFDTBUMDQGCSqGSIb3DQEFDDAnBBDa6ckWJNP3QBD7MIF8
4nVqAgEQAgEQMA0GCSqBHM9VAYMRAgUAMBwGCCqBHM9VAWgCBBDMUgr+5Y/XN2g9
mPGiISzGBIGQytwK98/ET4WrS0H7AsUri6FTqztrzAvgzFl3+s9AsaYtUlzE3EzE
x6RWxo8kpKO2yj0a/Jh9WZCD4XAcoZ9aMopiWlOdpXJr/iQlMGdirCYIoF37lHMc
jZHNffmk4ii7NxCfjrzpiFq4clYsNMXeSEnq1tuOEur4kYcjHYSIFc9bPG656a60
+SIJsJuPFi0f
-----END ENCRYPTED PRIVATE KEY-----`
	password := []byte("Password1")
	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, err := pkcs8.ParsePKCS8PrivateKeySM2(block.Bytes, password)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParsePKCS8PrivateKeySM2: %s\n", err)
		return
	}
	if pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok
Example (WithoutPassword)
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbFoKCy7tPL7D5PEl
K/4OKMUEoca/GZnuuwr57w+ObIWhRANCAASDVuZCpA69GNKbo1MvvZ87vujwJ8P2
85pbovhwNp+ZiJgfXv5V0cXN9sDvKwcIR6FPf99CcqjfCcRC8wWK+Uuh
-----END PRIVATE KEY-----`
	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, err := pkcs8.ParsePKCS8PrivateKeySM2(block.Bytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParsePKCS8PrivateKeySM2: %s\n", err)
		return
	}
	if pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok

func ParseSM9EncryptMasterPrivateKey added in v0.15.0

func ParseSM9EncryptMasterPrivateKey(der []byte, v ...[]byte) (*sm9.EncryptMasterPrivateKey, error)

ParseSM9EncryptMasterPrivateKey parses encrypted/unencrypted SM9 encrypt master private key in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

func ParseSM9EncryptPrivateKey added in v0.15.0

func ParseSM9EncryptPrivateKey(der []byte, v ...[]byte) (*sm9.EncryptPrivateKey, error)

ParseSM9EncryptPrivateKey parses encrypted/unencrypted SM9 encrypt private key in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

func ParseSM9SignMasterPrivateKey added in v0.15.0

func ParseSM9SignMasterPrivateKey(der []byte, v ...[]byte) (*sm9.SignMasterPrivateKey, error)

ParseSM9SignMasterPrivateKey parses encrypted/unencrypted SM9 sign master private key in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

Example (WithoutPassword)
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN SM9 SIGN PRIVATE KEY-----
MIHGAgEAMBUGCCqBHM9VAYIuBgkqgRzPVQGCLgEEgakwgaYCHwEw54RZ14VFy1TF
h+As9IDOC2Y0DzGfNIodWx8txfQDgYIABJ9kCAswhPcz5Ir/S0G1ZQEc4HEcXjks
+wqxtnkblMQIKduhFhUtH3hs6EPtJKO1c0FNIXc4apLdjxTWVpbqXjJphQk4q+oB
ErVzKfRH46DLrT4v2xp38zXonhQI0O8cJUHgClPdpTLaGnzgJ7ekb3QQBuhfXN/w
cw51wF+04yFt
-----END SM9 SIGN PRIVATE KEY-----`
	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, err := pkcs8.ParseSM9SignMasterPrivateKey(block.Bytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParseSM9SignMasterPrivateKey: %s\n", err)
		return
	}
	if pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok

func ParseSM9SignPrivateKey added in v0.15.0

func ParseSM9SignPrivateKey(der []byte, v ...[]byte) (*sm9.SignPrivateKey, error)

ParseSM9SignPrivateKey parses encrypted/unencrypted SM9 sign private key in PKCS#8 format. To parse encrypted private keys, a password of []byte type should be provided to the function as the second parameter.

func RegisterKDF

func RegisterKDF(oid asn1.ObjectIdentifier, params func() KDFParameters)

RegisterKDF registers a function that returns a new instance of the given KDF parameters. This allows the library to support client-provided KDFs.

Types

type Hash

type Hash uint

Hash identifies a cryptographic hash function that is implemented in another package.

const (
	SHA1 Hash = 1 + iota
	SHA224
	SHA256
	SHA384
	SHA512
	SHA512_224
	SHA512_256
	SM3
)

func (Hash) New

func (h Hash) New() hash.Hash

New returns a new hash.Hash calculating the given hash function. New panics if the hash function is not linked into the binary.

type KDFOpts

type KDFOpts interface {
	// DeriveKey derives a key of size bytes from the given password and salt.
	// It returns the key and the ASN.1-encodable parameters used.
	DeriveKey(password, salt []byte, size int) (key []byte, params KDFParameters, err error)
	// GetSaltSize returns the salt size specified.
	GetSaltSize() int
	// OID returns the OID of the KDF specified.
	OID() asn1.ObjectIdentifier
}

KDFOpts contains options for a key derivation function. An implementation of this interface must be specified when encrypting a PKCS#8 key.

type KDFParameters

type KDFParameters interface {
	// DeriveKey derives a key of size bytes from the given password.
	// It uses the salt from the decoded parameters.
	DeriveKey(password []byte, size int) (key []byte, err error)
}

KDFParameters contains parameters (salt, etc.) for a key deriviation function. It must be a ASN.1-decodable structure. An implementation of this interface is created when decoding an encrypted PKCS#8 key.

func ParsePrivateKey

func ParsePrivateKey(der []byte, password []byte) (any, KDFParameters, error)

ParsePrivateKey parses a DER-encoded PKCS#8 private key. Password can be nil. This is equivalent to ParsePKCS8PrivateKey.

Example
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIH2MGEGCSqGSIb3DQEFDTBUMDQGCSqGSIb3DQEFDDAnBBDa6ckWJNP3QBD7MIF8
4nVqAgEQAgEQMA0GCSqBHM9VAYMRAgUAMBwGCCqBHM9VAWgCBBDMUgr+5Y/XN2g9
mPGiISzGBIGQytwK98/ET4WrS0H7AsUri6FTqztrzAvgzFl3+s9AsaYtUlzE3EzE
x6RWxo8kpKO2yj0a/Jh9WZCD4XAcoZ9aMopiWlOdpXJr/iQlMGdirCYIoF37lHMc
jZHNffmk4ii7NxCfjrzpiFq4clYsNMXeSEnq1tuOEur4kYcjHYSIFc9bPG656a60
+SIJsJuPFi0f
-----END ENCRYPTED PRIVATE KEY-----`
	password := []byte("Password1")

	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, params, err := pkcs8.ParsePrivateKey(block.Bytes, password)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParsePrivateKey: %s\n", err)
		return
	}
	if params != nil && pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok
Example (WithoutPassword)
package main

import (
	"encoding/pem"
	"fmt"

	"os"

	"github.com/emmansun/gmsm/pkcs8"
)

func main() {
	const privateKeyPem = `
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbFoKCy7tPL7D5PEl
K/4OKMUEoca/GZnuuwr57w+ObIWhRANCAASDVuZCpA69GNKbo1MvvZ87vujwJ8P2
85pbovhwNp+ZiJgfXv5V0cXN9sDvKwcIR6FPf99CcqjfCcRC8wWK+Uuh
-----END PRIVATE KEY-----`
	block, _ := pem.Decode([]byte(privateKeyPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to parse PEM block\n")
		return
	}
	pk, params, err := pkcs8.ParsePrivateKey(block.Bytes, nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error from ParsePrivateKey: %s\n", err)
		return
	}
	if params == nil && pk != nil {
		fmt.Println("ok")
	} else {
		fmt.Println("fail")
	}
}
Output:

ok

type Opts

type Opts struct {
	Cipher  pkcs.Cipher
	KDFOpts KDFOpts
}

Opts contains options for encrypting a PKCS#8 key.

type PBKDF2Opts

type PBKDF2Opts struct {
	SaltSize       int
	IterationCount int
	HMACHash       Hash
}

PBKDF2Opts contains options for the PBKDF2 key derivation function.

func (PBKDF2Opts) DeriveKey

func (p PBKDF2Opts) DeriveKey(password, salt []byte, size int) (
	key []byte, params KDFParameters, err error)

func (PBKDF2Opts) GetSaltSize

func (p PBKDF2Opts) GetSaltSize() int

func (PBKDF2Opts) OID

type ScryptOpts

type ScryptOpts struct {
	SaltSize                 int
	CostParameter            int
	BlockSize                int
	ParallelizationParameter int
}

ScryptOpts contains options for the scrypt key derivation function.

func (ScryptOpts) DeriveKey

func (p ScryptOpts) DeriveKey(password, salt []byte, size int) (
	key []byte, params KDFParameters, err error)

func (ScryptOpts) GetSaltSize

func (p ScryptOpts) GetSaltSize() int

func (ScryptOpts) OID

Jump to

Keyboard shortcuts

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