4

My application is acting as a switchboard to transfer very sensitive messages between two parties and I'm trying to figure out how I can do this without "looking" at the message they're trying to send.

I have a HSM, and I've generated a keypair for the sender - they're going to encrypt the message with their public key that I gave them, and I can decrypt the message using the private key I have in the HSM.

I'm going to then pass that message on by encrypting it with the public key of the ultimate receiver, which I have.

Is there any way to chain these two operations inside the HSM, without having the decrypted message anywhere near my application memory? I would like the plaintext content to never leave the boundaries of the HSM.

I know that some HSMs have a CodeSafe / SEE machine feature that lets me write embedded system code and run it inside the HSM, before I get into that I'd like to see if there's a way using the general PKCS / JCE / CNG APIs to do this safely.

Sudhir Jonathan
  • 16,998
  • 13
  • 66
  • 90
  • 2
    I think you can do this with key wrapping. Use the sender's private key to unwrap the per-message symmetric key: you get a handle to the symmetric key, not the symmetric itself, and then you wrap that with the next receiver's public key. – Gilles 'SO- stop being evil' Jul 30 '19 at 06:29
  • Some HSMs provides translate data feature, where you can translate(decrypt with key1 and encrypt with key2) the data from incoming data key1 to outgoing key key2, the operation takes place inside HSM so you wont really be touching clear data, but these commands looks to be with symmetric keys only, you will have to device key exchange mechanism for the symmetric data key in that case – Pras Jul 30 '19 at 07:04
  • 1
    *...I've generated a keypair for the sender...* Well then, the game is over, you can read all their messages. – President James K. Polk Jul 30 '19 at 15:00
  • 2
    AFAIK none of the common cryptographic APIs support secure re-encryption. BTW I answered [similar question for PKCS#11 interface](https://stackoverflow.com/a/53310211/3325704) few months ago. – jariq Jul 30 '19 at 19:59
  • @Pras can you please be more specific about the HSM vendor/model that provides "translate data feature"? – jariq Jul 30 '19 at 20:01
  • @jariq Atalla Ax160 models support translate data – Pras Jul 31 '19 at 03:32
  • @Pras Thanks, will check out those HSMs. – Sudhir Jonathan Jul 31 '19 at 10:02
  • @Giles will look into that. The payload is indeed a symmetric key in this case, but I have no information about it’s format or generation. I don’t know if the HSM will recognize it as being crypto material and give me an opaque handler. – Sudhir Jonathan Jul 31 '19 at 10:02
  • 1
    @JamesKPolk yes, but the sender is paying me a great deal of money to fan out the message securely to tens of thousands of devices whose public keys I’m managing. They trust me to do it right, which is why they’ve encrypted it with the public key I gave them. I’m trying to make sure the payload gets to all its destinations without me or my team or employees or hackers ever being able to see it. – Sudhir Jonathan Jul 31 '19 at 10:05
  • Many HSM's have methods for key derivation using XOR. For XOR you can decrypt / encrypt in any order. As for the type of key, just say that it is a HMAC key, a HMAC key can have any size (although I would not be surprised if HSM's will still restrict you to certain sizes). – Maarten Bodewes Jul 31 '19 at 11:16
  • As Gilles wrote above, simple unwrapping into a session-only generic secret key, which then can be wrapped for all the recipients and deleted should work.....And which particular HSM are you using? Some specific settings (e.g. policy, unwrap template) can be (and should be) further used to tighten what is allowed to do with the stored/imported key material. – vlp Aug 01 '19 at 08:29
  • A Thales nCipher series, don't know the exact model. Think we're just going to go with CodeSafe SEE and write C/C++ to run inside the HSM. Need to do some other checks as well anyway. – Sudhir Jonathan Aug 02 '19 at 17:46

1 Answers1

5

If all you need is to re-encrypt the same secret under a different key, you can use C_Unwrap to create a temporal HSM object with value of the translated secret and then use C_Wrap to encrypt the value of this temporal HSM object for all the recipients.

This way the secret will never leave HSM.

Something like this (verified to be working on SafeNet Luna 7 with RSA-OAEP):

// Your private key for 'decrypting' secret. Must have key unwrapping allowed
CK_OBJECT_HANDLE hsmPrivateKey = ... ;

// Encrypted secret
byte[] wrappedKey = ... ; // 

// Template for temporal generic secret key with value of the secret
CK_ATTRIBUTE[] tempTemplate = new CK_ATTRIBUTE[] {
        new CK_ATTRIBUTE(CKA.CLASS, CKO.SECRET_KEY),
        new CK_ATTRIBUTE(CKA.KEY_TYPE, CKK.GENERIC_SECRET),
        new CK_ATTRIBUTE(CKA.TOKEN, false),
        new CK_ATTRIBUTE(CKA.PRIVATE, true),
        new CK_ATTRIBUTE(CKA.EXTRACTABLE, true),
        new CK_ATTRIBUTE(CKA.SENSITIVE, true),
        new CK_ATTRIBUTE(CKA.ENCRYPT, false),
        new CK_ATTRIBUTE(CKA.DECRYPT, false),
        new CK_ATTRIBUTE(CKA.WRAP, false),
        new CK_ATTRIBUTE(CKA.UNWRAP, false),
        new CK_ATTRIBUTE(CKA.SIGN, false),
        new CK_ATTRIBUTE(CKA.VERIFY, false),
        new CK_ATTRIBUTE(CKA.DERIVE, false)
};

// Unwrapping/decryption mechanism
CK_MECHANISM mechanism = ... ;

// Handle for temporal generic secret key with value of the secret
CK_OBJECT_HANDLE temporalValueHandle = new CK_OBJECT_HANDLE();

// Unwrap/decrypt the secret into temporal key
CryptokiEx.C_UnwrapKey(session, mechanism, hsmPrivateKey, wrappedKey, wrappedKey.length, tempTemplate, tempTemplate.length, temporalValueHandle);

// Wrap/encrypt the secret for recipients. Recipient public keys must have key wrapping allowed
for(CK_OBJECT_HANDLE recipientPublicKey : ... ) {
    LongRef resSize = new LongRef(0);
    CryptokiEx.C_WrapKey(session, mechanism, recipientPublicKey, temporalValueHandle, null, resSize);
    byte[] rewrappedKey = new byte[CryptokiUtils.safeIntCast(resSize.value)];
    CryptokiEx.C_WrapKey(session, mechanism, recipientPublicKey, temporalValueHandle, rewrappedKey, resSize);
    System.out.println("Re-wrapped key: " + bytesToHexString(rewrappedKey));
}

// Delete temporal generic secret key
CryptokiEx.C_DestroyObject(session, temporalValueHandle);

Good luck!

vlp
  • 7,811
  • 2
  • 23
  • 51
  • I'm afraid this solution does not fulfill OP's requirement "without having the decrypted message anywhere near my application memory". – jariq Aug 04 '19 at 14:35
  • 1
    @jariq Why -- decrypted message is inside HSM as a generic secret key. The application has only a handle for this key. Given the template of this generic secret key it is SENSITIVE...All the application does with the key is that it wraps it with public keys of "the recipients" (whoever they are)... – vlp Aug 04 '19 at 15:09
  • I'm sorry I didn't notice that you were using generic secret key. That's very interesting idea worth exploring! Now I wonder what's the size limit for generic key value. – jariq Aug 04 '19 at 16:15
  • @jariq I have never tried, but Luna 7 claims to support generation (via CKM_GENERIC_SECRET_KEY_GEN) of up to 512 bytes long generic secret keys. Also note that wrap/unwrap key has no multi-part variants. – vlp Aug 04 '19 at 16:48