pkcs15: add some prep for maybe ec key support later

This commit is contained in:
Greg T. Wallace 2024-06-24 18:23:05 -04:00
parent 06f9892501
commit 7c1ad8ef43
5 changed files with 167 additions and 86 deletions

View file

@ -2,6 +2,7 @@ package pkcs15
import ( import (
"apc-p15-tool/pkg/tools/asn1obj" "apc-p15-tool/pkg/tools/asn1obj"
"crypto/rsa"
"crypto/sha1" "crypto/sha1"
"encoding/binary" "encoding/binary"
"math/big" "math/big"
@ -11,22 +12,6 @@ import (
func (p15 *pkcs15KeyCert) keyId() []byte { func (p15 *pkcs15KeyCert) keyId() []byte {
// object to hash is just the RawSubjectPublicKeyInfo // object to hash is just the RawSubjectPublicKeyInfo
// Create Object to hash
// hashObj := asn1obj.Sequence([][]byte{
// asn1obj.Sequence([][]byte{
// // Key is RSA
// asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1),
// asn1.NullBytes,
// }),
// // 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 // SHA-1 Hash
hasher := sha1.New() hasher := sha1.New()
_, err := hasher.Write(p15.cert.RawSubjectPublicKeyInfo) _, err := hasher.Write(p15.cert.RawSubjectPublicKeyInfo)
@ -124,18 +109,28 @@ func (p15 *pkcs15KeyCert) keyIdInt7() []byte {
} }
// keyIdInt8 returns the sequence for keyId with INT val of 8; This value is equivelant // keyIdInt8 returns the sequence for keyId with INT val of 8; This value is equivelant
// to "pgp", which is PGP v3 key Id. This value is just the last 8 bytes of the public // to "pgp", which is PGP v3 key Id.
// key N value
func (p15 *pkcs15KeyCert) keyIdInt8() []byte { func (p15 *pkcs15KeyCert) keyIdInt8() []byte {
nBytes := p15.key.N.Bytes() var keyIdVal []byte
switch privKey := p15.key.(type) {
case *rsa.PrivateKey:
// RSA: The ID value is just the last 8 bytes of the public key N value
nBytes := privKey.N.Bytes()
keyIdVal = nBytes[len(nBytes)-8:]
default:
// panic if non-RSA key
panic("key id 8 for non-rsa key is unexpected and unsupported")
}
// object to return // object to return
obj := asn1obj.Sequence([][]byte{ idObj := asn1obj.Sequence([][]byte{
asn1obj.Integer(big.NewInt(8)), asn1obj.Integer(big.NewInt(8)),
asn1obj.OctetString(nBytes[len(nBytes)-8:]), asn1obj.OctetString(keyIdVal),
}) })
return obj return idObj
} }
// bigIntToMpi returns the MPI (as defined in RFC 4880 s 3.2) from a given // bigIntToMpi returns the MPI (as defined in RFC 4880 s 3.2) from a given
@ -156,33 +151,64 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte {
// Public-Key packet starting with the version field. The Key ID is the // Public-Key packet starting with the version field. The Key ID is the
// low-order 64 bits of the fingerprint. // low-order 64 bits of the fingerprint.
// the entire Public-Key packet // first make the public key packet
publicKeyPacket := []byte{} publicKeyPacket := []byte{}
// starting with the version field (A one-octet version number (4)). // starting with the version field (A one-octet version number (4)).
publicKeyPacket = append(publicKeyPacket, byte(4)) publicKeyPacket = append(publicKeyPacket, byte(4))
// A four-octet number denoting the time that the key was created. // A four-octet number denoting the time that the key was created.
time := make([]byte, 4)
// NOTE: use cert validity start as proxy for key creation since key pem // NOTE: use cert validity start as proxy for key creation since key pem
// doesn't actually contain a created at time -- in reality notBefore tends // doesn't actually contain a created at time -- in reality notBefore tends
// to be ~ 1 hour ish BEFORE the cert was even created. Key would also // to be ~ 1 hour ish BEFORE the cert was even created. Key would also
// obviously have to be created prior to the cert creation. // obviously have to be created prior to the cert creation.
time := make([]byte, 4)
binary.BigEndian.PutUint32(time, uint32(p15.cert.NotBefore.Unix())) binary.BigEndian.PutUint32(time, uint32(p15.cert.NotBefore.Unix()))
publicKeyPacket = append(publicKeyPacket, time...) publicKeyPacket = append(publicKeyPacket, time...)
// A one-octet number denoting the public-key algorithm of this key. // the next part is key type specific
// 1 - RSA (Encrypt or Sign) [HAC] switch privKey := p15.key.(type) {
publicKeyPacket = append(publicKeyPacket, byte(1)) case *rsa.PrivateKey:
// A one-octet number denoting the public-key algorithm of this key.
// 1 - RSA (Encrypt or Sign) [HAC]
publicKeyPacket = append(publicKeyPacket, byte(1))
// Algorithm-Specific Fields for RSA public keys: // Algorithm-Specific Fields for RSA public keys:
// multiprecision integer (MPI) of RSA public modulus n // multiprecision integer (MPI) of RSA public modulus n
publicKeyPacket = append(publicKeyPacket, bigIntToMpi(p15.key.N)...) publicKeyPacket = append(publicKeyPacket, bigIntToMpi(privKey.N)...)
// MPI of RSA public encryption exponent e // MPI of RSA public encryption exponent e
e := big.NewInt(int64(p15.key.PublicKey.E)) e := big.NewInt(int64(privKey.PublicKey.E))
publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...) publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...)
// case *ecdsa.PrivateKey:
// // A one-octet number denoting the public-key algorithm of this key.
// // 19 - ECDSA public key algorithm (see rfc 6637 s. 5)
// publicKeyPacket = append(publicKeyPacket, uint8(19))
// // Algorithm-Specific Fields for ECDSA public keys (see rfc 6637 s. 11 table)
// // This is a length byte followed by the curve ID (length is the number of bytes the curve ID uses)
// switch privKey.Curve.Params().Name {
// case "P-256":
// // 1.2.840.10045.3.1.7 8 2A 86 48 CE 3D 03 01 07 NIST curve P-256
// publicKeyPacket = append(publicKeyPacket, byte(8))
// hex, _ := hex.DecodeString("2A8648CE3D030107")
// publicKeyPacket = append(publicKeyPacket, hex...)
// case "P-384":
// // 1.3.132.0.34 5 2B 81 04 00 22 NIST curve P-384
// publicKeyPacket = append(publicKeyPacket, byte(5))
// hex, _ := hex.DecodeString("2B81040022")
// publicKeyPacket = append(publicKeyPacket, hex...)
// default:
// panic(fmt.Sprintf("key id 9 for ecdsa key curve %s is unexpected and unsupported", privKey.Curve.Params().Name))
// }
default:
// panic if non-RSA key
panic("key id 9 for non-rsa key is unexpected and unsupported")
}
// Assemble the V4 byte array that will be hashed // Assemble the V4 byte array that will be hashed
// 0x99 (1 octet) // 0x99 (1 octet)
@ -205,10 +231,10 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte {
keyId := sha1Hash[len(sha1Hash)-8:] keyId := sha1Hash[len(sha1Hash)-8:]
// object to return // object to return
obj := asn1obj.Sequence([][]byte{ idObj := asn1obj.Sequence([][]byte{
asn1obj.Integer(big.NewInt(9)), asn1obj.Integer(big.NewInt(9)),
asn1obj.OctetString(keyId), asn1obj.OctetString(keyId),
}) })
return obj return idObj
} }

View file

@ -1,12 +1,14 @@
package pkcs15 package pkcs15
import ( import (
"crypto"
"crypto/rsa" "crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"reflect"
) )
var ( var (
@ -22,7 +24,7 @@ var (
// pemKeyDecode attempts to decode a pem encoded byte slice and then attempts // 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 // to parse an RSA private key from the decoded pem block. an error is returned
// if any of these steps fail OR if the key is not RSA and of bitlen 1,024 or 2,048 // if any of these steps fail OR if the key is not RSA and of bitlen 1,024 or 2,048
func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { func pemKeyDecode(keyPem []byte) (crypto.PrivateKey, error) {
// decode // decode
pemBlock, _ := pem.Decode([]byte(keyPem)) pemBlock, _ := pem.Decode([]byte(keyPem))
if pemBlock == nil { if pemBlock == nil {
@ -30,13 +32,11 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) {
} }
// parsing depends on block type // parsing depends on block type
var rsaKey *rsa.PrivateKey var privateKey crypto.PrivateKey
switch pemBlock.Type { switch pemBlock.Type {
case "RSA PRIVATE KEY": // PKCS1 case "RSA PRIVATE KEY": // PKCS1
var err error rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
if err != nil { if err != nil {
return nil, errPemKeyFailedToParse return nil, errPemKeyFailedToParse
} }
@ -53,6 +53,22 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) {
} }
// good to go // good to go
privateKey = rsaKey
// case "EC PRIVATE KEY": // SEC1, ASN.1
// var ecdKey *ecdsa.PrivateKey
// ecdKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
// if err != nil {
// return nil, errPemKeyFailedToParse
// }
// // verify acceptable curve name
// if ecdKey.Curve.Params().Name != "P-256" && ecdKey.Curve.Params().Name != "P-384" {
// return nil, errPemKeyWrongType
// }
// // good to go
// privateKey = ecdKey
case "PRIVATE KEY": // PKCS8 case "PRIVATE KEY": // PKCS8
pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes) pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
@ -62,20 +78,28 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) {
switch pkcs8Key := pkcs8Key.(type) { switch pkcs8Key := pkcs8Key.(type) {
case *rsa.PrivateKey: case *rsa.PrivateKey:
rsaKey = pkcs8Key
// basic sanity check // basic sanity check
err = rsaKey.Validate() err = pkcs8Key.Validate()
if err != nil { if err != nil {
return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err) return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err)
} }
// verify proper bitlen // verify proper bitlen
if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 && rsaKey.N.BitLen() != 3072 { if pkcs8Key.N.BitLen() != 1024 && pkcs8Key.N.BitLen() != 2048 && pkcs8Key.N.BitLen() != 3072 {
return nil, errPemKeyWrongType return nil, errPemKeyWrongType
} }
// good to go // good to go
privateKey = pkcs8Key
// case *ecdsa.PrivateKey:
// // verify acceptable curve name
// if pkcs8Key.Curve.Params().Name != "P-256" && pkcs8Key.Curve.Params().Name != "P-384" {
// return nil, errPemKeyWrongType
// }
// // good to go
// privateKey = pkcs8Key
default: default:
return nil, errPemKeyWrongType return nil, errPemKeyWrongType
@ -86,12 +110,12 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) {
} }
// if rsaKey is nil somehow, error // if rsaKey is nil somehow, error
if rsaKey == nil { if reflect.ValueOf(privateKey).IsNil() {
return nil, errors.New("pkcs15: pem key: rsa key unexpectedly nil (report bug to project repo)") return nil, errors.New("pkcs15: pem key: rsa key unexpectedly nil (report bug to project repo)")
} }
// success! // success!
return rsaKey, nil return privateKey, nil
} }
// pemCertDecode attempts to decode a pem encoded byte slice and then attempts // pemCertDecode attempts to decode a pem encoded byte slice and then attempts

View file

@ -1,14 +1,14 @@
package pkcs15 package pkcs15
import ( import (
"crypto/rsa" "crypto"
"crypto/x509" "crypto/x509"
) )
// pkcs15KeyCert holds the data for a key and certificate pair; it provides // pkcs15KeyCert holds the data for a key and certificate pair; it provides
// various methods to transform pkcs15 data // various methods to transform pkcs15 data
type pkcs15KeyCert struct { type pkcs15KeyCert struct {
key *rsa.PrivateKey key crypto.PrivateKey
cert *x509.Certificate cert *x509.Certificate
} }

View file

@ -2,6 +2,7 @@ package pkcs15
import ( import (
"apc-p15-tool/pkg/tools/asn1obj" "apc-p15-tool/pkg/tools/asn1obj"
"crypto/rsa"
"encoding/asn1" "encoding/asn1"
"math/big" "math/big"
) )
@ -111,6 +112,38 @@ func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err
// the APC tool uses when generating a new private key (Note: no header is used on // the APC tool uses when generating a new private key (Note: no header is used on
// this file) // this file)
func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) { func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) {
// create public key object
var pubKeyObj []byte
switch privKey := p15.key.(type) {
case *rsa.PrivateKey:
pubKeyObj = asn1obj.ExplicitCompound(1, [][]byte{
asn1obj.Sequence([][]byte{
asn1obj.ExplicitCompound(0, [][]byte{
asn1obj.ExplicitCompound(1, [][]byte{
asn1obj.Sequence([][]byte{
asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1),
asn1.NullBytes,
}),
// RSAPublicKey SubjectPublicKeyInfo
asn1obj.BitString(
asn1obj.Sequence([][]byte{
asn1obj.Integer(privKey.PublicKey.N),
asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.E))),
}),
),
}),
}),
// not 100% certain but appears to be rsa key byte len
asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.N.BitLen() / 8))),
}),
})
default:
// panic if non-RSA key
panic("p15 key file for non-rsa key is unexpected and unsupported")
}
// private key object (slightly different than the key+cert format) // private key object (slightly different than the key+cert format)
privateKey := asn1obj.Sequence([][]byte{ privateKey := asn1obj.Sequence([][]byte{
// commonObjectAttributes - Label // commonObjectAttributes - Label
@ -181,27 +214,7 @@ func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) {
asn1obj.BitString([]byte{byte(0b01000000)}), asn1obj.BitString([]byte{byte(0b01000000)}),
}), }),
asn1obj.ExplicitCompound(1, [][]byte{ pubKeyObj,
asn1obj.Sequence([][]byte{
asn1obj.ExplicitCompound(0, [][]byte{
asn1obj.ExplicitCompound(1, [][]byte{
asn1obj.Sequence([][]byte{
asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1),
asn1.NullBytes,
}),
// RSAPublicKey SubjectPublicKeyInfo
asn1obj.BitString(
asn1obj.Sequence([][]byte{
asn1obj.Integer(p15.key.PublicKey.N),
asn1obj.Integer(big.NewInt(int64(p15.key.PublicKey.E))),
}),
),
}),
}),
// not 100% certain but appears to be rsa key byte len
asn1obj.Integer(big.NewInt(int64(p15.key.PublicKey.N.BitLen() / 8))),
}),
}),
}), }),
}), }),
}), }),

