app: restructure and start building p15 output

This commit is contained in:
Greg T. Wallace 2024-01-25 20:16:37 -05:00
parent 6610c92058
commit e2e4f2037c
24 changed files with 622 additions and 168 deletions

35
pkg/pkcs15/keyid.go Normal file
View file

@ -0,0 +1,35 @@
package pkcs15
import (
"apc-p15-tool/pkg/tools/asn1obj"
"crypto/sha1"
"math/big"
)
// keyId returns the keyId for the overall key object
func (p15 *pkcs15KeyCert) keyId() []byte {
// Create Object to hash
hashObj := asn1obj.Sequence([][]byte{
asn1obj.Sequence([][]byte{
// Key is RSA
asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1),
asn1obj.Null(),
}),
// BIT STRING of rsa key public key
asn1obj.BitString(
asn1obj.Sequence([][]byte{
asn1obj.Integer(p15.key.N),
asn1obj.Integer((big.NewInt(int64(p15.key.E)))),
}),
),
})
// SHA-1 Hash
hasher := sha1.New()
_, err := hasher.Write(hashObj)
if err != nil {
panic(err)
}
return hasher.Sum(nil)
}

66
pkg/pkcs15/marshal.go Normal file
View file

@ -0,0 +1,66 @@
package pkcs15
import (
"apc-p15-tool/pkg/tools/asn1obj"
"math/big"
)
const (
apcKeyLabel = "Private key"
)
// ToP15File turns the key and cert into a properly formatted and encoded
// p15 file
func (p15 *pkcs15KeyCert) ToP15File() ([]byte, error) {
// private key object
pkey, err := p15.toP15PrivateKey()
if err != nil {
return nil, err
}
// ContentInfo
p15File := asn1obj.Sequence([][]byte{
// contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type)
asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content),
// content
asn1obj.Explicit(0,
asn1obj.Sequence([][]byte{
asn1obj.Integer(big.NewInt(0)),
asn1obj.Sequence([][]byte{
asn1obj.Explicit(0,
asn1obj.Explicit(0,
pkey,
),
),
}),
}),
),
})
return p15File, nil
}
// toP15PrivateKey creates the encoded private key. it is broken our from the larger p15
// function for readability
func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) {
// key object
key := asn1obj.Sequence([][]byte{
// commonObjectAttributes - Label
asn1obj.Sequence([][]byte{
asn1obj.UTF8String(apcKeyLabel),
}),
// CommonKeyAttributes
asn1obj.Sequence([][]byte{
// CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE )
asn1obj.OctetString(p15.keyId()),
// CommonKeyAttributes - usage (trailing 0s will drop)
asn1obj.BitString([]byte{byte(0b11100010)}),
// CommonKeyAttributes - accessFlags (trailing 0s will drop)
asn1obj.BitString([]byte{byte(0b10110000)}),
}),
})
return key, nil
}

1
pkg/pkcs15/oids.go Normal file
View file

@ -0,0 +1 @@
package pkcs15

120
pkg/pkcs15/pem_decode.go Normal file
View file

@ -0,0 +1,120 @@
package pkcs15
import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
)
var (
errPemKeyBadBlock = errors.New("pkcs15: pem key: failed to decode pem block")
errPemKeyFailedToParse = errors.New("pkcs15: pem key: failed to parse key")
errPemKeyWrongBlockType = errors.New("pkcs15: pem key: unsupported pem block type (only pkcs1 and pkcs8 supported)")
errPemKeyWrongType = errors.New("pkcs15: pem key: unsupported key type (only rsa 2,048 supported)")
errPemCertBadBlock = errors.New("pkcs15: pem cert: failed to decode pem block")
errPemCertFailedToParse = errors.New("pkcs15: pem cert: failed to parse cert")
)
// pemKeyDecode attempts to decode a pem encoded byte slice and then attempts
// to parse an RSA private key from the decoded pem block. an error is returned
// if any of these steps fail OR if the rsa key is not of bitlen 2,048
func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) {
// decode
pemBlock, _ := pem.Decode([]byte(keyPem))
if pemBlock == nil {
return nil, errPemKeyBadBlock
}
// parsing depends on block type
var rsaKey *rsa.PrivateKey
switch pemBlock.Type {
case "RSA PRIVATE KEY": // PKCS1
var err error
rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
if err != nil {
return nil, errPemKeyFailedToParse
}
// basic sanity check
err = rsaKey.Validate()
if err != nil {
return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err)
}
// verify proper bitlen
if rsaKey.N.BitLen() != 2048 {
return nil, errPemKeyWrongType
}
// good to go
case "PRIVATE KEY": // PKCS8
pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
if err != nil {
return nil, errPemKeyFailedToParse
}
switch pkcs8Key := pkcs8Key.(type) {
case *rsa.PrivateKey:
rsaKey = pkcs8Key
// basic sanity check
err = rsaKey.Validate()
if err != nil {
return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err)
}
// verify proper bitlen
if rsaKey.N.BitLen() != 2048 {
return nil, errPemKeyWrongType
}
// good to go
default:
return nil, errPemKeyWrongType
}
default:
return nil, errPemKeyWrongBlockType
}
// if rsaKey is nil somehow, error
if rsaKey == nil {
return nil, errors.New("pkcs15: pem key: rsa key unexpectedly nil (report bug to project repo)")
}
// success!
return rsaKey, nil
}
// pemCertDecode attempts to decode a pem encoded byte slice and then attempts
// to parse a certificate from it. The certificate is also check against the
// key that is passed in to verify the key matches the certificate.
func pemCertDecode(certPem, keyPem []byte) (*x509.Certificate, error) {
// verify key and cert make a valid key pair
_, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return nil, err
}
// discard rest, apc tool only bundles end cert
block, _ := pem.Decode(certPem)
if block == nil || block.Type != "CERTIFICATE" {
return nil, errPemCertBadBlock
}
// Get the cert struct
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, errPemCertFailedToParse
}
return cert, nil
}

36
pkg/pkcs15/pem_to_p15.go Normal file
View file

@ -0,0 +1,36 @@
package pkcs15
import (
"crypto/rsa"
"crypto/x509"
)
// pkcs15KeyCert holds the data for a key and certificate pair; it provides
// various methods to transform pkcs15 data
type pkcs15KeyCert struct {
key *rsa.PrivateKey
cert *x509.Certificate
}
// ParsePEMToPKCS15 parses the provide pem files to a pkcs15 struct; it also does some
// basic sanity check; if any of this fails, an error is returned
func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) {
// decode / check key
key, err := pemKeyDecode(keyPem)
if err != nil {
return nil, err
}
// decode / check cert
cert, err := pemCertDecode(certPem, keyPem)
if err != nil {
return nil, err
}
p15 := &pkcs15KeyCert{
key: key,
cert: cert,
}
return p15, nil
}