diff --git a/pkg/pkcs15/keyid.go b/pkg/pkcs15/keyid.go index 76f4297..68a3051 100644 --- a/pkg/pkcs15/keyid.go +++ b/pkg/pkcs15/keyid.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/rsa" "crypto/sha1" "encoding/binary" "math/big" @@ -11,22 +12,6 @@ import ( 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) @@ -124,18 +109,28 @@ func (p15 *pkcs15KeyCert) keyIdInt7() []byte { } // 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 +// to "pgp", which is PGP v3 key Id. 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 - obj := asn1obj.Sequence([][]byte{ + idObj := asn1obj.Sequence([][]byte{ 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 @@ -156,33 +151,64 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte { // 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 + // first make the 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. + time := make([]byte, 4) 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)) + // the next part is key type specific + switch privKey := p15.key.(type) { + 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: - // multiprecision integer (MPI) of RSA public modulus n - publicKeyPacket = append(publicKeyPacket, bigIntToMpi(p15.key.N)...) + // Algorithm-Specific Fields for RSA public keys: + // multiprecision integer (MPI) of RSA public modulus n + publicKeyPacket = append(publicKeyPacket, bigIntToMpi(privKey.N)...) - // MPI of RSA public encryption exponent e - e := big.NewInt(int64(p15.key.PublicKey.E)) - publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...) + // MPI of RSA public encryption exponent e + e := big.NewInt(int64(privKey.PublicKey.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 // 0x99 (1 octet) @@ -205,10 +231,10 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte { keyId := sha1Hash[len(sha1Hash)-8:] // object to return - obj := asn1obj.Sequence([][]byte{ + idObj := asn1obj.Sequence([][]byte{ asn1obj.Integer(big.NewInt(9)), asn1obj.OctetString(keyId), }) - return obj + return idObj } diff --git a/pkg/pkcs15/pem_decode.go b/pkg/pkcs15/pem_decode.go index 1fab7d0..66a34b7 100644 --- a/pkg/pkcs15/pem_decode.go +++ b/pkg/pkcs15/pem_decode.go @@ -1,12 +1,14 @@ package pkcs15 import ( + "crypto" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "errors" "fmt" + "reflect" ) var ( @@ -22,7 +24,7 @@ var ( // 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 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 pemBlock, _ := pem.Decode([]byte(keyPem)) if pemBlock == nil { @@ -30,13 +32,11 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // parsing depends on block type - var rsaKey *rsa.PrivateKey + var privateKey crypto.PrivateKey switch pemBlock.Type { case "RSA PRIVATE KEY": // PKCS1 - var err error - - rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) if err != nil { return nil, errPemKeyFailedToParse } @@ -53,6 +53,22 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // 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 pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes) @@ -62,20 +78,28 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { switch pkcs8Key := pkcs8Key.(type) { case *rsa.PrivateKey: - rsaKey = pkcs8Key - // basic sanity check - err = rsaKey.Validate() + err = pkcs8Key.Validate() if err != nil { return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err) } // 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 } // 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: return nil, errPemKeyWrongType @@ -86,12 +110,12 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, 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)") } // success! - return rsaKey, nil + return privateKey, nil } // pemCertDecode attempts to decode a pem encoded byte slice and then attempts diff --git a/pkg/pkcs15/pem_parse.go b/pkg/pkcs15/pem_parse.go index dcd899a..2cd1fea 100644 --- a/pkg/pkcs15/pem_parse.go +++ b/pkg/pkcs15/pem_parse.go @@ -1,14 +1,14 @@ package pkcs15 import ( - "crypto/rsa" + "crypto" "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 + key crypto.PrivateKey cert *x509.Certificate } diff --git a/pkg/pkcs15/pem_to_p15.go b/pkg/pkcs15/pem_to_p15.go index 19392f2..c213ddb 100644 --- a/pkg/pkcs15/pem_to_p15.go +++ b/pkg/pkcs15/pem_to_p15.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/rsa" "encoding/asn1" "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 // this file) 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) privateKey := asn1obj.Sequence([][]byte{ // commonObjectAttributes - Label @@ -181,27 +214,7 @@ func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) { asn1obj.BitString([]byte{byte(0b01000000)}), }), - 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(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))), - }), - }), + pubKeyObj, }), }), }), diff --git a/pkg/pkcs15/private_key.go b/pkg/pkcs15/private_key.go index 8c6b0fb..0321551 100644 --- a/pkg/pkcs15/private_key.go +++ b/pkg/pkcs15/private_key.go @@ -1,24 +1,42 @@ 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 func (p15 *pkcs15KeyCert) privateKeyObject() []byte { - // ensure all expected vals are available - p15.key.Precompute() + var privKeyObj []byte - pkey := asn1obj.Sequence([][]byte{ - // P - asn1obj.IntegerExplicitValue(3, p15.key.Primes[0]), - // 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), - }) + switch privKey := p15.key.(type) { + case *rsa.PrivateKey: + privKey.Precompute() - 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 }