0

I want to encrypt a large number of plaintext using PKCS#11 and SafeNet HSM devices. It will take a long time when I encrypt each plaintext one by one.

So It came to my mind if there are any methods in PKCS#11 which can encrypt several data at once?

I've found the below sample for encryption of multiple-part data in SafeNet C Programming manual which is using PKCS #11 APIs.

/* read, encrypt, digest and write the cipher text in chunks
*/ totbw = 0;
for ( ;; ) {
    br = fread(buffer, 1, sizeof(buffer), ifp);
    if ( br == 0 )
        break;
    
    /* digest */

    /* encrypt */
    curLen = sizeof(encbuffer);
    rv = C_EncryptUpdate(hrSession, buffer, (CK_SIZE)br, encbuffer, &curLen);
    CHECK_RV(FN "C_EncryptUpdate", rv);
    if (rv) return 1;
    
    /* write cipher text */
    br = fwrite(encbuffer, 1, (int)curLen, ofp);
    totbw += br;
}

It is mentioned in that manual:

For the encryption, we use C_EncryptUpdate, which continues a multiple-part encryption operation, processing another data part.

I want to know if this method can be used for encrypting multiple plaintexts at once or it will consider all buffer elements just as blocks of the same input data?

I'm looking for a solution which can encrypt multiple plaintexts but consider them as seperate item (not as blocks of a big single item).

VSB
  • 9,825
  • 16
  • 72
  • 145
  • this may be helpful https://stackoverflow.com/questions/33240942/pkcs11-questions-regarding-c-encryptupdate-c-encryptfinal and.. what the manual says ? – gusto2 Oct 16 '21 at 08:08
  • as well - could you describe the use case, why do you want to encrypt 20M short 16 bit messages? (to find if there is a better way to achieve the goal). – gusto2 Oct 16 '21 at 08:20

2 Answers2

2

It will consider all data as blocks of one input. It is for encrypting data as as stream. So if you think you can use it to parallelize your encryption process, you are wrong.

BTW, what mechanism do your use for your encryption? Don't tell me you are just using a asymmetric algorithm (such as RSA) directly. If you do, that's why your encryption process is very slow.

Update:

Off top of my head, you can try something like this:

1-Genrate a random number using C_GenerateRandom.
2-Encrypt this random using HSM.
3-Use this encrypted random to encrypt your data locally.
4-Send encrypted data and non-encrypted random number to other side.

Other side can encrypt received random to reach encryption key and decrypt received encrypted data.

I should say that this is a 30 sec design and it should consider other critical parts (like timestamp). But I wanted to show how it can be done without overusing your HSM card.

Afshin
  • 8,839
  • 1
  • 18
  • 53
  • I'm using it for encrypting plaintext with Symmetric algorithms. So there are no methods to reduce I/O overhead and decrease number of method calls? – VSB Oct 13 '21 at 08:03
  • @VSB if you are using quick symmetric algorithms(such as AES), your token probably cannot handle the throughput you need. How much data are you trying to encrypt using your token? – Afshin Oct 13 '21 at 08:05
  • Around 20 millions. I'm using ProtectServer with PL-1500 card. I connect to it using Ethernet connection. – VSB Oct 13 '21 at 08:10
  • @VSB 20 million what? blocks? bytes? – Afshin Oct 13 '21 at 08:10
  • 20 millions of plaintexts. Consider I want to encrypt 20M plaintexts that each of them is 16 bytes. – VSB Oct 13 '21 at 09:40
  • 2
    @VSB This is too big. Transferring your data over Ethernet takes more than 700-800 ms in best case (assuming 10 Gb/s card). Then passing data over Ethernet may become bottleneck itself. I cannot find PL-1500 throughput, but assuming you are using AES encryption (because AES has hardware implementations so normally gives a huge throughput boost), AES itself should be able to process your data in a reasonable time. So I guess your main problem is transferring data to card. I suggest try changing your data encryption to local using HSM generated keys and decrease Ethernet data transfers. – Afshin Oct 13 '21 at 13:44
  • Key is stored inside HSM for security (and this is the reason that HSM is used) so key cannot be extracted and moved to local system for local encryption. That is why we should send several requests to HSM. – VSB Oct 16 '21 at 07:41
  • 1
    @VSB It is always like that, it is not something new. But the idea is something else. create something like session key using key inside HSM which can be exported, then use this new key to encrypt your data locally. Sending that huge amount of data to HSM will not be practical. – Afshin Oct 16 '21 at 07:45
  • @VSB I added a small idea to reply too. – Afshin Oct 16 '21 at 07:55
  • @Afshin your "new idea" of "random number" is called envelope encryption. The random number effectively becomes a data encryption key and for AES it needs to be 16 bit long. So no saving there. The guy asks about 20M HSM operations and that takes some time. – gusto2 Oct 16 '21 at 08:06
  • @gusto2 I never said it was new. In addition, there is no need to create 20M keys. He can just create 1 encrypted number and use it for IV(or key) in CTR mode and encrypt all 20M messages locally(HTM used once). or better, encrypt 2 random number (HSM used twice) and uses XTS mode. The main idea is entangling local encryption with HSM in a way that data cannot be decrypted without HSM and HSM is used rarely. My example was to show what idea he need to use generally. – Afshin Oct 16 '21 at 08:17
  • 1
    Words "Send encrypted data" assumes that you MUST have a transport/session key that can be restored by other side. You can't use your HSM **symmetric** key on this stage because you ned a some way to restore it on other side. This is not secure design. – Alexander Oct 18 '21 at 08:00
0

I have this solution for you, this are two parts:

    1. encrypt in your hardware. this should resolved whit threads in JAVA.
    1. Use the Salt Concept by HSM only one time in your encrypt program.

------- in your hardware (# CP´s) Run a Program to Cipher Whit AES256 ----- but Using HSM for take a token or a numberRandom, this will be our SALT.

    import java.io.Serializable;
    import java.nio.charset.StandardCharsets;
    import java.security.spec.KeySpec;
    import java.util.Base64;
    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.PBEKeySpec;
    import javax.crypto.spec.SecretKeySpec;

    public class Encrypt implements Serializable {

        private static final String secretKeyAES = "My_Long_bytes_to_cipher";
        private static final String saltAES = "My_Salt_only_one_time_for_20M_ByHMS_Random";

        public Encrypt() {
}

public String getAES(String data) {
    try {
        byte[] iv = new byte[16];
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec keySpec = new PBEKeySpec(secretKeyAES.toCharArray(), saltAES.getBytes(), 65536, 256);
        SecretKey secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
        SecretKeySpec secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes("UTF-8")));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

public String getAESDecrypt(String data) {
    byte[] iv = new byte[16];
    try {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec keySpec = new PBEKeySpec(secretKeyAES.toCharArray(), saltAES.getBytes(), 65536, 256);
        SecretKey secretKeyTemp = secretKeyFactory.generateSecret(keySpec);
        SecretKeySpec secretKey = new SecretKeySpec(secretKeyTemp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        return new String(cipher.doFinal(Base64.getDecoder().decode(data)));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
    }

-----this answer resolve HSM petitions, but now you should resolve whit petitions in JAVA.

the most processing would be done on the CPU side