This is the third in a series exploring how to encrypt the message data payload within a Google Pub/Sub message. This will not go into any real detail on the situations why you want to ensure confidentiality or integrity of a given message but many of them were discussed in the first part of the series. If you haven’t already, please do read atleat the introduction section in the first part.
Anyway, what we’ve covered so far is a form of simple symmetric encryption where the sender and receiver are both using a static key. We’ve also explored one way to use Google Cloud’s service account public/private keys to sign and encrypt a message. There are some advantages and disadvantages to these approaches as was outlined in those article. We’re here to see if we can improve on it or develop a different technique using Google’s managed Key Management System.
So, instead of sharing a symmetric key or overriding a PKI system, can we use the KMS system to encrypt the data itself?
This technique relies on the publisher having access to a KMS key to encrypt and the subscriber access to the same key to decrypt a message. We will be encrypting the entire message with KMS so there are not encryption keys in transit.
We will be using this to encrypt and HMAC sign for confidentiality and integrity/authentication (respectively).
As mentioned, in-terms of what we’re encrypting and signing, its the whole message done entirely on KMS (no local key generation or using a service accounts certificate/keys). There several disadvantages to this as outlined at the end but lets go through this anyway.
For encrypted message, we need to transmit the message itself with the signature. For verification, we need will extract the signature in the header and compare. As was the case with encryption, we are verifying the payload within the data
field. As above, there maybe a variation between the Pub/SubMessage attributes and the attributes embedded in the data payload:
_json_message = {
"data": "foo",
"attributes": {
"attribute1": "value1",
"attribute2": "value2"
}
}
So as transmitted Pub/SubMessage would be:
data: encrypted(json_message)
attributes:
kms_key: (kms_keyid)
Ok, so how does this work? Its pretty straight forward:
data:
field and optionally specify a reference to the KMS key_id to use as a hint to the subscriber.Easy enough, right? Yes its too easy but has some severe practical limitations for high-volume, high-frequency PubSub messages
We use KMS MAC keys to sign and verify,.
The signed message looks like this
data: _json_message
attributes:
kms_key: (kms_keyid)
signature: hmac(sha256(json_message))
export PROJECT_ID=`gcloud config get-value core/project`
export PROJECT_NUMBER=`gcloud projects describe $PROJECT_ID --format='value(projectNumber)'`
gcloud pubsub topics create my-new-topic
gcloud pubsub subscriptions create my-new-subscriber --topic=my-new-topic
gcloud iam service-accounts create publisher
gcloud iam service-accounts create subscriber
gcloud kms keyrings create mykeyring --location=us-central1
gcloud kms keys create key1 --keyring=mykeyring --purpose=encryption --location=us-central1
gcloud kms keys create key2 --keyring=mykeyring --purpose=mac --default-algorithm=hmac-sha256 --location=us-central1
The following sample assumes you have IAM permissions to
my-new-topic
my-new-subscriber
key1
key2
If you want to run using the two service accounts created above, you should grant the publisher
permissions a
and c
while the subscriber
should have permissions b
, d
.
$ python publisher.py --mode=encrypt --project_id $PROJECT_ID --pubsub_topic my-new-topic --kms_location_id us-central1 --kms_key_ring_id mykeyring --kms_crypto_key_id key1
2021-11-14 09:17:42,482 INFO >>>>>>>>>>> Start <<<<<<<<<<<
2021-11-14 09:17:42,965 INFO Start KMS encryption API call
2021-11-14 09:17:43,412 INFO End KMS encryption API call
2021-11-14 09:17:43,413 INFO Start PubSub Publish
2021-11-14 09:17:43,414 INFO Published Message: CiUAmT+VVQyCYfCbVH0OWPeWSvBcYEXPFbEH30SG6AI/nq7mxWMPEoYBABBpW9AoUe2HrcpurOdup0CoZF8QlNocdgK+NgLAB2VOu3cpAoWLmGSxcljmGRI9NzJGtu2Tt/GUWxPoT6NLRBpky8umMGPv20gpgtKyEPbhorjNZvD4WGrNRhwrifl25U1UWgRKUEfTqUJkFPaGFdszbP0Krt6rg0+dO6GGc30XohhDe0o=
2021-11-14 09:17:43,801 INFO Published MessageID: 3343866587961712
2021-11-14 09:17:43,801 INFO End PubSub Publish
2021-11-14 09:17:43,801 INFO >>>>>>>>>>> END <<<<<<<<<<<
$ python subscriber.py --mode decrypt --project_id $PROJECT_ID --pubsub_topic my-new-topic --pubsub_subscription my-new-subscriber
2021-11-14 09:17:35,643 INFO Listening for messages on projects/mineral-minutia-820/subscriptions/my-new-subscriber
2021-11-14 09:17:43,876 INFO ********** Start PubsubMessage
2021-11-14 09:17:43,877 INFO Received message ID: 3343866587961712
2021-11-14 09:17:43,877 INFO Received message publish_time: 2021-11-14 14:17:43.653000+00:00
2021-11-14 09:17:43,877 INFO Received message attributes["kms_key"]: projects/mineral-minutia-820/locations/us-central1/keyRings/mykeyring/cryptoKeys/key1
2021-11-14 09:17:43,877 INFO Starting KMS decryption API call
2021-11-14 09:17:44,276 INFO End KMS decryption API call
2021-11-14 09:17:44,276 INFO Decrypted data {"data": "foo", "attributes": {"epoch_time": 1636899462, "a": "aaa", "c": "ccc", "b": "bbb"}}
2021-11-14 09:17:44,277 INFO ACK message
2021-11-14 09:17:44,277 INFO End AES decryption
2021-11-14 09:17:44,277 INFO ********** End PubsubMessage
$ python publisher.py --mode=sign --project_id $PROJECT_ID --pubsub_topic my-new-topic \
--kms_location_id us-central1 --kms_key_ring_id mykeyring --kms_crypto_key_id key2 --kms_crypto_key_version=1
2021-11-14 09:18:16,702 INFO >>>>>>>>>>> Start <<<<<<<<<<<
2021-11-14 09:18:17,187 INFO Start KMS mac API call
2021-11-14 09:18:17,188 INFO data_to_sign e3Tz5WJQcnnZJjiI+WHWPuqikqrY51TAzaZQPxXk5Ig=
2021-11-14 09:18:17,591 INFO End KMS mac API call
2021-11-14 09:18:17,591 INFO MAC: FMNvird1CWtHikzwLyr3hOE91RgG08+S9wTDhRCkD0E=
2021-11-14 09:18:17,591 INFO Start PubSub Publish
2021-11-14 09:18:18,006 INFO Published MessageID: 3343867410411934
2021-11-14 09:18:18,006 INFO End PubSub Publish
2021-11-14 09:18:18,006 INFO >>>>>>>>>>> END <<<<<<<<<<<
$ python subscriber.py --mode verify --project_id $PROJECT_ID --pubsub_topic my-new-topic --pubsub_subscription my-new-subscriber
2021-11-14 09:18:19,000 INFO ********** Start PubsubMessage
2021-11-14 09:18:19,001 INFO Received message ID: 3343867410411934
2021-11-14 09:18:19,001 INFO Received message publish_time: 2021-11-14 14:18:17.908000+00:00
2021-11-14 09:18:19,001 INFO Received message attributes["kms_key"]: projects/mineral-minutia-820/locations/us-central1/keyRings/mykeyring/cryptoKeys/key2/cryptoKeyVersions/1
2021-11-14 09:18:19,001 INFO Starting HMAC
2021-11-14 09:18:19,002 INFO Verify message: b'{"data": "foo", "attributes": {"epoch_time": 1636899496, "a": "aaa", "c": "ccc", "b": "bbb"}}'
2021-11-14 09:18:19,002 INFO data_to_verify e3Tz5WJQcnnZJjiI+WHWPuqikqrY51TAzaZQPxXk5Ig=
2021-11-14 09:18:19,002 INFO With HMAC: FMNvird1CWtHikzwLyr3hOE91RgG08+S9wTDhRCkD0E=
2021-11-14 09:18:19,434 INFO MAC verified
2021-11-14 09:18:19,435 INFO ********** End PubsubMessage
Ok, now that we went through all this…what are the issues with this approach:
Plus:
Minus
This is a simple way to encrypt or sign your messages with GCP’s Key Management System (KMS). Its not doing anything particularly novel but shows you how to use KMS alone to encrypt/decrypt.
Ultimately, this isn’t feasible or practical: for a service like pub/sub where latency is important, why add on this extra network dependency?
Right…so is there anyway we can iterate on this idea? Yep, we can mitigate some of the network issues by using another technique that is a hybrid of the ones cited in the previous thread. We’ll cover that in the next and final article
Here is the setup i used in the screenshots below and testing
esp-demo-197318
publisher@esp-demo-197318.iam.gserviceaccount.com
subscriber@esp-demo-197318.iam.gserviceaccount.com
projects/esp-demo-197318/locations/us-central1/keyRings/mykeyring/cryptoKeys/key1
projects/esp-demo-197318/topics/my-new-topic
projects/esp-demo-197318/subscriptions/my-new-subscriber
The following describes the KMS setup in this article. The steps outline how to create a KMS keyring, key and then set IAM permissions on that key so that the publisher can encrypt and the subscriber can decrypt. For this test, we enable IAM permission such that
publisher@esp-demo-197318.iam.gserviceaccount.com
can Encryptsubscriber@esp-demo-197318.iam.gserviceaccount.com
can DecryptWe do a similar configuration on the Pubsub side
setup a topic which to which you grant publisher@esp-demo-197318.iam.gserviceaccount.com
the ability to post messages
and a subscription against that topic where projects/esp-demo-197318/subscriptions/my-new-subscriber
can pull messages.
This site supports webmentions. Send me a mention via this form.