PKCS11 diverveKey() и encrypt() возвращают разные результаты для 3DES
Я работаю над проблемой получения ключа с помощью HSM и PKCS11, и в настоящее время я не могу понять, почему я вижу совершенно разные результаты в зависимости от того, использую ли я метод DerveKey(), а не метод encrypt(). В обоих случаях я пытаюсь использовать алгоритм DESede/ECB/NoPadding для результата, и в то же время, в зависимости от того, какой метод (производный ключ или шифрование) я использую для получения результата, я вижу разные результаты.
Остановимся на мгновение, чтобы дать общий обзор... Я использую специальную методологию деривации ключей, используемую Глобальной платформой для диверсификации главного ключа для использования на смарт-карте. Метод начинается с главного ключа и 8 байтов данных (данных диверсификации), которые используются для получения нового ключа. Байты данных div буквально просто зашифрованы с помощью главного ключа с помощью DESede/ECB/NoPadding, и результат используется как часть нового производного ключа 3DES. (На самом деле, выполняется несколько шифрований, и результаты объединяются для формирования нового ключа, но здесь это не проблема.)
Я проверил главный ключ в обоих случаях, проверил, что данные диверсификации одинаковы в обоих случаях, проверил, что я использую один и тот же алгоритм с одинаковым заполнением. Я также попытался изменить шаблон производного ключа на DES, 2DES, 3DES. Все дают похожие результаты, которые отличаются только по длине.
В настоящее время я реализовал свой тестовый пример, используя оболочку IAIK (оболочку pkcs11, написанную на Java), которую я опубликую здесь. Ключ - это просто тестовый ключ, а данные div - это образцы данных div, поэтому здесь нет конфиденциальной информации. Сначала я создаю базовый ключ в HSM, затем пытаюсь получить новый ключ, вызвав метод session.deriveKey() с использованием данных диверсификации. Полученные байты ключа печатаются в шестнадцатеричном формате (значение ключа неверно, основываясь на моей текущей работающей реализации, которая выводит ключ в памяти, а не в HSM). Затем я просто инициализирую сеанс для выполнения шифрования с использованием главного ключа, а затем зашифрую данные диверсификации. Это дает ожидаемое значение (опять же, сверяется с работающей реализацией).
Что мне нужно, так это понять, почему эти операции дают разные результаты, когда я использую один и тот же базовый ключ, одни и те же данные div и один и тот же алгоритм шифрования. Я не понимаю, что происходит с помощью AsseveKey(), и я не могу найти какую-либо документацию или исходный код, чтобы пролить свет на это. Я должен иметь возможность использовать метод diverveKey(), поскольку производный ключ не будет доступен за пределами HSM.
Любое понимание приветствуется.
Mechanism keyGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_DES3_KEY_GEN);
List supportedMechanisms = Arrays.asList(token.getMechanismList());
if (!supportedMechanisms.contains(Mechanism.get(PKCS11Constants.CKM_DES3_KEY_GEN))) {
output_.println("Mechanism not supported: DES3_KEY_GEN");
return;
}
// This is the master key that I want to diversify
DES3SecretKey baseKeyTemplate = new DES3SecretKey();
baseKeyTemplate.getValue().setByteArrayValue(new byte[] {0x3d, 0x20, 0x5b, 0x29, (byte) 0xfd, 0x04, (byte) 0xd9, (byte) 0x89, (byte) 0xd0, (byte) 0xfd, (byte) 0x85, (byte) 0xd5, (byte) 0xf7, (byte) 0xb3, 0x31, (byte) 0xd3,
0x3d, 0x20, 0x5b, 0x29, (byte) 0xfd, 0x04, (byte) 0xd9, (byte) 0x89});
baseKeyTemplate.getDerive().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getPrivate().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getSensitive().setBooleanValue(Boolean.FALSE);
baseKeyTemplate.getExtractable().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getLabel().setCharArrayValue("GP-3des-aba".toCharArray());
baseKeyTemplate.getObjectClass().setLongValue(PKCS11Constants.CKO_SECRET_KEY);
baseKeyTemplate.getKeyType().setLongValue(PKCS11Constants.CKK_DES3);
baseKeyTemplate.getEncrypt().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getDecrypt().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getWrap().setBooleanValue(Boolean.TRUE);
baseKeyTemplate.getUnwrap().setBooleanValue(Boolean.TRUE);
output_.println("baseKeyTemplate: " + baseKeyTemplate.toString());
SecretKey baseKey = (SecretKey) session.createObject(baseKeyTemplate);
System.out.println("Base key: ");
System.out.println(baseKey.toString());
output_
.println("################################################################################");
output_.println("derive key");
//DES3 Key Template
DESSecretKey derived3DESKeyTemplate = new DESSecretKey();
SecretKey derivedKeyTemplate = derived3DESKeyTemplate;
derivedKeyTemplate.getSensitive().setBooleanValue(Boolean.FALSE);
derivedKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
derivedKeyTemplate.getExtractable().setBooleanValue(Boolean.TRUE);
derivedKeyTemplate.getPrivate().setBooleanValue(Boolean.FALSE);
derivedKeyTemplate.getKeyType().setLongValue(PKCS11Constants.CKK_DES);
// This represents the diversification data (.ie div bytes from some smart card)
byte[] data = new byte[] {0x00, (byte) 0x84, 0x30, (byte) 0x95, 0x35, 0x05,(byte) 0xf0, 0x01};
KeyDerivationStringDataParameters param = new KeyDerivationStringDataParameters(data);
Mechanism mechanism = Mechanism.get(PKCS11Constants.CKM_DES3_ECB);
if (!supportedMechanisms.contains(Mechanism
.get(PKCS11Constants.CKM_DES3_ECB))) {
output_.println("Mechanism not supported: CKM_DES3_ECB");
return;
}
mechanism.setParameters(param);
System.out.println("Derivation Mechanism: ");
output_.println(mechanism.toString());
output_
.println("--------------------------------------------------------------------------------");
Key derivedKey = session.deriveKey(mechanism, baseKey, derivedKeyTemplate);
if (derivedKey == null) {
output_.println("Found NO key that can be used for encryption.");
output_.flush();
System.exit(0);
}
System.out.println("Derived key: ");
output_.println(derivedKey.toString());
output_
.println("################################################################################");
output_.println("finished");
// initialize for encryption
Mechanism encryptionMechanism = Mechanism.get(PKCS11Constants.CKM_DES3_ECB);
session.encryptInit(encryptionMechanism, baseKey);
byte[] encryptedData = session.encrypt(data);
System.out.println("Encrypted data: " + new String(Hex.encodeHex(encryptedData)));
// This is the second part of the derived key, let's not worry about this yet since the first part isn't
// working.
// data = new byte[] {0x00, (byte) 0x84, 0x30, (byte) 0x95, 0x35, 0x05,(byte) 0x0f, 0x01,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//
// session.encryptInit(encryptionMechanism, baseKey);
// encryptedData = session.encrypt(data);
// System.out.println("Encrypted data: " + new String(Hex.encodeHex(encryptedData)));
session.closeSession();
pkcs11Module.finalize(null);
Вот соответствующий вывод, когда я запускаю приведенный выше код:
Base key:
Object Class: Secret Key
Token: true
Private: true
Modifiable: true
Label: GP-3des-aba
Key Type: DES3
ID: <NULL_PTR>
Start Date: 00.00.0000 (DD.MM.YYYY)
End Date: 00.00.0000 (DD.MM.YYYY)
Derive: true
Local: false
Key Generation Mechanism: <Information unavailable>
Allowed Mechanisms: <NULL_PTR>
Sensitive: false
Encrypt: true
Decrypt: true
Sign: false
Verify: false
Wrap: true
Unwrap: true
Extractable: true
Always Sensitive: false
Never Extractable: false
Check Value: <Attribute not present>
Wrap With Trusted: <Attribute not present>
Trusted: <Attribute not present>
Wrap Template: <Attribute not present>
Unwrap Template: <Attribute not present>
Value (hex): 3d205b29fd04d989d0fd85d5f7b331d33d205b29fd04d989
################################################################################
derive key
Derivation Mechanism:
Mechanism: CKM_DES3_ECB
Parameters:
String data (hex): 008430953505f001
--------------------------------------------------------------------------------
01/18/13 14:12:10 CALL: entering (in Java_iaik_pkcs_pkcs11_wrapper_PKCS11Implementation_C_1DeriveKey)
[snip]
Derived key:
Object Class: Secret Key
Token: true
Private: false
Modifiable: true
Label: <NULL_PTR>
Key Type: DES
ID: <NULL_PTR>
Start Date: 00.00.0000 (DD.MM.YYYY)
End Date: 00.00.0000 (DD.MM.YYYY)
Derive: false
Local: false
Key Generation Mechanism: CKM_DES3_ECB
Allowed Mechanisms: <NULL_PTR>
Sensitive: false
Encrypt: false
Decrypt: false
Sign: false
Verify: false
Wrap: false
Unwrap: false
Extractable: true
Always Sensitive: false
Never Extractable: false
Check Value: <Attribute not present>
Wrap With Trusted: <Attribute not present>
Trusted: <Attribute not present>
Wrap Template: <Attribute not present>
Unwrap Template: <Attribute not present>
Value (hex): 3efe0eab6d3db397 <--- call to deriveKey() value incorrect
################################################################################
finished
01/18/13 14:12:12 CALL: entering (in Java_iaik_pkcs_pkcs11_wrapper_PKCS11Implementation_C_1EncryptInit)
01/18/13 14:12:12 CALL: exiting (in Java_iaik_pkcs_pkcs11_wrapper_PKCS11Implementation_C_1EncryptInit)
01/18/13 14:12:12 CALL: entering (in Java_iaik_pkcs_pkcs11_wrapper_PKCS11Implementation_C_1Encrypt)
01/18/13 14:12:12 CALL: exiting (in Java_iaik_pkcs_pkcs11_wrapper_PKCS11Implementation_C_1Encrypt)
Encrypted data: 3fff0faa6c3cb297 <--- call to encrypt() returns the expected value
1 ответ
Оказывается, что ключ, сгенерированный в HSM с использованием DeriveKey, и байты ключа, сгенерированные мной с помощью session.encrypt(), по сути, одинаковы, если учитывать, как работает четность DES.
Наименее значимым битом каждого байта в ключе DES является бит четности, который игнорируется во многих реализациях и не используется как часть ключа, поэтому, если я правильно установлю биты четности в моем результате шифрования, оба результата совпадут.
0x3efe0eab6d3db397 <--- HSM сгенерировал значение с правильной нечетной четностью
00111110 11111110 00001110 10101011 01101101 00111101 10110011 10010111
0x3fff0faa6c3cb297 <--- результат шифрования без битов четности, установленных правильно
00111111 11111111 00001111 10101010 01101100 00111100 10110010 10010111