Skip to content

Instantly share code, notes, and snippets.

@salrashid123
Last active August 14, 2024 02:03
Show Gist options
  • Save salrashid123/c1de41bf380c1f9a3602675276977e48 to your computer and use it in GitHub Desktop.
Save salrashid123/c1de41bf380c1f9a3602675276977e48 to your computer and use it in GitHub Desktop.
GCE mTLS Cert Extractor
package main
/*
script extracts the tpm encryptedblob (which is the client public/private key
curl -v -H 'Metadata-Flavor: Google' http://metadata/computeMetadata/v1/instance/credentials/certs
{
"encrypted_credentials": "qmiCsZSSGxU8w7MnZoKe0XriLAPYmNGh6t9BqwtI5MqEJS7sv3pGFaHWi+U0oT8zQpuVsVVaEHP2qLwjE11v7xGR8ZCGr2Y5XnvlyEao8rcD8th6hCswLdJiMUATSZviThwziGIGAAzuHOofzEHbp8vDMe4hFsinz1sPvmOrd1aN1HAYPcXH5qpMdo0kgklXhc3O8p+cgVKO2UA7uNqM+/mOHzgxdLZ9f+X8gxAA1WWgr+X2G4W1BUUTPxFTchnzImoandoUo/SMinSVSrvVSkBCEm6GttxBjjgjc0Y391bOsCcEZH2xUx/Ev7+TfI2zqz0nMYQgB8Vly4/07WAPwUX3D8mFAgoTnokAOgHz1mcenH6C7XhKQRKd84wst2e5L35WFAsXY2Dtj7NgAS08T1e1N81VzDIBS7L8B1y9Y9ACu4n2gcKr/uRo36zunKEOd9rBRvNOdpkzaU3RQC97FUJrAUu6At2q9RLE2brphkq5bn11/CXWskNuzApS3MwzG9jeHJ1AAmDYbKicr1jzgMXhSBB63FXMs87kNtqj3X8XiCFJ89Ckewv1r3G9kzHisOQP1JjSfzD/AFOabkg/eFI6WfnriADuNPwMJHCKMsjDik+f6w4rLuOuLjMFm5OCAQkqAIppiWiBIfoT+4P7Z8Eg31s35wBvd82bIuTx8jG5ODYR7DjXa2kKlnytSI6KKPzlu3Q4z2gzkn3vuP5H5qjpWgDripKr2uqGMsYCRTEC0q0AutLtEY4ayU/Xo07kPyC/fBfQgXJuK/4Eq4UGHmbUP3YWazSph4/2gl4YFZfEzVIpeqzDKLaLWq0BhdJ3nCEFi4NFF0f31bF4QOdqsAzaEZJRYqAAflMDbsJA00Xluz8vQnQs29M8gKEk/600qzvCls81MWlqOetmxWdzkOZk5V2/rVms8QBiKUWh0+WIcPf4krcA9XQ/0I1ppFqX87h0dNH6tPk0vuSrUhSrKTSl25+B+6lZ174S319ObSjkaDjjoeFbCboj2mDvst6R5m/0YpYEfMzhI2VnJWUFrL2JH3VSsiMoBUpu/ZjmZ+CHsFaNkqxmjlZ8qLSMf4SvYO5Cl5Yhq9oXeLuajFiteHaSUluU5rEIDo1YbohFFj1YIE+9kSHk5j+asERsXuiKFSUx4g/plk1E/PXVtIbnK54DjR3mLxl+HQ48vrzRLOSxDoFmCKkY3k1qsFNnlzxDn/g0u0XTp7iV/y5T04bPV1dYs3VwudPQgNZxmPX3yIjw8125FQVyzDy5+J9LhpsoyzlU68tXzuA9g4r1P4isav71nveXPtVY",
"key_import_blob": {
"duplicate": "ACCM8+q96Kw/opc27hp2eLDjRMtPdhhsVEKL93DTnRgXv4vuLM9X7ESCgen6UR85aFEKuQvmUxGy9dMNSSAGXplQ6XExv/Sm6tZGQvLZiRSuVYXS+qd2SBVD6SQ=",
"encrypted_seed": "ACA1qmyysQSllFL7JCwI+83e5OVBy5u7UHHUqTQuxnER2AAgRkQm0TWX8S0sSEWkn0wk5+uncbjcFgU1rwr1XYc9hJs=",
"public_area": "AAgACwAAAEAAAAAQACC+l/8i4DIV3sFwI9W0QJj2uSbnj3mrYYaLa1F+YqWGBg=="
}
}
then extracts the root ca from uefi
then you can use both to call the server
$ curl -vvv --cacert root.crt --cert client.crt --key client.key -H "Metadata-Flavor: Google" https://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
root.crt:
```bash
-----BEGIN CERTIFICATE-----
MIIB8jCCAZmgAwIBAgIQMJ4M/U4XlcHHijn6Z/yc1zAKBggqhkjOPQQDAjBJMQsw
CQYDVQQGEwJVUzEgMB4GA1UECgwXR29vZ2xlIENvbXB1dGUgSW50ZXJuYWwxGDAW
BgNVBAMMD2dvb2dsZS5pbnRlcm5hbDAgFw0yNDA4MTIxMTUyMjFaGA8yMDc0MDcz
MTExNTcyMVowSTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF0dvb2dsZSBDb21wdXRl
IEludGVybmFsMRgwFgYDVQQDDA9nb29nbGUuaW50ZXJuYWwwWTATBgcqhkjOPQIB
BggqhkjOPQMBBwNCAARGAmdkrV2T+bHYEeDIBNDgyE9H9cLDxBTZo7iufvfDQPb1
adjWWLnlk3rtHgXBwbqr3mTyxuF/uKaajTYI7T9so2EwXzASBgNVHRMBAf8ECDAG
AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU2jmj7l5rSw0yVb/vlWAY
kK/YBwkwGgYDVR0RBBMwEYIPZ29vZ2xlLmludGVybmFsMAoGCCqGSM49BAMCA0cA
MEQCIFGFNVvHj8Zdo4N5jdoh57HUYGvG8cdfuvyKXvek6YpdAiAnHF2ZqGsMiyHj
8ZzzB7BhZDi4JnNKOJvkggBohD9EdA==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
30:9e:0c:fd:4e:17:95:c1:c7:8a:39:fa:67:fc:9c:d7
Signature Algorithm: ecdsa-with-SHA256
Issuer: C=US, O=Google Compute Internal, CN=google.internal
Validity
Not Before: Aug 12 11:52:21 2024 GMT
Not After : Jul 31 11:57:21 2074 GMT
Subject: C=US, O=Google Compute Internal, CN=google.internal
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:46:02:67:64:ad:5d:93:f9:b1:d8:11:e0:c8:04:
d0:e0:c8:4f:47:f5:c2:c3:c4:14:d9:a3:b8:ae:7e:
f7:c3:40:f6:f5:69:d8:d6:58:b9:e5:93:7a:ed:1e:
05:c1:c1:ba:ab:de:64:f2:c6:e1:7f:b8:a6:9a:8d:
36:08:ed:3f:6c
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Subject Key Identifier:
DA:39:A3:EE:5E:6B:4B:0D:32:55:BF:EF:95:60:18:90:AF:D8:07:09
X509v3 Subject Alternative Name:
DNS:google.internal
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:44:02:20:51:85:35:5b:c7:8f:c6:5d:a3:83:79:8d:da:21:
e7:b1:d4:60:6b:c6:f1:c7:5f:ba:fc:8a:5e:f7:a4:e9:8a:5d:
02:20:27:1c:5d:99:a8:6b:0c:8b:21:e3:f1:9c:f3:07:b0:61:
64:38:b8:26:73:4a:38:9b:e4:82:00:68:84:3f:44:74
```
==============
server.crt
```bash
$ cat server.crt
-----BEGIN CERTIFICATE-----
MIIB7zCCAZWgAwIBAgIQVpSIxheNTu0UQYNxSE112TAKBggqhkjOPQQDAjBJMQsw
CQYDVQQGEwJVUzEgMB4GA1UECgwXR29vZ2xlIENvbXB1dGUgSW50ZXJuYWwxGDAW
BgNVBAMMD2dvb2dsZS5pbnRlcm5hbDAgFw0yNDA4MTIxMTUyMjJaGA8yMDc0MDcz
MTExNTcyMlowQzELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF0dvb2dsZSBDb21wdXRl
IEludGVybmFsMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAASCqUSirOi0uyhsYmgzYk2Kms/CZW2wXjxjqTIflKA5W3rgm04vU5ic
0pU1tLK7CszMUDJbQ5eVSXspIJg1JBG1o2MwYTAMBgNVHRMBAf8EAjAAMA4GA1Ud
DwEB/wQEAwIHgDApBgNVHREEIjAgghhtZXRhZGF0YS5nb29nbGUuaW50ZXJuYWyH
BKn+qf4wFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSAAwRQIh
ALYb5iqMlAFuyopdbiW36h55USM3Srng0TfHSnvD6fbSAiADZe/f6OF6mnqbP3Ob
f4TIrwMr14GU6M+08ZSbD6iYWg==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
56:94:88:c6:17:8d:4e:ed:14:41:83:71:48:4d:75:d9
Signature Algorithm: ecdsa-with-SHA256
Issuer: C=US, O=Google Compute Internal, CN=google.internal
Validity
Not Before: Aug 12 11:52:22 2024 GMT
Not After : Jul 31 11:57:22 2074 GMT
Subject: C=US, O=Google Compute Internal, CN=localhost
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:82:a9:44:a2:ac:e8:b4:bb:28:6c:62:68:33:62:
4d:8a:9a:cf:c2:65:6d:b0:5e:3c:63:a9:32:1f:94:
a0:39:5b:7a:e0:9b:4e:2f:53:98:9c:d2:95:35:b4:
b2:bb:0a:cc:cc:50:32:5b:43:97:95:49:7b:29:20:
98:35:24:11:b5
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature
X509v3 Subject Alternative Name:
DNS:metadata.google.internal, IP Address:169.254.169.254
X509v3 Extended Key Usage: critical
TLS Web Server Authentication
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:45:02:21:00:b6:1b:e6:2a:8c:94:01:6e:ca:8a:5d:6e:25:
b7:ea:1e:79:51:23:37:4a:b9:e0:d1:37:c7:4a:7b:c3:e9:f6:
d2:02:20:03:65:ef:df:e8:e1:7a:9a:7a:9b:3f:73:9b:7f:84:
c8:af:03:2b:d7:81:94:e8:cf:b4:f1:94:9b:0f:a8:98:5a
```
client.crt
```bash
$ cat client.crt
-----BEGIN CERTIFICATE-----
MIIB7jCCAZSgAwIBAgIRAIL2RGietbLMgTX/KWEdv54wCgYIKoZIzj0EAwIwSTEL
MAkGA1UEBhMCVVMxIDAeBgNVBAoMF0dvb2dsZSBDb21wdXRlIEludGVybmFsMRgw
FgYDVQQDDA9nb29nbGUuaW50ZXJuYWwwHhcNMjQwODEzMjMwNTI2WhcNMjQwODIw
MjMxMDI2WjBAMQswCQYDVQQGEwJVUzEeMBwGA1UECgwVR29vZ2xlIENvbXB1dGUg
RW5naW5lMREwDwYDVQQDDAhtdGxzdGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABEsWIDi7KGTc+MBQ82/C4r5fxXPrZ+TBfqj4zQ1zizRopOw5u6iLfTbBtORT
1+ROR1tlwpYDEkMJ4QghS7EkH4+jZjBkMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/
BAQDAgeAMCwGA1UdEQQlMCOCIW10bHN0ZXN0LmMuc3Jhc2hpZC10ZXN0Mi5pbnRl
cm5hbDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjAKBggqhkjOPQQDAgNIADBFAiBH
9KD/218EYr9NTNx0VpTiOMShYEhy9rTcude8FbH4vQIhAM6KFN4NbC0fVToWS++S
I0lgIH0Qnlg8l4UsxnQUVuwr
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
82:f6:44:68:9e:b5:b2:cc:81:35:ff:29:61:1d:bf:9e
Signature Algorithm: ecdsa-with-SHA256
Issuer: C=US, O=Google Compute Internal, CN=google.internal
Validity
Not Before: Aug 13 23:05:26 2024 GMT
Not After : Aug 20 23:10:26 2024 GMT
Subject: C=US, O=Google Compute Engine, CN=mtlstest
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:4b:16:20:38:bb:28:64:dc:f8:c0:50:f3:6f:c2:
e2:be:5f:c5:73:eb:67:e4:c1:7e:a8:f8:cd:0d:73:
8b:34:68:a4:ec:39:bb:a8:8b:7d:36:c1:b4:e4:53:
d7:e4:4e:47:5b:65:c2:96:03:12:43:09:e1:08:21:
4b:b1:24:1f:8f
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature
X509v3 Subject Alternative Name:
DNS:mtlstest.c.srashid-test2.internal
X509v3 Extended Key Usage: critical
TLS Web Client Authentication
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:45:02:20:47:f4:a0:ff:db:5f:04:62:bf:4d:4c:dc:74:56:
94:e2:38:c4:a1:60:48:72:f6:b4:dc:b9:d7:bc:15:b1:f8:bd:
02:21:00:ce:8a:14:de:0d:6c:2d:1f:55:3a:16:4b:ef:92:23:
49:60:20:7d:10:9e:58:3c:97:85:2c:c6:74:14:56:ec:2b
```
*/
import (
"context"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"io"
"net"
"slices"
"google.golang.org/protobuf/encoding/protojson"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/uefi"
"cloud.google.com/go/compute/metadata"
pb "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/agentcrypto/credentials"
"github.com/google/go-tpm-tools/client"
pbt "github.com/google/go-tpm-tools/proto/tpm"
"github.com/google/go-tpm-tools/simulator"
"github.com/google/go-tpm/tpmutil"
"github.com/google/tink/go/aead/subtle"
)
var TPMDEVICES = []string{"/dev/tpm0", "/dev/tpmrm0"}
func openTPM(path string) (io.ReadWriteCloser, error) {
if slices.Contains(TPMDEVICES, path) {
return tpmutil.OpenTPM(path)
} else if path == "simulator" {
return simulator.GetWithFixedSeedInsecure(1073741825)
} else {
return net.Dial("tcp", path)
}
}
const (
googleGUID = "a2858e46-a37f-456a-8c79-0c1fe48b65ff"
// googleRootCACertEFIVarName is predefined string part of the UEFI variable name that holds Root CA cert.
googleRootCACertEFIVarName = "InstanceRootCACertificate"
)
func main() {
// f, err := os.ReadFile("certs.json")
// if err != nil {
// panic(err)
// }
c, err := metadata.GetWithContext(context.Background(), "instance/credentials/certs")
if err != nil {
panic(err)
}
f := []byte(c)
res := &pb.GuestCredentialsResponse{}
if err := protojson.Unmarshal(f, res); err != nil {
panic(err)
}
dek, err := extractKey(res.KeyImportBlob)
if err != nil {
panic(err)
}
err = protojson.Unmarshal(f, res)
if err != nil {
panic(err)
}
fmt.Printf("DEK %s\n", hex.EncodeToString(dek))
plaintext, err := decrypt(dek, res.GetEncryptedCredentials(), nil)
if err != nil {
panic(err)
}
fmt.Printf("plaintext \n%s\n", string(plaintext))
googleRootCACertUEFIVar := uefi.VariableName{Name: googleRootCACertEFIVarName, GUID: googleGUID}
v, err := readRootCACert(googleRootCACertUEFIVar)
if err != nil {
panic(err)
}
fmt.Printf("RootCA: \n%s\n", v.Content)
}
func readRootCACert(name uefi.VariableName) (*uefi.Variable, error) {
rootCACert, err := uefi.ReadVariable(name)
if err != nil {
return nil, fmt.Errorf("unable to read root CA cert file contents: %w", err)
}
if _, err := parseCertificate(rootCACert.Content); err != nil {
return nil, fmt.Errorf("unable to verify Root CA cert: %w", err)
}
fmt.Printf("Successfully read root CA Cert from %+v", name)
return rootCACert, nil
}
// parseCertificate validates certificate is in valid PEM format.
func parseCertificate(cert []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(cert)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM certificate")
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
return x509Cert, nil
}
func extractKey(importBlob *pbt.ImportBlob) ([]byte, error) {
rwc, err := openTPM("/dev/tpm0")
defer rwc.Close()
ek, err := client.EndorsementKeyECC(rwc)
if err != nil {
return nil, fmt.Errorf("failed to load a key from TPM: %w", err)
}
defer ek.Close()
dek, err := ek.Import(importBlob)
if err != nil {
return nil, fmt.Errorf("failed to decrypt import blob: %w", err)
}
return dek, nil
}
// encrypt encrypts plain text using AES GCM algorithm.
func encrypt(aesKey []byte, plainText []byte, associatedData []byte) ([]byte, error) {
cipher, err := subtle.NewAESGCM(aesKey)
if err != nil {
return nil, fmt.Errorf("failed to initialize cipher: %v", err)
}
return cipher.Encrypt(plainText, associatedData)
}
// decrypt decrypts AES GCM encrypted cipher text.
func decrypt(aesKey []byte, cipherText []byte, associatedData []byte) ([]byte, error) {
cipher, err := subtle.NewAESGCM(aesKey)
if err != nil {
return nil, fmt.Errorf("failed to initialize cipher: %v", err)
}
return cipher.Decrypt(cipherText, associatedData)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment