Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Last active September 14, 2024 05:56
Show Gist options
  • Save salrashid123/10320c153ad6acdc31854c9775c43c0d to your computer and use it in GitHub Desktop.
Save salrashid123/10320c153ad6acdc31854c9775c43c0d to your computer and use it in GitHub Desktop.
Issue CA-signed certificate for TPM public key using (-force_pubkey)

Issue CA-signed certificate using TPM public key

Rough procedure to force sign/issue a CA signed certificate that is tied to a TPM's public key.

This procedure uses the -force_pubkey key parameter for openssl

When a certificate is created set its public key to key instead of the key in the certificate or certificate request. This option is useful for creating certificates where the algorithm can't normally sign requests, for example DH.

The format or key can be specified using the -keyform option.

basically, w'ere going to 'force' the CA to override/ignore the public key in the CSR and use the public key we provide that is associated with the TPM:

on TPM get ekpub

tpm2_createek -c A-ek.ctx -G rsa -u A-ek.pub
tpm2_readpublic -c A-ek.ctx -o A-ek.pem -f PEM

# more A-ek.pem 
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyLLB37zQTi3KfKridPpY
tj9yKm0ci/QUGqrzBsVVqxqOsQUxocsaKMZPIO7VxJlJd8KHWMoGY6f1VOdNUFCN
ufg5WMqA/t6rXvjF4NtPTvR05dCV4JegBBDnOjF9NgmV67+NgAm3afq/Z1qvJ336
WUop2prbTWpseNtdlp2+4TOBSsNZgsum3CFr40qIsa2rb9xFDrqoMTVkgKGpJk+z
ta+pcxGXYFJfU9sb7F7cs3e+TzjucGFcpVEiFzVq6Mga8cmh32sufM/PuifVYSLi
BYV4s4c53gVq7v0Oda9LqaxT2A9EmKopcWUU8CEgbsBxhmVAhsnKwLDmJYKULkAk
uwIDAQAB
-----END PUBLIC KEY-----

on CA issuer

first get the public key in whatever way you want:

gcloud compute instances get-shielded-identity attestor --format="value(encryptionKey.ekPub)" > ek.pem


# use a simple CA
git clone https://github.com/salrashid123/ca_scratchpad.git

cd ca_scratchpad
mkdir -p ca/root-ca/private ca/root-ca/db crl certs
chmod 700 ca/root-ca/private
cp /dev/null ca/root-ca/db/root-ca.db
cp /dev/null ca/root-ca/db/root-ca.db.attr

echo 01 > ca/root-ca/db/root-ca.crt.srl
echo 01 > ca/root-ca/db/root-ca.crl.srl

openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:2048 \
      -pkeyopt rsa_keygen_pubexp:65537 -out ca/root-ca/private/root-ca.key

openssl req -new  -config single-root-ca.conf  -key ca/root-ca/private/root-ca.key \
   -out ca/root-ca.csr  

openssl ca -selfsign     -config single-root-ca.conf  \
   -in ca/root-ca.csr     -out ca/root-ca.crt  \
   -extensions root_ca_ext

