The community forums are in BETA and closed to the public.

Encryption for APNS, GCM, WMS with PubNub

originally authored by Geremy Cohen

Do you need to send sensitive data as a push notification via APNS or GCM just like you do with a PubNub realtime data streams? There are some initial hurdles to overcome due to a couple of factors:

  1. PubNub would receive the message payload completely encrypted unaware that there is push gateway attributes embedded.
  2. iOS and Android would not inherently know how to decrypt your push notifications when your app is not in the foreground.

Let's see what you need to do to workaround these two challenges.

Dictionary objects sent to APNS and GCM mobile services must be in a well-formed format in order for the remote service (operated by Apple, Google, etc) to process.

For example, when sending native APNS and GCM alerts to Apple and Google via PubNub, you could send in this format:

{
    "pn_apns": {
        "aps": {
            "alert": "Your order is ready for pickup!",
            "badge": 1,
            "payment_info": {
                "credit_card": 987656789876,
                "expiration": "0108"
            }
        }
    },
    "pn_gcm": {
        "data": "Your order is ready for pickup!",
        "payment_info": {
            "credit_card": 987656789876,
            "expiration": "0108"
        }
    },
    "realtime_data": {
        "info": "This is data all non-APNS and non-GCM devices would receive. You can encrypt the entire set of data here and decrypt it in subscribe and history callbacks."
    }
}

When the above message is published:

  • APNS-gateway enabled devices will only receive the data contained from within the pn_apns key.
  • GCM-gateway enabled devices will only receive the data contained from within the pn_gcm key.
  • Non-APNS/GCM devices will receive the entire message payload, including the push gateway keys/values.

As noted above, the value of the APNS object (contained within the pn_apns key) contains an object that will be forwarded in whole to the APNS gateway.

Note that it has one mandatory key: aps. Within the aps object, it has other non-mandatory fields, which include alert and badge, as well as some additional user-info/metadata fields contained within the payment_info key: credit_card and expiration.

Normally, when encryption is enabled for a PubNub publishing client, the entire payload is encrypted. If we had encryption enabled, and we published the above message, it would hit the wire looking something like: fghj678asd7as878987sdasd99asdasdd==

Not very useful to our remote device users.

So the question is...

How can PubNub be used to deliver both well-formed data to Apple and/or GCM, while still enabling for the transport of encrypted data?

The answer is to selectively encrypt certain keys using PubNub's encrypt() and decrypt() methods. Before we jump into the code, here is our objective:

  • Keep all PubNub object keys in plaintext. This enables the PN mobile gateway to process / route the traffic appropriately.
  • Keep all Apple/Google provider keys in plaintext. This enables Apple/Google to route / process the traffic, appropriately.
  • Encrypt all sensitive, non-mandatory fields.

All official PubNub client SDKs are guaranteed to encrypt and decrypt with other official PubNub client SDKs. So if you encrypt with Java, you can decrypt with iOS. If you encrypt with iOS, you can decrypt with Android, etc. Since we are discussing specifically the iOS and Android realms in this article, we'll use these client libraries as examples.

In Java and Android (same method for both), we'd encrypt using the PubnubCrypto class: https://rawgit.com/pubnub/java/master/java/doc/com/pubnub/api/PubnubCrypto.html ~~~ import com.pubnub.api.*; import org.bouncycastle.crypto.InvalidCipherTextException;

PubnubCrypto pnc; String ciphertext; String plaintext;

// instantiate a new crypto object using 'enigma' as the cipher key pnc = new PubnubCrypto("enigma"); try { // encrypt the string 'foo' ciphertext = pnc.encrypt("foo"); } catch (InvalidCipherTextException e) { e.printStackTrace(); } ~~~

And then, we'd decrypt similarly, using the decrypt() method:
~~~ try { // decrypt the string 'foo' plaintext = pnc.decrypt(ciphertext); } catch (InvalidCipherTextException e) { e.printStackTrace(); } ~~~

On iOS, we'd use the equivalent counterparts: https://github.com/pubnub/objective-c/tree/master/iOS#encrypt--decrypt-methods

You must have already setup a cipher key in order for this to work: https://github.com/pubnub/objective-c/tree/master/iOS#encryption-notes ~~~ NSString *cipherText = [PubNub AESEncrypt:@"foo"]; NSString *plainText = [PubNub AESDecrypt:cipherText]; ~~~

The next step is to create the PubNub message accordingly, to create the half-encrypted, half-plaintext message object. Run the sensitive values through the encryption logic, and replace the plaintext with the ciphertext:

{
    "pn_apns": {
        "aps": {
            "alert": "Your order is ready for pickup!",
            "badge": 1,
            "payment_info": {
                "credit_card": "zxcvbnm123456==",
                "expiration": "mnbvcxz987665=="
            }
        }
    },
    "pn_gcm": {
        "data": "Your order is ready for pickup!",
        "payment_info": {
            "credit_card": "zxcvbnm123456==",
            "expiration": "mnbvcxz987665=="
        }
    },
    "realtime_data": {
        "zxcvbnkviuckje85858kdkjfj3ikkfaijeifkdeom123456===",
    }
}

When you publish this message, Apple and PubNub intermediaries will have the information they need to reliably deliver this APNS message to the device.

When it arrives and the app is not foreground, the message "Your order is ready for pickup!" will be displayed to the user.

When the user clicks on this notification, the app will come to foreground, and this message will be routed to the method in the app:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo

From here, you can selectively decrypt the payment_info values from the userInfo object via the AESDecrypt methods.


Comments to this discussion are now closed!