View file

@ -1,24 +1,42 @@
package pkcs15 package pkcs15
import "apc-p15-tool/pkg/tools/asn1obj" import (
"apc-p15-tool/pkg/tools/asn1obj"
"crypto/rsa"
)
// privateKeyObject returns the ASN.1 representation of a private key // privateKeyObject returns the ASN.1 representation of a private key
func (p15 *pkcs15KeyCert) privateKeyObject() []byte { func (p15 *pkcs15KeyCert) privateKeyObject() []byte {
// ensure all expected vals are available var privKeyObj []byte
p15.key.Precompute()
pkey := asn1obj.Sequence([][]byte{ switch privKey := p15.key.(type) {
// P case *rsa.PrivateKey:
asn1obj.IntegerExplicitValue(3, p15.key.Primes[0]), privKey.Precompute()
// Q
asn1obj.IntegerExplicitValue(4, p15.key.Primes[1]),
// Dp
asn1obj.IntegerExplicitValue(5, p15.key.Precomputed.Dp),
// Dq
asn1obj.IntegerExplicitValue(6, p15.key.Precomputed.Dq),
// Qinv
asn1obj.IntegerExplicitValue(7, p15.key.Precomputed.Qinv),
})
return pkey // ensure all expected vals are available
privKeyObj = asn1obj.Sequence([][]byte{
// P
asn1obj.IntegerExplicitValue(3, privKey.Primes[0]),
// Q
asn1obj.IntegerExplicitValue(4, privKey.Primes[1]),
// Dp
asn1obj.IntegerExplicitValue(5, privKey.Precomputed.Dp),
// Dq
asn1obj.IntegerExplicitValue(6, privKey.Precomputed.Dq),
// Qinv
asn1obj.IntegerExplicitValue(7, privKey.Precomputed.Qinv),
})
// case *ecdsa.PrivateKey:
// // Only private piece is the integer D
// privKeyObj = asn1obj.Sequence([][]byte{
// asn1obj.Integer(privKey.D),
// })
default:
// panic if non-RSA key
panic("private key object for non-rsa key is unexpected and unsupported")
}
return privKeyObj
} }