version: 2022-02
This software allows you to protect your organisation's Google Workspace data with strong Client Side Encryption (CSE).
Google Workspace Key Manager (WKM) is on-prem software service which wraps and unwraps (encrypts/decrypts) Data Encryption Keys (DEKs) for use in Google Workspace web and native applications such as Google Drive and Google Docs.
Key Encryption Keys (KEKs) are derived on the fly using a set of Master Secrets, which in turn may be stored using your existing infrastructure, on any PKCS#11 or KMIP-compatible Hardware Security Module (HSM) or software key management system (KMS). KEK-encrypted Data Encryption Keys (DEKs) are then uploaded to GSuite along with encrypted data.
In Google terminology, our WKM is referred to as "KACLS - Key ACL Service".
The API is a REST API with JSON body requests and responses, accessed by Google Workspace web app and Google Workspace client software. SSL connections can be either terminated in the WKM, or you can use SSL terminating reverse proxy (in which case you can set api.https.enabled=false
in the config file).
All requirements described in On-prem deployment requirements plus the following:
Type | Requirement |
---|---|
Key Storage | The WKM derives keys on the fly from Master Secrets. You have a choice on how to load Master Secrets during startup, from properties file, stdin, a PKCS#11 or a KMIP-1.0 compatible Key Store (such as Gemalto Safenet KeySecure over KMIP, Fortanix SDKMS and Equinix SmartKey over PKCS#11) |
Networking | WKM must be accessible on an internal URL from client devices, with TLS cert trusted by client. The instance should not be accessible from public internet, but it needs limited outward access to the internet to fetch IdP JWK for user authentication and Google JWK for request authorization. |
FlowCrypt WKM is distributed as a zip file containing:
Filename | Description |
---|---|
flowcrypt-workspace-key-manager.jar |
Runnable Java JAR |
flowcrypt-workspace-key-manager.properties |
Default config |
flowcrypt-workspace-key-manager-docs.md |
This documentation |
LICENSE.txt |
License file (proprietary) |
For deployment, install OpenJDK JRE, copy the .jar
and the .properties
file, and edit the properties file before running.
Windows only If your OS does not use UTF-8 file encoding by default, then you should ensure the app uses UTF-8 encoding.
UTF-8 support is required by the Google Workspace CSE KACLS.
The easiest way to do this is to set an environment variable JAVA_TOOL_OPTIONS
to -Dfile.encoding=UTF8
.
Alternatively when starting the application you can add the encoding system property to the startup command: java -Dfile.encoding=UTF-8 -jar flowcrypt-workspace-key-manager.jar
Before you run the application, you have to set up your Master Secrets. See generating master secrets.
Once your master secrets and other properties are configured, start WKM by running: java -jar flowcrypt-workspace-key-manager.jar
Default command is to start the server at localhost:32567
. Other commands:
Command | Description |
---|---|
--version |
Print application version |
--help |
Print application help |
--create-master-key |
Generates Master Key and print it out if the store.master.key.source is either stdin or properties . If store.master.key.source is set to either kmip or pkcs11 , the newly generated key will also be automatically stored at the configured KMIP or PKCS#11 store. This should be done one time during initial configuration. |
--test-store-connection |
This will test that you have Master Key retrieval properly configured. It may ask for Master Key in case you set store.master.key.source to stdin . |
Command line options:
Argument | Default | Description |
---|---|---|
--config=<cfg.properties> |
flowcrypt-workspace-key-manager.properties |
config file path |
The sample configuration file appropriate for your software version will come distributed along with the jar file. It consists of several sections: Common, Store, Logger, Authentication, Google Workspace, ACL. Each is described in detail below.
You will also need to set up a CSE configuration file containing IdP information that is publicly accessible on your domain.
- Logger section: see On-prem logging
- On-prem High Availability and Scaling.
- Deployment checklist.
See On-prem general configuration and HTTPS
The WKM needs to be set up with Master Key and a Test Vector. All individual object (cloud file) keys are then derived from these master secrets on the fly. To generate your master secrets, run:
$ java -jar flowcrypt-workspace-key-manager.jar --create-master-key
This command will generate master key and print out the asociated test vector for you:
Output | Description |
---|---|
Master Key |
Base64 encoded 32 bytes (256 bits) random value used for key derivation. Keep this strictly secret. It is only required if store.master.key.source is either stdin or properties |
Test Vector |
Base64 encoded 32 byte (256 bit) test value encoded as Base64 that is used to verify correctness of supplied Master Key. This is especially useful when sysadmin chooses to enter secrets from stdin manually. |
Secret retrieval option | Description |
---|---|
properties |
Store the master key in the properties file |
stdin |
Sysadmin needs to supply the master key manually during app startup, through a command line prompt |
pkcs11 |
Master key retrieved automatically from a pkcs11 compatible secret storage |
kmip |
Master key retrieved automatically from a KMIP compatible secret storage |
Retrieval options can and should be combined. For example, for high security, you could have one secret stored over KMIP and another one supplied manually during WKM startup. On the other end of the spectrum, for ease of setup and convenience, you could supply both values in the properties file.
Property | Example | Description |
---|---|---|
store.type |
MasterKeyStore |
This is the default and recommended option. |
store.master.derivation.scheme |
sha256-aes-ecb |
Derivation scheme. Current only option is sha256-aes-ecb which uses AES256-ECB-ENCRYPT(key=MasterKey, data=SHA256(entry_id)) to derive individual keys from Master Key. |
store.master.key.source |
stdin |
How to retrieve Master Key. Options are properties , stdin , kmip and pkcs11 |
store.master.key.value |
(from --create-master-key ) |
(for store.master.key.source=properties ) set if you wish to set Master Key in properties file |
store.test.vector.source |
properties |
The only option - set Test Vector directly in properties file. |
store.test.vector.value |
(from --create-master-key ) |
This is used to cross-check entered Master Key |
If you have chosen kmip
or pkcs11
for any of the source
properties, you also need to tell WKM how to connect to such store to retrieve master key.
Our implementation is tested against the PyKMIP server. While KMIP is a vendor-agnostic protocol, implementations do vary from vendor to vendor. When planning a PoC, please allow some time for us to test against and integrate with your particular KMIP vendor.
Property | Example | Description |
---|---|---|
store.kmip.hostname |
kmipserver |
(for store.master.key.source=kmip ) The hostname of the KMIP server. |
store.kmip.port |
5696 |
(for store.master.key.source=kmip ) The listening port of the KMIP server. |
store.kmip.master.key.identifier |
2085 |
(for store.master.key.source=kmip ) The unique identifier of the master key stored at KMIP server. |
store.kmip.master.key.name |
flowcrypt-workspace-km-master-key |
(for store.master.key.source=kmip ) The name of the master key stored at KMIP server. |
store.kmip.key.file |
keystore.p12 |
(for store.master.key.source=kmip ) The keystore file containing the certificate to present to the KMIP server on TLS handshake. |
store.kmip.key.password |
changeit |
(for store.master.key.source=kmip ) The password to access the KMIP keystore file. |
KMIP authentication works in the following way: the server (your key store/HSM) as well as the client (in this case this WKM) each have their own TLS certificates. Each certificate must be mutually trusted to establish a connection. When configuring WKM, you can use the truststore.file
to point to a file containing trusted certificates to validate KMIP server's credentials, preventing MITM. Likewise, your KMIP server should be configured to strictly verify the client's cert.
We develop and test our implementation against Equinix SmartKey (Fortanix SDKMS). For this we used their PKCS#11 shared library module (.so), accessed through iaik.pkcs.pkcs11.wrapper
Java library. To connect our software to your PKCS#11 HSM, update store.pkcs11.module
to point to module provided by your HSM vendor.
To test your pkcs module, you can use pkcs11-tool on Ubuntu 18.04:
$ sudo apt-get -y install opensc
$ pkcs11-tool --module vendor-pkcs11.so --show-info
Then set the following in the properties file:
Property | Example | Description |
---|---|---|
store.pkcs11.module |
./vendor-pkcs11.so |
(for store.master.key.source=pkcs11 ) Path to a pkcs#11 shared library. The library can be obtained from your HSM vendor |
store.pkcs11.pin |
file://vendor-pkcs11.cfg |
(for store.master.key.source=pkcs11 ) PKCS#11 token pin. In the case of Fortanix and possibly other vendors, this is actually a path to a pkcs#11 config file |
To test that the WKM can retrieve Master Secrets and that they are valid, run:
java -jar flowcrypt-workspace-key-manager.jar --test-store-connection
This will validate a Test Vector to make sure the Store is properly set up. The command returns status 0 on unix systems when successful. Successful output:
Registering StdoutLogger as Logger implementation
INFO Reflection - Registering MasterKeyStore as Store implementation
INFO MasterKeyStore - Successfully validated MasterKeyStore Test Vector
INFO c.f.keymanager.TestStoreConnection - initiating test
INFO c.f.keymanager.TestStoreConnection - success
The authenticator component configures an OpenId Connect service which verifies non-Google issued JWTs (JSON web tokens), asserting identity of user who accesses an object.
First, you need to decide if you want to use Google as your IdP or if you want to use your own. Then you set up an OpenID Connect (OIDC) authentication app on your IdP as follows:
IdP App property | Value |
---|---|
App Type | Open ID Connect , OIDC , Web Application , oAuth2 (exact choice depends on your IdP) |
Name | FlowCrypt WKM (customizable) |
Rectangular icon | Download from: https://flowcrypt.com/assets/imgs/svgs/flowcrypt-logo.svg |
Square icon | Download from: https://flowcrypt.com/img/favicons/mstile-150x150.png |
authorized JavaScript origins | drive.google.com , docs.google.com (more origins will be added over time as Google implements them) |
Redirect/callback URL | https://krahsc.google.com/callback |
Login URL | (if using OneLogin) leave blank |
IdP specific instructions:
- OneLogin:
Applications
->Custom Connectors
->New Connector
-> (fill) ->Save
. When done, followApplications
->Custom Connectors
-> (FlowCrypt WKM line)Add App to Connector
->Save
. You will be redirected toApplications
->FlowCrypt WKM
, where you clickSSO
in the left menu - there are the fields to put into WKM properties file:Client ID
(put intoauth.openid.audience
and set in hostedcse-configuration
file as bothclient_id
andaudience
fields - see below),Issuer URL V2
(put intoauth.openid.issuer
) andV2 .well-known URL
(set in hostedcse-configuration
file asdiscovery_uri
field - see below). Make sure all of your users can access this newly created Application, using a rule/policy or other such mechanism on OneLogin. - Google Developer Console:
console.developers.google.com
-> (top left project selector) ->NEW PROJECT
->flowcrypt-WKM
. Next, newOAuth Consent Screen
-> internal, add a name and logo as above, authorized domains:google.com
. Finally, in left menuCredentials
->+ CREATE CREDENTIALS
->OAuth Client ID
->Web Application
-> fill origins and redirect URI as above ->CREATE
. You will get yourClient ID
on the next screen (client secret is not needed).
Next, host a file at the (sub)domain associated with your Google account. If your account domain was subdomain.domain.tld
, then host this file at: https://cse.subdomain.domain.tld/.well-known/cse-configuration with the following format:
{
"name" : "[Name of your IdP]",
"client_id" : "[oAuth Client ID of the app requesting authentication, get this from your IdP]",
"discovery_uri" : "[your IdP issuer path]/.well-known/openid-configuration",
"audience" : "[the expected JWT aud claim, get this from your IdP - often same as client_id above]"
}
As an example, if you were to use Google as your IdP, you would set up an internal oAuth Screen web app on your Google Developer Console. Then host the following file:
{
"name" : "Google IdP",
"client_id" : "[oAuth Client ID from Google Developer Console]",
"discovery_uri" : "https://accounts.google.com/.well-known/openid-configuration",
"audience" : "[same as the client_id above]"
}
Property | Example | Description |
---|---|---|
auth.enduser.idps |
default |
Comma separated list of Identity Providers names for standard users. |
auth.enduser.<idp>.issuer |
https://accounts.google.com/ |
Substitute for the IdP name above. The expected issuer of the authentication JWT token. |
auth.enduser.<idp>.audience |
audience |
Substitute for the IdP name. The expected JWT token "aud" claim. If you use Google as your IdP, then this should be your oAuth Client ID. |
auth.enduser.<idp>.jwks |
https://www.googleapis.com/oauth2/v3/certs |
(Optional) Substitute for the IdP name. The JWKS (JSON Web Key Set) containing the public keys to validate the authentication JWTs against. When not provided, WKM will try to poll [issuer]/.well-known/openid-configuration to retrieve the jwks url during startup. |
auth.admin.idps |
default |
Comma separated list of Identity Providers names for admins. |
auth.admin.<idp>.issuer |
https://accounts.google.com/ |
Substitute for the IdP name. The expected issuer of the authentication JWT token. |
auth.admin.<idp>.audience |
audience |
Substitute for the IdP name. The expected JWT token "aud" claim. If you use Google as your IdP, then this should be your oAuth Client ID. |
auth.admin.<idp>.jwks |
https://www.googleapis.com/oauth2/v3/certs |
(Optional) Substitute for the IdP name. The JWKS (JSON Web Key Set) containing the public keys to validate the authentication JWTs against. When not provided, WKM will try to poll [issuer]/.well-known/openid-configuration to retrieve the jwks url during startup. |
Make sure this cse-configuration file can be accessed over CORS by browsers. For this you need to add the following CORS headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
To verify that you have added CORS headers properly, you can use curl as follows (edit to match your path):
curl -Is https://cse.subdomain.domain.tld/.well-known/cse-configuration
The GSuite authorization component configures the Google OpenId Connect service which verifies Google issued JWTs, making sure the user requesting access to a particular object is allowed to access it.
Property | Example | Description |
---|---|---|
google.workspace.products |
drive,meet,calendar,gmail |
Comma separated list of Google Workspace products to integrate with WKM. |
google.workspace.privileged.users |
[email protected],[email protected] |
List of legal admins (different from technical admins) who may perform privileged operations on data that belongs to other users. |
google.workspace.kacl.account.email |
[email protected] |
Google account email associated with this KACL. |
google.workspace.<product>.product.family |
default |
(Optional) Google CSE product family. Options: default , gmail . Default value is default |
google.workspace.<product>.issuer |
see default properties file | Substitute for the Workspace product above. The expected issuer of the authorization JWT token, this is the only accepted value. |
google.workspace.<product>.audience |
cse-authorization |
Substitute for the Workspace product above. This is the only accepted value. |
google.workspace.<product>.jwks |
see default properties file | Substitute for the Workspace product above. The JWKS (JSON Web Key Set) containing the public keys to validate the authorization JWTs against. This is the only accepted value. |
The ACL component allows third party implementations further protect resources. Please email [email protected]
to use a 3rd party ACL.
Property | Example | Description |
---|---|---|
acl.type |
NoThirdPartyAcl |
Currently NoThirdPartyAcl is the only ACL option. |
Because the Google CSE combines their own systems with your existing IdP and our WKM, which in turn may rely on your KMIP or PKCS11 store, each with various configuration options, you may run into issues the first time you attempt to configure all these together.
To troubleshoot WKM startup, first see Troubleshooting.
In this early stage of Google CSE, you may encounter unintuitive errors with no clear resulution guidance, such as the ones described below.
If during testing you are getting a 404 when your IdP redirects to this URL after login (for example when you're uploading a new file), this can have one of the following causes:
- (during Google Alpha) Google needs to whitelist your user or issuer
- (during Google Alpha) You signed into several Google accounts, and the test user is not the default user on your browser. Try to log out of all accounts and only sign into the target test account. Alternatively, use Incognito mode in Chrome with only the target test account.
This can manifest as an error saying "An error occurred with the identity provider service", or "Can't decrypt file (Something went wrong and your file wasn't downloaded)" or "An error occurred with identity provider service". There are two possible causes:
- (during Google Alpha) your browser did not yet authenticate with your IdP within drive.google.com. To authenticate during Alpha, upload a drive file first instead, go through an "Upload failure" and force re-authentication as described below. Then you can go back to your original task (opening file, updating doc, etc).
- your IdP is misconfigured, such as the user you are logged in with was not assigned to the IdP app, or wrong Client ID in cse-configuration, etc. To debug, you can observe the browser network tab, or ask Google.
You can see an "Upload failure" on drive.google.com when you are uploading an encrypted file and have not yet been authenticated on this browser. To resolve, click the exclamation mark in a red circle (!) shown with this error. This will force re-authentication.
Re-authenticating through the encrypted file upload flow will fix other authentication issues around the Drive/Docs apps that don't yet have their own robust auth error handling mechanism.
For help or to report issues, please email [email protected]
.
# docs:
# https://flowcrypt.com/docs/technical/workspace-key-manager/latest/workspace-key-manager.html
### General ###
org.id=evaluation.org
api.hostname=0.0.0.0
api.port=32567
api.accept.hosts=wkm.evaluation.org,localhost:32567
api.https.enabled=true
api.https.key.file=wkm-https-cert.p12
api.https.key.password=password
api.cors.origins=https://*.google.com
api.url=https://wkm.evaluation.org
#api.error.format=detailed|short|id_only
api.error.format=id_only
api.openapi.enabled=false
# Truststore is optional - if you want to override default JRE truststore
# use this to verify KMIP server with custom cert
#truststore.file=wkm-dev-rootca.p12
#truststore.password=password
### Store ###
# the options for "key.source" field is: properties | stdin | kmip | pkcs11
# the only option for "test.vector.source" is: properties
# Required key format is 32 bytes (256 bits) of random data encoded as base64
# Generate master secrets, and test vector by running: --create-master-key (uses Java SecureRandom, or KMS over KMIP)
store.type=MasterKeyStore
store.master.derivation.scheme=sha256-aes-ecb
store.master.key.source=properties
# enter "--create-master-key" output (Master Key) here to set Master Key in properties file
store.master.key.value=
store.test.vector.source=properties
# enter "--create-master-key" output (Test Vector) here either way. This is used to cross-check entered Key
store.test.vector.value=
# master key stored in secure key storage over KMIP protocol (eg Gemalto Safenet KeySecure)
# remember to also set `truststore.file` above if KMIP server uses certs with custom CA
#store.kmip.hostname=localhost
#store.kmip.port=5696
#store.kmip.key.file=localhost.p12
#store.kmip.key.password=password
#store.kmip.master.key.name=flowcrypt-workspace-km-master-key
#store.kmip.master.key.identifier=
# master key stored over PKCS#11 protocol (Fortanix SDKMS, Equinix SmartKey or any compatible HSM)
#store.pkcs11.module=./vendor-pkcs11.so
#store.pkcs11.pin=file://vendor-pkcs11.cfg
### Logger ###
# comma separated list - can have any combination of StdoutLogger, FileLogger, StackdriverLogger, SplunkHttpLogger
logger.types=StdoutLogger
# trace, debug, info, warn, error
logger.default.level=info
#logger.stdout.include.datetime=true
#logger.file.folder=/var/logs
#logger.file.history.size=14
#logger.file.history.compression=false
#logger.file.include.datetime=true
#logger.stackdriver.credentials.file=/etc/google/auth/application_default_credentials.json
#logger.splunk.url=https://splunk-instance:8088
#logger.splunk.token=327bfa46-...
#logger.splunk.disable.certificate.validation=true
### Authentication ###
# Use defaults provided below if you intend to use Google as your IdP. In that case, set up an oAuth Screen
# on Google Developer Console, and provide oAuth Client ID in audience field.
# Else change issuer and jwks to your custom company IdP.
auth.enduser.type=OidcAuthenticator
auth.enduser.idps=default
auth.enduser.default.issuer=https://accounts.google.com
auth.enduser.default.audience=SET_YOUR_GOOGLE_OAUTH_CLIENT_ID_HERE
auth.enduser.default.jwks=https://www.googleapis.com/oauth2/v3/certs
auth.admin.type=OidcAuthenticator
auth.admin.idps=default
auth.admin.default.issuer=https://accounts.google.com
auth.admin.default.audience=SET_YOUR_GOOGLE_OAUTH_CLIENT_ID_HERE
auth.admin.default.jwks=https://www.googleapis.com/oauth2/v3/certs
### Google Workspace Products ###
google.workspace.products=drive,meet,calendar,gmail
google.workspace.privileged.users=
google.workspace.kacl.account.email=
### Google Workspace Authorization (Docs & Drive) ###
google.workspace.drive.product.family=default
google.workspace.drive.issuer[email protected]
google.workspace.drive.audience=cse-authorization
google.workspace.drive.jwks=https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
### Google Workspace Authorization (Meet) ###
google.workspace.meet.product.family=default
google.workspace.meet.issuer[email protected]
google.workspace.meet.audience=cse-authorization
google.workspace.meet.jwks=https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
### Google Workspace Authorization (Calendar) ###
google.workspace.calendar.product.family=default
google.workspace.calendar.issuer[email protected]
google.workspace.calendar.audience=cse-authorization
google.workspace.calendar.jwks=https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
### Google Workspace Authorization (Gmail) ###
google.workspace.gmail.product.family=gmail
google.workspace.gmail.issuer[email protected]
google.workspace.gmail.audience=cse-authorization
google.workspace.gmail.jwks=https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
### ACL ###
acl.type=NoThirdPartyAcl