Encrypting sensitive data

/by apr/

Protecting Customer's Data

Many jPOS-EE users and PEP members have a recurring problem: encrypting sensitive data in order to protect their customers and if possible, make auditors happy.

But there’s a recurring issue: unless you use an HSM, a smartcard to protect your keystore, or you have someone enter a pass-phrase at boot time, you have to live with the fear that someone could somehow access your keystore, get your master keys and decrypt your sensitive data.

The problem becomes more critical in situations where jPOS is installed at small merchants or medium-sized store chains, we can’t ask them to enter a passphrase at boot time, we don’t have smartcard readers there and we don’t have HSMs. We could do some security through obscurity but we can leave that to our competitors 🙂

Based on the work performed by Hani in the jPOS’s security module, and the DUKPT implementation we did with Dave from InfoSecBlurb in a related project, I came up with a nice solution that I’d like to present here in order to get your feedback.

Here is a simple example:

  SSM ssm = (SSM) NameRegistrar.get ("ssm");
  SecureKeyStore ks = new SimpleKeyFile ("cfg/keys.cfg");
  SecureDESKey bdk = (SecureDESKey) ks.getKey ("our-bdk");
  String encrypted = ssm.customEncrypt (bdk, "4111111111111111=0805".getBytes());
  String decrypted = new String (ssm.customDecrypt (bdk, encrypted), "ISO8859_1");

customEncrypt produces something like this:

B9B35297F48C792646CED3B367F40E6D,F302BA266FDAC3A6D4773B4C74553C49F584934BCA9224F8,5C9538880BED87FD

and customDecrypt gives you your original byte[] array back.

Here is what we do:

  • We generate three clear-text 128-bit keys
  • We use the security module’s console to enter those keys and produce a BDK encrypted under the local master keys (using standard key-ceremony procedures)
  • The BDK cryptogram is stored in the keys.cfg file
  • When we receive a message to encrypt, we generate two 64-bit random DES keys that we use as KSNs. We split them to produce a baseKeyID, deviceID and transactionCounter as required by KeySerialNumber
  • We use our BDK and these two KSNs to produce a pair of derived 64 bit keys
  • We combine these two 64 bit keys and do 3DES with them in order to encrypt our payload
  • We use both KSNs and derived keys plus the encrypted payload to produce an SHA hash, so we can perform sanity checks

So far, so good, it seems pretty nice, but it’s still breakable if you have access to the master keys and the BDK’s cryptograms, or, if you have access to the jar and you manage to call ssm.customDecrypt with the right parameters.

In order to mitigate that, I’m experimenting with several anti-debug features; besides obfuscating the jpos-ee jar (that’s something that the PEP version already has), we are trying to have a hash of the stacktrace of the caller in order to validate the caller id and we have some timing control too.

But anyway, here is the best thing, a very simple one: we don’t have to run this at every merchant, we can use this approach (or an HSM-based one) at a central location (such as an acquirer or transaction switch).

When an acquirer or transaction switch receives say a 0200 message with a some sensitive data (i.e. PAN, EXP, TRACK1, TRACK2), it could reply with a 0210 carrying a cryptogram of that data computed at the remote facility, where we don’t have access to the LMKs. We could do that with an extremely simple ISOFilter.

Imagine something like this:

Message sent from merchant to acquirer:


    <isomsg>
      <field id="0" value="0200"/>
      <field id="3" value="000000"/>
      <field id="4" value="000000012183"/>
      <field id="7" value="0309123613"/>
      <field id="11" value="010370"/>
      <field id="12" value="123613"/>
      <field id="13" value="0309"/>
      <field id="22" value="022"/>
      <field id="24" value="105"/>
      <field id="25" value="00"/>
      <field id="35" value="4111111111111111=0805"/>
      <field id="41" value="29110001"/>
      <field id="42" value="012345         "/>
      <field id="44" value="0000000000"/>
      <field id="48" value="0010000000000000"/>
      <field id="49" value="858"/>
      <field id="60" value="jPOS-1.5.1"/>
      <field id="62" value="7297873"/>
    </isomsg>

