Google App Engine Security Module API and JWT support

Introduction

In late March 2011, Google rolled out a new Google App Engine API that can be used to support 'application identity'. Most of you are already familiar with user identity, authenticating a user usually involves a user name and password, or an oauth token bind to that particular user. We are going to apply the same authentication concept to applications, give applications a cloud identifier that could be used to interoperate with other cloud applications.

The new API is currently referred to as the Cloud Security Module API and at the most basic level it allows you to create a digital signature. The digital key used to create the signature is secured by Google App Engine, and thus the developer's code does not need to worry about protecting the key. This is functionally similar to a Hardware Security Module though the Cloud Security Module API does not implement the full PKCS#11 interface. The API includes two interfaces:

  • app_identity.getPublicCertificatesForApp(): This function will enable your application to get the public key in a self-signed X.509 PEM certificate format. Note that the Google App Engine automatically rotates the public key every few hours. We suggest having a well defined URL on your site that calls this function and returns the current certificate. An example of a returned x509 certificate can be found in the "Certificate Format" section below.

  • app_identity.signForApp(string_blob): The blob you pass to this function will be used to generate an RSA signature using the X.509 private key that Google App Engine manages for your application. The system that needs to verify this signature will need to lookup the current certificate that Google App Engine used to created the signature.

We are making the source code of these apps available to help with industry discussions about standards and interop. The API App is written in Java. One calling app is written in Python and the other in Java. You can checkout the source code at https://code.google.com/p/app-identity-samples/.

Live Demo

Here is one basic use case that leverages this API to do application authentication:

  • Client App generates a signed blob by calling app_identity.signForApp(string_blob)

  • Client App exposes its public certificates on a public endpoint, for example clientapp.com/certs. In the demos below we use a trivial Json format to expose certificates, something like: {"cert1":"x509 cert pem", "cert2":"x509 cert pem 2"...}.

  • Client App sends a request to API App along with the signed blob and the URL that contains Client App's public certificates.

  • API App fetches Client App's public certificates from that URL

  • API App verifies the signature of the signed blob. The API App might perform other business logic like checking if the Client App (as identified by the URL of its public certificates) is on an access control list.

  • Both apps should agree on the same 'signed blob' format. In our demo we use JWT as the signed token format. The detailed spec can be found at http://self-issued.info/

