{"copy":"Copy","expand":"Expand","collapse":"Collapse","copy_success":"Copied!","copy_error":"Copying failed!"}

Code Signing with Google Cloud KMS

With Google Cloud KMS, you get a cloud HSM certified at the FIPS 140-2 Level 3. You can sign code securely, quickly, and from anywhere. The cost per number of operations (signatures) is also very favorable. Cloud KMS supports signing using the Microsoft Cryptography API: Next Generation (CNG).

To configure and sign the code, you need to perform the following steps, which we will go through one by one:

  • Install the CNG provider
  • Create a Key Ring and a private key
  • Create a CSR and obtain a certificate
  • Sign your artifact

Download the CNG Provider and Required Packages

Google has published its CNG provider in its GitHub repository. These files can be installed on your Windows system using the attached .msi installation file. This program requires configuration in the form of a YAML file, which you will find in one of the steps of this guide.

Now move to Linux (you can use WSL in Windows). We recommend generating the private key and CSR on Linux; although you can install OpenSSL in PowerShell on Windows, there is less chance of encountering complications on Linux. This is also the procedure recommended by the official Google guide.

We will use the gcloud application from the google-cloud-cli package to communicate with the cloud.

Create a Key Ring and Private Key in Cloud HSM

Perform the following steps on Linux. Create a new Key Ring for the Cloud Key Management Service (KMS) API and create a private key in it, which is hardware-protected using Cloud HSM. Choose an asymmetric RSA algorithm for signing and a key length of 3072b, because SignTool cannot use EC keys in combination with Google Cloud KMS CNG.

First, log in to your Google account on Linux and authorize the session (applies also for Windows PowerShell): gcloud auth application-default login

Create a Key Ring to store the private key: gcloud kms keyrings create KEYRING-NAME \
--location=europe-west3 \
--project=PROJECT-NAME

We will place the private key into this Key Ring. We will also generate it using the gcloud utility; fill in the names (in capital letters) according to reality (project name in GCP) and your naming.

gcloud kms keys create KEY-NAME --keyring=KEYRING-NAME --location=europe-west3 --purpose=asymmetric-signing --protection-level=hsm --default-algorithm=rsa-sign-pkcs1-3072-sha256 --project=PROJECT-NAME

Once you create the first version of the key, we recommend copying the Copy resource name in its details in the Cloud Console, as you will need this information for the KEY_ID. It looks like this: projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/1

Now back to Windows, where you will need to create a file config.yaml with the configuration for the KMS integration. Enter the following in PowerShell: $yaml = @"
resources:
- crypto_key_version: "projects/PROJECT-NAME/locations/europe-west3/keyRings/KEYRING-NAME/cryptoKeys/KEY-NAME/cryptoKeyVersions/1" "@

$yaml = $yaml -replace "`t"," "

$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText('C:\Windows\KMSCNG\config.yaml', $yaml, $utf8NoBom)

Creating a CSR

It is naturally not possible to import a certificate with a private key into HSM, nor is it possible to export this pair. The private key and CSR must thus be generated in HSM, and then you'll provide the CSR to SSLmarket to have it signed by DigiCert. The import of the issued certificate is in the next step.

It's best to create the CSR on Linux using the libengine-pkcs11-openssl, libkmsp11, and API libraries. Of course, you'll also need the google-cloud-cli package.

Creating the CSR looks like this (fill in the key name (CryptoKey) according to GCP after pkcs11:object=): openssl req -new \
-subj "/C=CZ/O=ZONER /CN=ZONER" \
-sha256 -engine pkcs11 -keyform engine \
-key "pkcs11:object=KEY_NAME;type=private" \
-reqexts v3_req -config <(cat /etc/ssl/openssl.cnf; printf "\n[v3_req]\nextendedKeyUsage=codeSigning\n") \
-out cs-request.csr

You will then have the request for DigiCert in the cs-request.csr file in the folder where you are currently working. Please send us this CSR.

Obtaining the Certificate

Have the created CSR signed by DigiCert, and you'll get a new Code Signing certificate back. You don't need to upload it to the Google Cloud Console; it's enough to have the private key there. We'll work locally with the certificate on Windows, so save it somewhere on your disk as a file.

Sign Your Artifacts

Let's continue on Windows. As of 2024, Google has released an official Cloud KMS CNG Provider, which is registered in Windows as a Crypto Service Provider (CSP) / Key Storage Provider (KSP) with the name Google Cloud KMS Provider. This allows SignTool to use keys in the cloud and not just locally on a token.

We will sign using SignTool from the Windows SDK and the x64 version of the tool; we recommend the latest version. Make sure you enter the correct parameters (see explanation below the example).

Example of signing using SignTool and PowerShell: & $SignTool sign `
/v /debug `
/fd sha256 /td sha256 `
/tr http://timestamp.digicert.com `
/f "PATH_TO_CERT" `
/csp "Google Cloud KMS Provider" `
/kc "projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/1" `
"PATH_TO_FILE_TO_SIGN"

Explanation:

  • /f PATH_TO_CERT is the path to the Code Signing certificate file (public part). You will receive it from us when it's issued for HSM in this merged form.
  • /csp specifies the particular CNG provider, as there may be several in Windows. Google Cloud KMS Provider is registered just like "Microsoft Software Key Storage Provider", for example.
  • /kc (Key Container) is the path to the particular key and its version (KMS CryptoKeyVersion), matching the previously mentioned KEY_ID.

During signing, you will see which certificate was chosen for signing and the result. Since SignTool has trouble linking the certificate to the private key in the cloud (via CSP/KSP), we recommend having the certificate stored locally in a file. Choosing using the organization name or the SHA1 hash of the certificate is problematic and usually non-functional; only the certificate in a file worked.

The following certificate was selected:
Issued to: ZONER a.s.
Issued by: DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1
Expires: Wed Nov 12 01:59:59 2025
SHA1 hash: F9BC96AC1764AD9F2072780FFB64940538A3B292

The following additional certificates will be attached:
Issued to: DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1
Issued by: DigiCert Trusted Root G4
Expires: Tue Apr 29 01:59:59 2036
SHA1 hash: 7B0F360B775F76C94A12CA48445AA2D2A875701C

Done Adding Additional Store
Successfully signed: C:\Users\jindrich.zechmeister\HelloSign.exe

Number of files successfully Signed: 1
Number of warnings: 0
Number of errors: 0

Bonus - Verify the Signature

We can also verify the newly created digital signature using SignTool.

PS C:\Users\jindrich.zechmeister> signtool verify /pa c:\Users\jindrich.zechmeister\App.exe
File: c:\Users\jindrich.zechmeister\App.exe
Index Algorithm Timestamp
========================================
0 sha256 RFC3161
Successfully verified: c:\Users\jindrich.zechmeister\App.exe

The signed EXE file is successfully signed!

Sources:

Has this article been useful?