Message response from acquirer:

    <isomsg>
      <field id="0" value="0210"/>
      <field id="3" value="000000"/>
      <field id="4" value="000000012183"/>
      <field id="7" value="0309123613"/>
      <field id="11" value="010370"/>
      <field id="12" value="123613"/>
      <field id="13" value="0309"/>
      <field id="22" value="022"/>
      <field id="24" value="105"/>
      <field id="37" value="000000130682"/>
      <field id="38" value="130682"/>
      <field id="39" value="00"/>
      <field id="41" value="29110001"/>
      <field id="42" value="012345         "/>
      <field id="44" value="0000000   0000000"/>
      <field id="48" value="0010000000000000"/>
      <field id="49" value="858"/>
      <field id="62" value="7297873"/>
      <field id="63" value="B9B35297F48C792646CED3B367F40E6D,F302BA266FDAC3A6D4773B4C74553C49F584934BCA9224F8,5C9538880BED87FD"/> 
    </isomsg>

The acquirer’s outgoing filter would build a custom cryptogram for us either using the aforementioned algorithm, an HSM on any other similar approach, and it would put it in a private field for us (in this case we used field 63, but could be any other free variable length private field) and an incoming filter at the acquirer would take care of decrypting it locally, unsetting that private field and setting the appropriate field 35 (same goes for other sensitive fields such as 2,14,45,52,55, etc.).

At the client application, you could store some clear-text information (such as BIN, last four digits of the PAN) for reporting purposes and store that opaque cryptogram produced by your acquirer/processor; that would be your ticket to request funds for that transaction if for some reason you need to perform a batch upload operation.

Although an scheme like this wouldn’t solve problems like this, it would certainly help merchants protect its customers’ data with a minor effort from the acquirers.

The encrypted information could be an inner ISOMsg with just some sensitve field. The document Secure Payment Framework presents some related concepts.

I’d love to listen to potential flaws in this. I’d also like you to exchange some anti-debug ideas and links. So far, I’m serializing a Throwable’s stacktrace, producing and validating a set of hashes with valid caller id info, an approach that is extremely sensible to JVM versions…

2 thoughts on “Encrypting sensitive data

  1. Andy Orrock

    jPOS readers and users will be interested to note that a good applicability for Alejandro’s proposed approach is for adherence to Visa’s CISP program.

    For auditing purposes and network compliance, applications connected to the Visa network (either directly or through a payment gateway like FDR, Fifth Third, NPC, etc.) must adhere to Visa’s Cardholder Information Security Program (“CISP”), which dictates acceptable practices in regards to the protection of cardholder data for official information, refer to http://usa.visa.com/business/accepting_visa/ops_risk_management/cisp.html. The specific requirements at issue here are those related to cardholder data – these are discussed in bullets 3.3 and 3.4 of Visa’s Payment Card Industry (“PCI”) PCI Security Audit (see embedded PDF as hyperlink in Visa page referenced above). Those requirements read:

    3.3 Mask account numbers when displayed (the first six and last four digits are the maximum number of digits to be displayed).

    Note that this does not apply to those employees and other parties with a specific need to see full credit card numbers.

    3.4 Render sensitive cardholder data unreadable anywhere it is stored, (including data on portable media, in logs, and data received from or stored by wireless networks) by using any of the following approaches:

    • One-way hashes (hashed indexes) such as SHA-1

    • Truncation

    • Index tokens and PADs, with the PADs being securely stored

    • Strong cryptography, such as Triple-DES 128-bit or AES 256-bit with associated key management processes and procedures.

  2. apr Post author

    While reviewing use cases for the proposed scheme, I found that it has flaw when dealing with stand-in situations, where you need to SAF a message. You don’t have access to the acquirer’s encryption service at that time.

    I thought we could use an hybrid approach, local encryption until you transmit the transaction and get the acquirer-generated cryptogram. But this is actually not required if we use PKI.

    So the proposed scheme seems good to me for local encryption where you need to protect customer information. When you can use a remote encryption service, PKI is the way to go. You just need to encrypt using your acquirer’s public key.

Comments are closed.