1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
package pemutil
import (
"crypto"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
)
const (
certType string = "CERTIFICATE"
keyType string = "PRIVATE KEY"
)
func ParseCertificates(certsBytes []byte) ([]*x509.Certificate, error) {
objects, err := parseBlocks(certsBytes, certType)
if err != nil {
return nil, err
}
certs := []*x509.Certificate{}
for _, object := range objects {
cert, ok := object.(*x509.Certificate)
if !ok {
return nil, fmt.Errorf("expected *x509.Certificate; got %T", object)
}
certs = append(certs, cert)
}
return certs, nil
}
func ParsePrivateKey(keyBytes []byte) (crypto.PrivateKey, error) {
objects, err := parseBlocks(keyBytes, keyType)
if err != nil {
return nil, err
}
if len(objects) == 0 {
return nil, nil
}
privateKey, ok := objects[0].(crypto.PrivateKey)
if !ok {
return nil, fmt.Errorf("expected crypto.PrivateKey; got %T", objects[0])
}
return privateKey, nil
}
func EncodePKCS8PrivateKey(privateKey interface{}) ([]byte, error) {
keyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{
Type: keyType,
Bytes: keyBytes,
}), nil
}
func EncodeCertificates(certificates []*x509.Certificate) []byte {
pemBytes := []byte{}
for _, cert := range certificates {
pemBytes = append(pemBytes, pem.EncodeToMemory(&pem.Block{
Type: certType,
Bytes: cert.Raw,
})...)
}
return pemBytes
}
func parseBlocks(blocksBytes []byte, expectedType string) ([]interface{}, error) {
objects := []interface{}{}
var foundBlocks = false
for {
if len(blocksBytes) == 0 {
if len(objects) == 0 && !foundBlocks {
return nil, errors.New("no PEM blocks found")
}
return objects, nil
}
object, rest, foundBlock, err := parseBlock(blocksBytes, expectedType)
blocksBytes = rest
if foundBlock {
foundBlocks = true
}
switch {
case err != nil:
return nil, err
case object != nil:
objects = append(objects, object)
}
}
}
func parseBlock(pemBytes []byte, pemType string) (interface{}, []byte, bool, error) {
pemBlock, rest := pem.Decode(pemBytes)
if pemBlock == nil {
return nil, nil, false, nil
}
if pemBlock.Type != pemType {
return nil, rest, true, nil
}
var object interface{}
var err error
switch pemType {
case certType:
object, err = x509.ParseCertificate(pemBlock.Bytes)
case keyType:
object, err = x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
default:
err = fmt.Errorf("PEM type not supported: %q", pemType)
}
if err != nil {
return nil, nil, false, err
}
return object, rest, true, nil
}
|