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

Code Signing Using Google Cloud KMS

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

To set up and sign 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 private key
  • Create a CSR and obtain a certificate
  • Sign your artifact

Download CNG Provider and Necessary Packages

Google has published its CNG provider in its repository on GitHub. These files can be installed on your Windows system using the included .msi installer 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). Creating a private key and CSR is recommended to be done on Linux; although you can install OpenSSL in PowerShell on Windows, there is less likelihood of encountering complications in Linux. The official Google guide also follows this approach.

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 within it, create a private key that is hardware protected using Cloud HSM. Select an asymmetric RSA algorithm for signing and a key length of 3072b, as SignTool cannot use EC keys in combination with Google Cloud KMS CNG.

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

This is how you create a Key Ring for placing the private key: gcloud kms keyrings create KEYRING-NAME \
--location=europe-west3 \
--project=PROJECT-NAME

We will place the private key in this Key Ring. It will be generated using the gcloud utility; complete the names (in capital letters) according to your 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 KEY_ID. It looks like this: projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/1

Now back to Windows, where it will be necessary to create another file, config.yaml, with the configuration for 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

Importing a certificate with a private key into an HSM is naturally not possible, nor can this pair be exported. The private key and CSR must therefore be generated in the HSM, and then the CSR is submitted to SSLmarket for signing by DigiCert. The import of the issued certificate is in the next step.

Creating a CSR is best done on Linux, using the libengine-pkcs11-openssl, libkmsp11, and API libraries. Of course, you will also need the google-cloud-cli package.

The creation of the CSR looks like this (after pkcs11:object=, add the key name (CryptoKey) according to GCP): 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

The request for DigiCert will then be in the cs-request.csr file in the folder where you are currently working. Send us this CSR.

Obtaining a Certificate

Have the created CSR signed by DigiCert, and you will receive back a new Code Signing certificate. You no longer need to insert it into the Google Cloud Console; it's only necessary to have the private key there. We will work with the certificate locally on Windows, so save it somewhere on the disk as a file.

Sign Your Artifacts

Continue on Windows. From 2024, Google has released an official Cloud KMS CNG Provider, which is registered as a Crypto Service Provider (CSP) / Key Storage Provider (KSP) in Windows under the name Google Cloud KMS Provider. This allows SignTool to use the key(s) in the cloud and not just locally on the token.

We will use SignTool from the Windows SDK and the x64 version of the tool for signing; we recommend the latest version. Make sure to 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 in this merged manner if you have it issued for HSM.
  • /csp specifies the particular CNG provider, as there can be more in Windows. The Google Cloud KMS Provider is registered just like, for example, the "Microsoft Software Key Storage Provider".
  • /kc (Key Container) is the path to the particular key and its version (KMS CryptoKeyVersion), corresponding to the already mentioned KEY_ID.

While signing, you will see which certificate was selected for signing and the result. Since SignTool has a problem binding the certificate to the private key in the cloud (via CSP/KSP), I recommend having the certificate stored locally in a file. The choice by the organization name or 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

Newly created digital signature can also be verified 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?