Google has built two sample Apps which demonstrate this use case and which both run on Google App Engine, one is Python, one is Java.

  • The Java app can act as both as a Client App and API App:

    • http://app-identity-java.appspot.com/resource: This is a REST API endpoint which acts in the API App role. It accepts two URL parameters, jwt and certurl. It verifies the JWT signature by fetching certificates from the public certificate provided by the certurl parameter.

      • http://app-identity-java.appspot.com/access: This is the user facing part of the app which acts in the Client App role. By default it makes a call the the API endpoint above running on the same application. However you can modify the URL of the endpoint it calls, for example by pointing it at your own verification endpoint which accepts the same parameters as the sample API endpoint.

      • http://app-identity-java.appspot.com/certs: This is the second part of the Client App functionality which exposes public certificates for this app. NOTICE: In real cases, public certificates URL must be a HTTPS URL to ensure transmit integrity.

  • The Python app (http://app-identity-python.appspot.com/) can only act as a Client App. It generates a JWT by calling the GAE app_identity.signForApp function, then calls the Java app's API endpoint, and shows the response it receives. This python app also exposes its certs at the URL

  • http://app-identity-python.appspot.com/certs.

Sample Code

The API App that verifies the signatures has very little code that is unique to Google App Engine. So if you want to perform some basic interop testing, you can run an instance of that code on your own server. If you visit the two calling apps, you can then override the URL of the API endpoint that they try to call and point them at your app. Alternatively, you can run your own instance of a calling app, including publishing its cert on an Internet accessible URL. If you access the UI of that app, it can make an API call to the API App and confirm that the signature was verified.

Google has also started to test the ability to use OAuth2 assertions when making calls to Google APIs.

Using the Google App Engine Identity API

If you want to write your own Google App Engine app that uses the new identity API feature, you might find it easiest to start with the sample code described above. However if you want to write your own AppEngine app from scratch, we have provided some instructions below on how to get started.

PYTHON:

You need to import two files into your projects:

https://code.google.com/p/app-identity-samples/source/browse/trunk/python/app_identity.py

https://code.google.com/p/app-identity-samples/source/browse/trunk/python/app_identity_service_pb.py

JAVA:

You need to import one additional Jar to your project:

https://code.google.com/p/app-identity-samples/source/browse/trunk/app_identity_java/war/WEB-INF/lib/appengine-api.jar

There also exists a Java version JWT library, which is compatible with JWT spec by Mike Jones, you can find the library at http://code.google.com/p/jsontoken/, a Jar version could be found at https://code.google.com/p/app-identity-samples/source/browse/trunk/app_identity_java/war/WEB-INF/lib/jsontoken.jar. The library is based on the Draft 1.0 JWT spec, but the later drafts did not make big changes to the functionality.

There're some packages which JWT library depends on, I also include those packages at :

https://code.google.com/p/app-identity-samples/source/browse/trunk/app_identity_java/war/WEB-INF/lib

Common for both JAVA and PYTHON:

  • Currently App needs to call getPublicCertificatesForApp, and write code to expose certs, in nearly future, we are planning to provide a standard Google API to expose those certificates in a standard format.

  • In order to construct a valid JWT, you need to provide an 'issuer' field, in the above example, it doesn't matter what 'issuer' be used, but in some more sophisticated use cases, 'issuer' can be important.

Certificate format in Google App Engine Identity API

The App Engine function app_identity.getPublicCertificatesForApp() is used to get the list of public certificates which should be used to verify the signature created by the app_identity.signForApp(string_blob) function. Because App Engine will rotate the signing key periodically, there could be multiple valid certificates at the same time. The certificate itself will be an self-signed X.509 formatted certificate with an embedded public key in PEM format. There's also an identifier string called 'keyName' associated with each x509 certificate. Below is an example of a returned x509 certificate:

"-----BEGIN CERTIFICATE-----"

0scHWVODFUrLTu7ds3zi/Jyp2G/5Ub6tyNe2ppEKIpkdgg==

AxMnNjQxMDgzNzM4MTMyMjU5MzM3My1yb2JvdC5hLmFwcHNwb3QuY29tMB4XDTEx

MDEyNzIzMDAyM1oXDTExMDEyOTAwMDAyM1owMjEwMC4GA1UEAxMnNjQxMDgzNzM4

MTMyMjU5MzM3My1yb2JvdC5hLmFwcHNwb3QuY29tMIIBIjANBgkqhkiG9w0BAQEF

AAOCAQ8AMIIBCgKCAQEAqG+bwIBwH9PKKJ4orHzK5jcJu+amJha5uH4vdI4HvUIV

LzT6by/6t9MiNi/uEmtDirl3Uo3Hr6i7QsMVaH142Lw+kkbhXqZ53bwwsfDcFE8J

Pyx+EtuQsm0faXF846Hv6Hx4Z4fo0wLTREX5cGLzet4EZpYAehM8w5b+UiO4PjUD

i1iBG/JdeFZD7op8p9S5Idb/y/V3ngEyX8l9nTb/kCwHNPfzXitv7NkIpp9jlrQU

bYDH3cj76PkuugaV1J9Z5iwQKRqWC/S5fH4HrMHm7Cul61cubNtcTXAGGtBmObE5

dNqgWvhwZlAs1nLJ1jxx0iKgZDGh4kVkGcPJ66rgDQIDAQABozgwNjAMBgNVHRMB

Af8EAjAAMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjAN

BgkqhkiG9w0BAQUFAAOCAQEAiwZMpdGhNTsyyMpiTu9liF1ZXus4PmJug0ynwpl5

qTfWnkw3lUGGGYBLkG6R4QitjEn0zl2QEiX9zpZB1eTJDPcjZnNlK2qZ9gOqm8j8

oLkB4RY5FROX6ZGFP0zs+VfZetSFJCQvAytZCoThnNFJK4Nh6TBxaU3oduW4FMBp

mC9/zvyllNcMKcIRSLmoxi4JtaR74TvIp9tuPI5NeUS+P92dzirkCjv6yJamGixU

7/K2tkiY3h/mWov/Ts1+v6Qn4UP6NZNzjvYJO+fBHWPXaLOeaZ9tEGj6UTqRvTTv

MIIDHjCCAgagAwIBAgIIGcVprmKcIMcwDQYJKoZIhvcNAQEFBQAwMjEwMC4GA1UE

-----END CERTIFICATE-----

Here is the decoded version:

Certificate:

Data:

Version: 3 (0x2)

Serial Number:

19:c5:69:ae:62:9c:20:c7

Signature Algorithm: sha1WithRSAEncryption

Issuer: CN=foo.appspot.com

Validity

Not Before: Jan 27 23:00:23 2011 GMT

Not After : Jan 29 00:00:23 2011 GMT

Subject: CN=foo.appspot.com

Subject Public Key Info:

Public Key Algorithm: rsaEncryption

RSA Public Key: (2048 bit)

Modulus (2048 bit):

00:a8:6f:9b:c0:80:70:1f:d3:ca:28:9e:28:ac:7c:

ca:e6:37:09:bb:e6:a6:26:16:b9:b8:7e:2f:74:8e:

07:bd:42:15:2f:34:fa:6f:2f:fa:b7:d3:22:36:2f:

ee:12:6b:43:8a:b9:77:52:8d:c7:af:a8:bb:42:c3:

15:68:7d:78:d8:bc:3e:92:46:e1:5e:a6:79:dd:bc:

30:b1:f0:dc:14:4f:09:3f:2c:7e:12:db:90:b2:6d:

1f:69:71:7c:e3:a1:ef:e8:7c:78:67:87:e8:d3:02:

d3:44:45:f9:70:62:f3:7a:de:04:66:96:00:7a:13:

3c:c3:96:fe:52:23:b8:3e:35:03:8b:58:81:1b:f2:

5d:78:56:43:ee:8a:7c:a7:d4:b9:21:d6:ff:cb:f5:

77:9e:01:32:5f:c9:7d:9d:36:ff:90:2c:07:34:f7:

f3:5e:2b:6f:ec:d9:08:a6:9f:63:96:b4:14:6d:80:

c7:dd:c8:fb:e8:f9:2e:ba:06:95:d4:9f:59:e6:2c:

10:29:1a:96:0b:f4:b9:7c:7e:07:ac:c1:e6:ec:2b:

a5:eb:57:2e:6c:db:5c:4d:70:06:1a:d0:66:39:b1:

39:74:da:a0:5a:f8:70:66:50:2c:d6:72:c9:d6:3c:

71:d2:22:a0:64:31:a1:e2:45:64:19:c3:c9:eb:aa:

e0:0d

Exponent: 65537 (0x10001)

X509v3 extensions:

X509v3 Basic Constraints: critical

CA:FALSE

X509v3 Key Usage: critical

Digital Signature

X509v3 Extended Key Usage: critical

TLS Web Client Authentication

Signature Algorithm: sha1WithRSAEncryption

8b:06:4c:a5:d1:a1:35:3b:32:c8:ca:62:4e:ef:65:88:5d:59:

5e:eb:38:3e:62:6e:83:4c:a7:c2:99:79:a9:37:d6:9e:4c:37:

95:41:86:19:80:4b:90:6e:91:e1:08:ad:8c:49:f4:ce:5d:90:

12:25:fd:ce:96:41:d5:e4:c9:0c:f7:23:66:73:65:2b:6a:99:

f6:03:aa:9b:c8:fc:a0:b9:01:e1:16:39:15:13:97:e9:91:85:

3f:4c:ec:f9:57:d9:7a:d4:85:24:24:2f:03:2b:59:0a:84:e1:

9c:d1:49:2b:83:61:e9:30:71:69:4d:e8:76:e5:b8:14:c0:69:

98:2f:7f:ce:fc:a5:94:d7:0c:29:c2:11:48:b9:a8:c6:2e:09:

b5:a4:7b:e1:3b:c8:a7:db:6e:3c:8e:4d:79:44:be:3f:dd:9d:

ce:2a:e4:0a:3b:fa:c8:96:a6:1a:2c:54:ef:f2:b6:b6:48:98:

de:1f:e6:5a:8b:ff:4e:cd:7e:bf:a4:27:e1:43:fa:35:93:73:

8e:f6:09:3b:e7:c1:1d:63:d7:68:b3:9e:69:9f:6d:10:68:fa:

51:3a:91:bd:34:ef:d2:c7:07:59:53:83:15:4a:cb:4e:ee:dd:

b3:7c:e2:fc:9c:a9:d8:6f:f9:51:be:ad:c8:d7:b6:a6:91:0a:

22:99:1d:82