Now create a temp key and CSR (we wont' really use these)

export NAME=attestor
export SAN=DNS:attestor.domain.com
openssl req -new     -config server.conf \
     -out certs/$NAME.csr   \
     -keyout certs/$NAME.key \
     -subj "/C=US/O=Google/OU=Enterprise/CN=attestor.domain.com"

openssl x509 \
       		-in certs/$NAME.csr \
       		-req  -extfile single-root-ca.conf	-extensions server_ext	-CA ca/root-ca.crt -CAkey ca/root-ca/private/root-ca.key \
       		-force_pubkey ../ek.pem \
       		-keyform PEM \
       		-out certs/$NAME.crt -extensions server_ext


# note that the public key is the same
openssl x509 -pubkey -noout -in certs/$NAME.crt

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyLLB37zQTi3KfKridPpY
tj9yKm0ci/QUGqrzBsVVqxqOsQUxocsaKMZPIO7VxJlJd8KHWMoGY6f1VOdNUFCN
ufg5WMqA/t6rXvjF4NtPTvR05dCV4JegBBDnOjF9NgmV67+NgAm3afq/Z1qvJ336
WUop2prbTWpseNtdlp2+4TOBSsNZgsum3CFr40qIsa2rb9xFDrqoMTVkgKGpJk+z
ta+pcxGXYFJfU9sb7F7cs3e+TzjucGFcpVEiFzVq6Mga8cmh32sufM/PuifVYSLi
BYV4s4c53gVq7v0Oda9LqaxT2A9EmKopcWUU8CEgbsBxhmVAhsnKwLDmJYKULkAk
uwIDAQAB
-----END PUBLIC KEY-----


# the certificate specs 
openssl x509 -in certs/$NAME.crt -text -noout

Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            1b:eb:ae:98:ff:60:87:28:71:59:2e:e2:f1:e1:69:f5:02:04:84:46
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Google, OU = Enterprise, CN = Enterprise Root CA
        Validity
            Not Before: Jun 14 00:59:01 2023 GMT
            Not After : Oct 26 00:59:01 2024 GMT
        Subject: C = US, O = Google, OU = Enterprise, CN = attestor.domain.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:c8:b2:c1:df:bc:d0:4e:2d:ca:7c:aa:e2:74:fa:
                    58:b6:3f:72:2a:6d:1c:8b:f4:14:1a:aa:f3:06:c5:
                    55:ab:1a:8e:b1:05:31:a1:cb:1a:28:c6:4f:20:ee:
                    d5:c4:99:49:77:c2:87:58:ca:06:63:a7:f5:54:e7:
                    4d:50:50:8d:b9:f8:39:58:ca:80:fe:de:ab:5e:f8:
                    c5:e0:db:4f:4e:f4:74:e5:d0:95:e0:97:a0:04:10:
                    e7:3a:31:7d:36:09:95:eb:bf:8d:80:09:b7:69:fa:
                    bf:67:5a:af:27:7d:fa:59:4a:29:da:9a:db:4d:6a:
                    6c:78:db:5d:96:9d:be:e1:33:81:4a:c3:59:82:cb:
                    a6:dc:21:6b:e3:4a:88:b1:ad:ab:6f:dc:45:0e:ba:
                    a8:31:35:64:80:a1:a9:26:4f:b3:b5:af:a9:73:11:
                    97:60:52:5f:53:db:1b:ec:5e:dc:b3:77:be:4f:38:
                    ee:70:61:5c:a5:51:22:17:35:6a:e8:c8:1a:f1:c9:
                    a1:df:6b:2e:7c:cf:cf:ba:27:d5:61:22:e2:05:85:
                    78:b3:87:39:de:05:6a:ee:fd:0e:75:af:4b:a9:ac:
                    53:d8:0f:44:98:aa:29:71:65:14:f0:21:20:6e:c0:
                    71:86:65:40:86:c9:ca:c0:b0:e6:25:82:94:2e:40:
                    24:bb
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        9e:31:ac:d4:22:39:e5:31:35:bd:85:7e:f8:43:4e:dc:f6:26:
        bf:01:56:ed:bf:00:8b:da:49:be:d5:4f:d9:6e:49:73:21:71:
        b2:c6:bc:44:8e:68:7c:bb:a1:0e:ad:24:0f:1b:d2:ab:f6:b6:
        2a:f7:39:b8:81:58:8d:57:ff:1a:a8:ad:6a:9f:cf:5b:39:12:
        66:8a:e4:2b:2c:9c:2a:de:41:a1:ba:cc:a7:94:a1:52:66:b0:
        f8:bf:90:d6:68:55:52:ee:a8:08:c4:8a:b0:f8:dc:ce:5a:17:
        32:80:57:c7:8a:59:79:0d:99:65:49:1a:45:fe:7b:67:53:e4:
        14:50:02:51:b2:90:f1:8f:8b:1f:41:87:bb:b1:7f:12:88:dc:
        80:75:d8:e4:de:ce:8d:ca:5d:de:d8:70:7a:72:05:cb:fa:4a:
        a0:16:a5:38:98:2c:6a:e2:8c:b8:2f:b4:d2:3a:e4:57:03:a5:
        bc:4c:36:14:01:7e:41:48:67:51:88:b2:c9:88:4f:f1:2d:00:
        45:ea:05:6d:fe:e4:2f:31:cb:dc:4e:4d:0b:ab:da:6b:31:6c:
        c3:a4:6b:c8:88:44:af:d7:06:66:8e:27:5b:07:b1:45:48:63:
        d8:96:69:07:7d:0e:67:fe:41:47:2e:49:2b:22:9e:31:dc:91:
        52:de:ad:8b

TODO:

Set the attributes in the certificate that indicate a TPM:

pg 24 of TCG EK Credential Profile

The first Extended Key Usage, SAN extension we can find easily

That just tells us that this certificate was part of a Google TPM (see TCG TPM Vendor ID Registry )

For completeness, the X509v3 Subject Directory Attributes, is actually OID 2.23.133.2.16: tcg-at-tpmSpecification which you can see in an asn1 parser output below. The values denotes the TPM level (TPM 2.0)

ASNParsed Key

        X509v3 extensions:
            Authority Information Access: 
                CA Issuers - URI:http://pki.infineon.com/OptigaRsaMfrCA041/OptigaRsaMfrCA041.crt

            X509v3 Key Usage: critical
                Key Encipherment
            X509v3 Subject Alternative Name: critical
                DirName:/2.23.133.2.1=id:49465800/2.23.133.2.2=SLM 9670/2.23.133.2.3=id:0D0B
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 CRL Distribution Points: 

                Full Name:
                  URI:http://pki.infineon.com/OptigaRsaMfrCA041/OptigaRsaMfrCA041.crl

            X509v3 Certificate Policies: 
                Policy: 1.2.276.0.68.1.20.1

            X509v3 Authority Key Identifier: 
                keyid:56:F6:B5:97:39:63:01:18:5B:91:D8:00:AC:EE:91:62:19:E3:A6:BB

            X509v3 Extended Key Usage: 
                2.23.133.8.1
            X509v3 Subject Directory Attributes: 
                0.0...g....1.0...2.0.......

References


using golang

package main

import (
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
	"time"
)

var ()

const (
	ekPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyLLB37zQTi3KfKridPpY
tj9yKm0ci/QUGqrzBsVVqxqOsQUxocsaKMZPIO7VxJlJd8KHWMoGY6f1VOdNUFCN
ufg5WMqA/t6rXvjF4NtPTvR05dCV4JegBBDnOjF9NgmV67+NgAm3afq/Z1qvJ336
WUop2prbTWpseNtdlp2+4TOBSsNZgsum3CFr40qIsa2rb9xFDrqoMTVkgKGpJk+z
ta+pcxGXYFJfU9sb7F7cs3e+TzjucGFcpVEiFzVq6Mga8cmh32sufM/PuifVYSLi
BYV4s4c53gVq7v0Oda9LqaxT2A9EmKopcWUU8CEgbsBxhmVAhsnKwLDmJYKULkAk
uwIDAQAB
-----END PUBLIC KEY-----
`

	rootCertPem = `-----BEGIN CERTIFICATE-----
MIIDnzCCAoegAwIBAgIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEP
MA0GA1UECgwGR29vZ2xlMRMwEQYDVQQLDApFbnRlcnByaXNlMRswGQYDVQQDDBJF
bnRlcnByaXNlIFJvb3QgQ0EwHhcNMjMwNjE0MDA1NTMxWhcNMzMwNjEzMDA1NTMx
WjBQMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGR29vZ2xlMRMwEQYDVQQLDApFbnRl
cnByaXNlMRswGQYDVQQDDBJFbnRlcnByaXNlIFJvb3QgQ0EwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQC9+EC2NuEp27L5ueFHwgTlwkx0/Q5j1kMUI27+
y9bViiNh783ZBN5bFOLF2xi2incsA2xip9ZhMd78f8mVxXbtuLogRvDGLC3prSxw
XpQHWbX0u50CxNclFrrMvlnkx4LnEjY+duMNEfjMLDOlwCSEIYq5PdWndeKT6YqL
M9v93ZDdx7G3n+MXL9wGDQpTnH0rbi5jE+76lIi5ddpqRZNrD/k/82e4KxFDzHkf
qpkWcF/hLW2qLGY+Oq5+d8GQxhXy1wLg95HEV1ITDPucyLUlt4Grl3f5LfDYKEC/
Z4M1KTS4R4SEctIVWL+K5bZYQc9zlvCbDBRyHDX8EB/0mM0hAgMBAAGjgYMwgYAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAZkyP5Q
2867vD6GErXSjDbVSG/aMB8GA1UdIwQYMBaAFAZkyP5Q2867vD6GErXSjDbVSG/a
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOC
AQEAZRt1L7REMiPiHsw/N5PcsFgIPXmgiu4nLNvpAmsCulGbOoKUXZ/cILtCH4/Z
MBOiL/gGXt6yLM3rQpaBOdJRc9Qz3W1vPNOSRW7sTlAv0QJT3eWdIU+5juP6Tu4q
UoOHopBMK4ficE7rc9vl+/YnHVLu22ZKGsH2CaKsG2j0Afa7it1Yl7EI/5iPu2Y8
ydAkvjxpJhWDJ2IbmlnVPqlMDQ/PqH7XnhNGKtxF5xLqWqKpfaAGD7Aweaoa3dfM
au/wUUFSE2Dd3pLIK3s6DYXmV0Hocrqc+4QrJs/KloErKNvqyqzbHqQpMNGypaYx
6bLZaJVaPoNjm0kZhIOaLgWEUA==
-----END CERTIFICATE-----`

	rootKeyPem = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9+EC2NuEp27L5
ueFHwgTlwkx0/Q5j1kMUI27+y9bViiNh783ZBN5bFOLF2xi2incsA2xip9ZhMd78
f8mVxXbtuLogRvDGLC3prSxwXpQHWbX0u50CxNclFrrMvlnkx4LnEjY+duMNEfjM
LDOlwCSEIYq5PdWndeKT6YqLM9v93ZDdx7G3n+MXL9wGDQpTnH0rbi5jE+76lIi5
ddpqRZNrD/k/82e4KxFDzHkfqpkWcF/hLW2qLGY+Oq5+d8GQxhXy1wLg95HEV1IT
DPucyLUlt4Grl3f5LfDYKEC/Z4M1KTS4R4SEctIVWL+K5bZYQc9zlvCbDBRyHDX8
EB/0mM0hAgMBAAECggEABPR0kKQgDZjRw154LChY+xqtJpiCn3NDWuM9NgdgTj/N
AjUQZg6Q06iY7R70ty8nolXhBfY5et3Dgou9OOs2eccZkLW0+kBE/s0x5WjsHMB3
5N8fXmb+sxSTkPtOii9zgD4q/MhaTfkX55BoFPTM5wbecMw2dkiWfTPoVY7n2Chb
iYEdd/xwey2mBWG90jmVfISElTVxYIbzIdUSYc4P3+Mkv73UmNQa2CWgzmIekEKY
J6e93ouxDAXcZluRqik4ng7QzO6uz9eU/R481AtBRG9h2sApF9sHVXUhFAJUUYaP
LzGQhIb4O7DM5HfTtzaD8ThaKsVxViAE1rAtLUTjiQKBgQC+MtGkfDAVcKM86V4q
zQkcurcDk0QjGZ5Q7UaQyQ8qH/o9GYXBwb6fNqbdKn47dDZ4J22iAz7LMwxqILHP
49JsWdthIwPcc/vm/PgUv+My64LmBBOL8eXLzobjQBRMZUlcp1GgFEi7LYLY9QuL
eNpuMzTdzQ58/xMwK8wu3dKW6QKBgQD/sSwVHfKDaDD5fTP7QWZSoNN1D1gyxRz7
iDyoxBjJorFy29Doo6GHcsiTjPMqHWHPk5NGZTrBvmwvx1d0GlU1rVxTGS2vlO08
vgZiBNoUhEcsMUtfZjG1R5AGXwJLG5j5mAcQjFJWJ/wnDidEfqkCOr3bcu/3P+m/
HQj5bXoReQKBgQCCeCHPD1F8o5OWiQ8LlTKW/o3kafTnjv9GzD0HvKQmZWEm4Icc
bmbwmHDGbgvBhOqIaaOO62L7yLzPYDYZQVJq+QjADWDsrLMztHwI38heeaeFWW5/
JSRZNgbZhG9oKGK2qCAaC1AI9iM36IBO4EwWJBIzxCIYJHFpLeIWlBuNYQKBgQC5
mw2p4oxJMVfjsywJpka1kkMNRW87o3RPbmzCAV8Q90WiX8h9J1K49Ox+lyFon92B
hdmxjwgg3a7A3A4ynG7gQRC0zaEpRI0Vu8b8XhNEM2VKwhm5jYBl3CXDSjJq/QSF
ogL20zVtjybt3stIYD0k/ikVZJCmxUFTVqtODFdA2QKBgA+uFIKNTBKn9frOL9kC
KgSYyUu9zZhIEiG5eg8Is7rZEdsDQ6L1Ld0A342CzrD2VNZmlhGd0+bz14fYEkvC
JO6jbgg/OL4fa3HKS7UibLXYZGFMtHjA9Wk5nnrKT5K6qJBz95srp3wlvsH3Ivw0
p/JG0riyBVMxpqOB5NdvIs80
-----END PRIVATE KEY-----
`
)

func main() {

	rblock, _ := pem.Decode([]byte(rootKeyPem))
	r, err := x509.ParsePKCS8PrivateKey(rblock.Bytes)
	if err != nil {
		fmt.Println(err)
		return
	}

	/// *********************************************************
	var notBefore time.Time
	notBefore = time.Now()

	notAfter := notBefore.Add(time.Hour * 24 * 365)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to generate serial number: %s", err)
		os.Exit(1)
	}

	block, _ := pem.Decode([]byte(ekPem))
	if block == nil {
		fmt.Fprintf(os.Stderr, "Failed to generate serial number: %s", err)
		os.Exit(1)
	}
	key, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to generate serial number: %s", err)
		os.Exit(1)
	}
	
	rcblock, _ := pem.Decode(rootCertPem)

	rcert, err := x509.ParseCertificate(rcblock.Bytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to load root cert: %s", err)
		os.Exit(1)
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization:       []string{"Acme Co"},
			OrganizationalUnit: []string{"Enterprise"},
			Locality:           []string{"Mountain View"},
			Province:           []string{"California"},
			Country:            []string{"US"},
			CommonName:         "foo",
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,
		KeyUsage:  x509.KeyUsageDigitalSignature,
		//ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		BasicConstraintsValid: true,
		IsCA:                  false,
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, rcert, key, r)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create certificate: %s\n", err)
		os.Exit(1)
	}

	p, err := x509.ParseCertificate(derBytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to  parse certificate: %s", err)
		os.Exit(1)
	}
	fmt.Printf("cert Issuer %s\n", p.Issuer)
	c := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	fmt.Printf("cert \n%s\n", c)

	k1 := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: block.Bytes})
	fmt.Printf("key1 \n%s\n", k1)

	pubkey_bytes, err := x509.MarshalPKIXPublicKey(p.PublicKey)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to marshall certificate publcikey: %s", err)
		os.Exit(1)
	}
	k2pem := pem.EncodeToMemory(
		&pem.Block{
			Type:  "PUBLIC KEY",
			Bytes: pubkey_bytes,
		},
	)

	fmt.Printf("key2 \n%s\n", k2pem)

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment