Simple http client/server in golang where the private key used in the connection is generated and embedded within a Trusted Platform Module.
This repo mostly uses the crypto.Signer
implementation from my own library implementing that interface for TPM("github.com/salrashid123/signer/tpm"
) and not the one from fromgo-tpm-tools.
The steps here will create two GCP VMs with TPMs create TPM based RSA keys, generate a CSR using those keys, then an external CA will issue an x509 cert using that csr.
Finally, the client will establish an mTLS https connection to the server
NOTE: this repo is not supported by Google
NOTE:
Unable to Open TPM: open /dev/tpm0: device or resource busy
You can find the source here
NOTE:
Unable to Open TPM: open /dev/tpm0: device or resource busy
Other references:
RSA-PSS padding:
First create a server and install golang go version go1.16.5 linux/amd64
gcloud compute instances create ts-server \
--zone=us-central1-a --machine-type=n1-standard-1 \
--tags tpm --no-service-account --no-scopes \
--shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring \
--image=debian-10-buster-v20210916 --image-project=debian-cloud
gcloud compute firewall-rules create allow-https-tpm --action=ALLOW --rules=tcp:8081 --source-ranges=0.0.0.0/0 --target-tags=tpm
gcloud compute ssh ts-server
# in vm:
sudo su -
apt-get update
apt-get install wget git
wget https://golang.org/dl/go1.16.5.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.16.5.linux-amd64.tar.gz
# get the source repo
git clone https://github.com/salrashid123/go_tpm_https_embed.git
cd go_tpm_https_embed
# generate CSR
go run src/csr/csr.go --pemCSRFile certs/server.csr --dnsSAN server.domain.com -v 20 -alsologtostderr
# generate the server certificate
cd certs/
mkdir new_certs
openssl ca -config openssl.conf -in server.csr -out server.crt -subj "/C=US/ST=California/L=Mountain View/O=Google/OU=Enterprise/CN=server.domain.com"
# run the server
go run src/server/server.go -cacert certs/CA_crt.pem -servercert certs/server.crt -tpmfile k.bin -port :8081
You can test the config locally using the pre-generated client certificates provided in this repo
export SERVER_IP=`gcloud compute instances describe ts-server --format="value(networkInterfaces.accessConfigs[0].natIP)"`
curl -v -H "Host: server.domain.com" --resolve server.domain.com:8081:$SERVER_IP --cert certs/client.crt --key certs/client.key --cacert certs/CA_crt.pem https://server.domain.com:8081/
gcloud compute instances create ts-client \
--zone=us-central1-a --machine-type=n1-standard-1 \
--tags tpm --no-service-account --no-scopes \
--shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring \
--image=debian-10-buster-v20210916 --image-project=debian-cloud
gcloud compute ssh ts-client
sudo su -
apt-get update
apt-get install wget git
wget https://golang.org/dl/go1.16.5.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.16.5.linux-amd64.tar.gz
# get the source repo
git clone https://github.com/salrashid123/go_tpm_https_embed.git
cd go_tpm_https_embed
# generate the client cert csr
go run src/csr/csr.go --pemCSRFile certs/kclient.csr --dnsSAN client.domain.com -v 20 -alsologtostderr
cd certs/
mkdir new_certs
openssl ca -config openssl.conf -in kclient.csr -out kclient.crt -subj "/C=US/ST=California/L=Mountain View/O=Google/OU=Enterprise/CN=client.domain.com"
# run the client using the server's IPaddress
echo $SERVER_IP
go run src/client/client.go -cacert certs/CA_crt.pem -tpmfile k.bin --address $SERVER_IP
At this point, you should see a simple ‘ok’ from the sever
This repo includes a TLS wrapper function that uses the tpm crypto.Signer.
At the core is a Sign()
function which loads the TPM and signs. As mentioned, its serial access to /dev/tpm0
so the code loads it every invocation (i know, it crappy)
We also utilize RSA-PSS Algorithm here for TLS 1.3:
func (t TPM) Sign(rr io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
t.refreshMutex.Lock()
defer t.refreshMutex.Unlock()
var err error
rwc, err := tpm2.OpenTPM(t.TpmDevice)
if err != nil {
return []byte(""), fmt.Errorf("google: Public: Unable to Open TPM: %v", err)
}
defer rwc.Close()
khBytes, err := ioutil.ReadFile(t.TpmHandleFile)
if err != nil {
return []byte(""), fmt.Errorf("ContextLoad read file for kh: %v", err)
}
kh, err := tpm2.ContextLoad(rwc, khBytes)
if err != nil {
return []byte(""), fmt.Errorf("ContextLoad failed for kh: %v", err)
}
defer tpm2.FlushContext(rwc, kh)
t.k, err = tpm2tools.NewCachedKey(rwc, tpm2.HandleEndorsement, unrestrictedKeyParams, kh)
if err != nil {
return []byte(""), fmt.Errorf("Couldnot load CachedKey: %v", err)
}
s, err := t.k.GetSigner()
if err != nil {
return []byte(""), fmt.Errorf("Couldnot get Signer: %v", err)
}
opts = &rsa.PSSOptions{
Hash: crypto.SHA256,
SaltLength: rsa.PSSSaltLengthAuto,
}
return s.Sign(rr, digest, opts)
}
Which also means the default Key Template we use in generating the CSR and Cert will utilize RSA-PSS
src/csr/csr.go
:var (
unrestrictedKeyParams = tpm2.Public{
Type: tpm2.AlgRSA,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin |
tpm2.FlagUserWithAuth | tpm2.FlagSign,
AuthPolicy: []byte{},
RSAParameters: &tpm2.RSAParams{
Sign: &tpm2.SigScheme{
Alg: tpm2.AlgRSAPSS,
Hash: tpm2.AlgSHA256,
},
KeyBits: 2048,
},
}
)
/// ...
var csrtemplate = x509.CertificateRequest{
Subject: pkix.Name{
Organization: []string{"Acme Co"},
OrganizationalUnit: []string{"Enterprise"},
Locality: []string{"Mountain View"},
Province: []string{"California"},
Country: []string{"US"},
CommonName: *san,
},
DNSNames: []string{*san},
SignatureAlgorithm: x509.SHA256WithRSAPSS,
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrtemplate, s)
if err != nil {
glog.Fatalf("Failed to create CSR: %s", err)
}
Also included are two utility functions to flush all TPM handles (incase you’ve used up all of them)
go run src/util/util.go --mode flush -v 20 -alsologtostderr
And a a function to print the public RSA key for a given key (you can ofcourse also derive that from the certificate or csr)
go run src/util/util.go --mode print --keyfile k.bin -v 20 -alsologtostderr
This site supports webmentions. Send me a mention via this form.