setup of Google Proxies for Enterprise Certificates (GA) with SoftHSM
This sample will embed a device certificate and key into SoftHSM and then access softHSM for mTLS using the GCP proxy.
A default GCS client uses the enterprise proxy transparently to access gcp resources via mTLS
First step is to setup device certificates. This isn't covered in this repo but just assume
the keypair user10.crt
, user10.key
represents a user's device cert provisioned by an admin
Initialize softhsm
mkdir /tmp/softhsm_tokens
create file /tmp/softhsm.conf
with
log.level = DEBUG
objectstore.backend = file
directories.tokendir = /tmp/softhsm_tokens
slots.removable = true
import the certs
sudo apt-get install softhsm opensc
export SOFTHSM2_CONF=/tmp/softhsm.conf
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --list-mechanisms --slot-index 0
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --slot-index=0 --init-token --label="token1" --so-pin="123456"
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --label="token1" --init-pin --so-pin "123456" --pin mynewpin
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --slot-index=0 --init-token --label="token1" --so-pin="123456"
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --label="token1" --init-pin --so-pin "123456" --pin mynewpin
### convert to der
openssl x509 -in user10.crt -out sts.crt.der -outform DER
openssl rsa -inform pem -in user10.key -outform der -out sts.key.der
openssl x509 -pubkey -noout -in user10.crt > user10pub.pem
openssl rsa -pubin -inform PEM -in user10pub.pem -outform DER -out user10pub.der
# import key and cert
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --pin mynewpin \
--write-object sts.crt.der --type cert --id 10 --label keylabel3 --slot-index 0
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --pin mynewpin \
--write-object sts.key.der --type privkey --id 10 --label keylabel3 --slot-index 0
pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --pin mynewpin \
--write-object user10pub.der --type pubkey --id 10 --label keylabel3 --slot-index 0
print the token and slots
$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --list-token-slots
Available slots:
Slot 0 (0x51aac4a2): SoftHSM slot ID 0x51aac4a2
token label : token1
token manufacturer : SoftHSM project
token model : SoftHSM v2
token flags : login required, rng, token initialized, PIN initialized, other flags=0x20
hardware version : 2.6
firmware version : 2.6
serial num : 7b07faced1aac4a2
pin min/max : 4/255
Slot 1 (0x1): SoftHSM slot ID 0x1
token state: uninitialized
$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --list-objects --pin mynewpin
Using slot 0 with a present token (0x51aac4a2)
Public Key Object; RSA 2048 bits
label: keylabel3
ID: 10
Usage: encrypt, verify, wrap
Access: none
Private Key Object; RSA
label: keylabel3
ID: 10
Usage: decrypt, sign, unwrap
Access: sensitive
Certificate Object; type = X.509 cert
label: keylabel3
subject: DN: L=US, O=Google, OU=Enterprise, CN=user10.esodemoapp2.com
serial: 1C
ID: 10
create sdk config file api_cert_config.config
note the slot below..your value will be different. you can find it with the command above
{
"cert_configs": {
"pkcs11": {
"label": "keylabel3",
"user_pin": "mynewpin",
"slot": "0x51aac4a2",
"module": "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"
}
},
"libs": {
"ecp": "/usr/lib/google-cloud-sdk/bin/ecp",
"ecp_client": "/usr/lib/google-cloud-sdk/platform/enterprise_cert/libecp.so",
"tls_offload": "/usr/lib/google-cloud-sdk/platform/enterprise_cert/libtls_offload.so"
},
"version": 1
}
install enterprise proxy
gcloud components install enterprise-certificate-proxy
export GOOGLE_API_USE_CLIENT_CERTIFICATE=true
export GOOGLE_API_CERTIFICATE_CONFIG=/path/to/api_cert_config.json
export ENABLE_ENTERPRISE_CERTIFICATE_LOGS=1
export SOFTHSM2_CONF=/tmp/softhsm.conf
Now run any gcs client as is
package main
import (
"context"
"fmt"
"io"
"os"
"cloud.google.com/go/storage"
)
const (
projectID = "your-project"
bucketName = "your-bucket"
objectName = "foo.txt"
)
func main() {
ctx := context.Background()
storageClient, err := storage.NewClient(ctx)
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}
defer storageClient.Close()
bkt := storageClient.Bucket(bucketName)
obj := bkt.Object(objectName)
rdr, err := obj.NewReader(ctx)
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}
_, err = io.Copy(os.Stdout, rdr)
if err = rdr.Close(); err != nil {
fmt.Println(err)
}
fmt.Println()
}
$ export SOFTHSM2_CONF=/tmp/softhsm.conf
$ modutil -dbdir sql:.pki/nssdb/ -list
$ modutil -dbdir sql:.pki/nssdb/ -add "SoftHSM PKCS#11" -libfile /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
$ modutil -dbdir sql:.pki/nssdb/ -list
Listing of PKCS #11 Modules
-----------------------------------------------------------
1. NSS Internal PKCS #11 Module
uri: pkcs11:library-manufacturer=Mozilla%20Foundation;library-description=NSS%20Internal%20Crypto%20Services;library-version=3.87
slots: 2 slots attached
status: loaded
slot: NSS Internal Cryptographic Services
token: NSS Generic Crypto Services
uri: pkcs11:token=NSS%20Generic%20Crypto%20Services;manufacturer=Mozilla%20Foundation;serial=0000000000000000;model=NSS%203
slot: NSS User Private Key and Certificate Services
token: NSS Certificate DB
uri: pkcs11:token=NSS%20Certificate%20DB;manufacturer=Mozilla%20Foundation;serial=0000000000000000;model=NSS%203
2. OpenSC smartcard framework (0.23)
library name: /usr/lib/x86_64-linux-gnu/onepin-opensc-pkcs11.so
uri: pkcs11:library-manufacturer=OpenSC%20Project;library-description=OpenSC%20smartcard%20framework;library-version=0.23
slots: There are no slots attached to this module
status: loaded
3. SoftHSM PKCS#11
library name: /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
uri: pkcs11:library-manufacturer=SoftHSM;library-description=Implementation%20of%20PKCS11;library-version=2.6
slots: 2 slots attached
status: loaded
slot: SoftHSM slot ID 0x51aac4a2
token: token1
uri: pkcs11:token=token1;manufacturer=SoftHSM%20project;serial=7b07faced1aac4a2;model=SoftHSM%20v2
slot: SoftHSM slot ID 0x1
token:
uri: pkcs11:manufacturer=SoftHSM%20project;model=SoftHSM%20v2
then set the SOFTHSM2_CONF
env var and launch chrome
export SOFTHSM2_CONF=/tmp/softhsm.conf
/opt/google/chrome/chrome
if you'd rather not use the enterprise proxy and instead do this all by hand,
package main
import (
"context"
"crypto/tls"
"fmt"
"io"
"log"
"net/http"
"os"
"cloud.google.com/go/storage"
"github.com/ThalesIgnite/crypto11"
salpkcs "github.com/salrashid123/mtls_pkcs11/signer/pkcs"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
)
const (
projectID = "core-eso"
bucketName = "core-eso-bucket"
objectName = "foo.txt"
)
func main() {
ctx := context.Background()
// export GOOGLE_API_USE_CLIENT_CERTIFICATE=true
// export SOFTHSM2_CONF=`pwd`/softhsm.conf
// softhsm
// export SOFTHSM2_CONF=/home/srashid/Desktop/misc/soft_hsm/softhsm.conf
// var slotNum *int
// slotNum = new(int)
// *slotNum = 1370145954 // 0x51aac4a2
config := &crypto11.Config{
Path: "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so",
TokenLabel: "token1",
Pin: "mynewpin",
//SlotNumber: slotNum,
}
pctx, err := crypto11.Configure(config)
if err != nil {
log.Fatal(err)
}
defer pctx.Close()
r, err := salpkcs.NewPKCSCrypto(&salpkcs.PKCS{
Context: pctx,
PkcsId: nil, //softhsm
PkcsLabel: []byte("keylabel3"), //softhsm
PublicCertFile: "user10.crt", //softhsm
})
if err != nil {
log.Fatal(err)
}
//cert, err := tls.LoadX509KeyPair("user10.crt", "user10.key")
if err != nil {
log.Fatalf("failed to load root CA certificates error=%v", err)
}
tt := &tls.Config{
//Certificates: []tls.Certificate{cert},
Certificates: []tls.Certificate{r.TLSCertificate()},
}
sslKeyLogfile := os.Getenv("SSLKEYLOGFILE")
if sslKeyLogfile != "" {
var w *os.File
w, err := os.OpenFile(sslKeyLogfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
fmt.Printf("Could not create keylogger: ", err)
return
}
tt.KeyLogWriter = w
}
tr := &http.Transport{
TLSClientConfig: tt,
}
ts, err := google.DefaultTokenSource(ctx)
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}
client := &http.Client{
Transport: &oauth2.Transport{
Base: tr,
Source: ts,
},
}
storageClient, err := storage.NewClient(ctx, option.WithHTTPClient(client))
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}
defer storageClient.Close()
bkt := storageClient.Bucket(bucketName)
obj := bkt.Object(objectName)
rdr, err := obj.NewReader(ctx)
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}
_, err = io.Copy(os.Stdout, rdr)
if err != nil {
fmt.Printf("%v", err)
os.Exit(1)
}
if err = rdr.Close(); err != nil {
fmt.Println(err)
}
fmt.Println()
}