diff --git a/pkg/pkcs15/keyid.go b/pkg/pkcs15/keyid.go index cf586f3..76f4297 100644 --- a/pkg/pkcs15/keyid.go +++ b/pkg/pkcs15/keyid.go @@ -3,31 +3,33 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" "crypto/sha1" - "encoding/asn1" + "encoding/binary" "math/big" ) // keyId returns the keyId for the overall key object func (p15 *pkcs15KeyCert) keyId() []byte { + // 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)))), - }), - ), - }) + // 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 hasher := sha1.New() - _, err := hasher.Write(hashObj) + _, err := hasher.Write(p15.cert.RawSubjectPublicKeyInfo) if err != nil { panic(err) } @@ -50,3 +52,163 @@ func (p15 *pkcs15KeyCert) keyIdInt2() []byte { return obj } + +// keyIdInt3 returns the sequence for keyId with INT val of 3; This value is equivelant +// to "issuerAndSerialNumberHash" and rfc defines IssuerAndSerialNumber SEQUENCE: +// https://datatracker.ietf.org/doc/html/rfc3852#section-10.2.4 +func (p15 *pkcs15KeyCert) keyIdInt3() []byte { + // object to hash + hashObj := asn1obj.Sequence([][]byte{ + // issuerDistinguishedName + p15.cert.RawIssuer, + // serialNumber + asn1obj.Integer(p15.cert.SerialNumber), + }) + + // SHA-1 Hash + hasher := sha1.New() + _, err := hasher.Write(hashObj) + if err != nil { + panic(err) + } + + // object to return + obj := asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(3)), + asn1obj.OctetString(hasher.Sum(nil)), + }) + + return obj +} + +// keyIdInt6 returns the sequence for keyId with INT val of 6; This value is equivelant +// to "issuerNameHash" +func (p15 *pkcs15KeyCert) keyIdInt6() []byte { + // object to hash is just the RawIssuer + + // SHA-1 Hash + hasher := sha1.New() + _, err := hasher.Write(p15.cert.RawIssuer) + if err != nil { + panic(err) + } + + // object to return + obj := asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(6)), + asn1obj.OctetString(hasher.Sum(nil)), + }) + + return obj +} + +// keyIdInt7 returns the sequence for keyId with INT val of 7; This value is equivelant +// to "subjectNameHash" +func (p15 *pkcs15KeyCert) keyIdInt7() []byte { + // object to hash is just the RawIssuer + + // SHA-1 Hash + hasher := sha1.New() + _, err := hasher.Write(p15.cert.RawSubject) + if err != nil { + panic(err) + } + + // object to return + obj := asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(7)), + asn1obj.OctetString(hasher.Sum(nil)), + }) + + return obj +} + +// 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 +// key N value +func (p15 *pkcs15KeyCert) keyIdInt8() []byte { + nBytes := p15.key.N.Bytes() + + // object to return + obj := asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(8)), + asn1obj.OctetString(nBytes[len(nBytes)-8:]), + }) + + return obj +} + +// bigIntToMpi returns the MPI (as defined in RFC 4880 s 3.2) from a given +// big.Int; this is used as a helper for key ID 9 (openPGP) +func bigIntToMpi(i *big.Int) []byte { + length := make([]byte, 2) + binary.BigEndian.PutUint16(length, uint16(i.BitLen())) + + return append(length, i.Bytes()...) +} + +// keyIdInt9 returns the sequence for keyId with INT val of 9; This value is equivelant +// to "openPGP", which is PGP v4 key Id. +// see: https://www.rfc-editor.org/rfc/rfc4880.html s 12.2 +func (p15 *pkcs15KeyCert) keyIdInt9() []byte { + // A V4 fingerprint is the 160-bit SHA-1 hash of the octet 0x99, + // followed by the two-octet packet length, followed by the entire + // Public-Key packet starting with the version field. The Key ID is the + // low-order 64 bits of the fingerprint. + + // the entire Public-Key packet + publicKeyPacket := []byte{} + + // starting with the version field (A one-octet version number (4)). + publicKeyPacket = append(publicKeyPacket, byte(4)) + + // 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 + // 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 + // obviously have to be created prior to the cert creation. + binary.BigEndian.PutUint32(time, uint32(p15.cert.NotBefore.Unix())) + publicKeyPacket = append(publicKeyPacket, time...) + + // 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: + // multiprecision integer (MPI) of RSA public modulus n + publicKeyPacket = append(publicKeyPacket, bigIntToMpi(p15.key.N)...) + + // MPI of RSA public encryption exponent e + e := big.NewInt(int64(p15.key.PublicKey.E)) + publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...) + + // Assemble the V4 byte array that will be hashed + // 0x99 (1 octet) + toHash := []byte{0x99} + + // big endian encoded length of public key packet (2 octets) + length := make([]byte, 2) + binary.BigEndian.PutUint16(length, uint16(len(publicKeyPacket))) + toHash = append(toHash, length...) + + // Public-Key packet + toHash = append(toHash, publicKeyPacket...) + + // SHA-1 Hash (Fingerprint) + hasher := sha1.New() + hasher.Write(toHash) + sha1Hash := hasher.Sum(nil) + + // keyId is lower 64 bits (8 bytes) + keyId := sha1Hash[len(sha1Hash)-8:] + + // object to return + obj := asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(9)), + asn1obj.OctetString(keyId), + }) + + return obj +} diff --git a/pkg/pkcs15/pem_to_p15.go b/pkg/pkcs15/pem_to_p15.go index 61c63ef..a7a9fef 100644 --- a/pkg/pkcs15/pem_to_p15.go +++ b/pkg/pkcs15/pem_to_p15.go @@ -101,7 +101,6 @@ func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) { // NOTE: Do not use this to try and turn just a cert into a p15. I don't believe, // such a thing is permissible under the spec. func (p15 *pkcs15KeyCert) toP15Cert() ([]byte, error) { - // cert object cert := asn1obj.Sequence([][]byte{ // commonObjectAttributes - Label @@ -114,11 +113,23 @@ func (p15 *pkcs15KeyCert) toP15Cert() ([]byte, error) { // additional keyids asn1obj.ExplicitCompound(2, [][]byte{ p15.keyIdInt2(), - // p15.keyIdInt3(), - // p15.keyIdInt6(), - // p15.keyIdInt7(), - // p15.keyIdInt8(), - // p15.keyIdInt9(), + p15.keyIdInt3(), + p15.keyIdInt6(), + p15.keyIdInt7(), + p15.keyIdInt8(), + p15.keyIdInt9(), + }), + // CommonKeyAttributes - startDate + asn1obj.GeneralizedTime(p15.cert.NotBefore), + // CommonKeyAttributes - [4] endDate + asn1obj.GeneralizedTimeExplicitValue(4, p15.cert.NotAfter), + }), + // actual certificate itself + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + p15.cert.Raw, + }), }), }), })