package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" "crypto/sha1" "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)))), // }), // ), // }) // SHA-1 Hash hasher := sha1.New() _, err := hasher.Write(p15.cert.RawSubjectPublicKeyInfo) if err != nil { panic(err) } return hasher.Sum(nil) } // keyIdInt2 returns the sequence for keyId with INT val of 2 // For APC, this appears to be the same value is the base keyId // but this isn't compliant with the spec which actually seems // to call for SKID (skid octet value copied directly out of the // certificate's x509 extension) func (p15 *pkcs15KeyCert) keyIdInt2() []byte { // Create Object obj := asn1obj.Sequence([][]byte{ asn1obj.Integer(big.NewInt(2)), // Note: This is for APC, doesn't seem compliant with spec though asn1obj.OctetString(p15.keyId()), }) 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 }