Горячие для использования механизмов CKM_ECDH1_DERIVE с pkcs11interop

Я купил HSM NitroKey и хотел получить секрет с ЕС. Предыдущий вопрос

Для этого я хочу использовать CKM_ECDH1_DERIVE механизмы. Который поддерживается этим HSM, смотрите:

Ссылаясь на спецификацию PKCS#11, это следует учитывать:

  1. Механизм CKM_ECDH1_DERIVE должен использоваться с функцией Derive (Стр. 188)
  2. Механизм CKM_ECDH1_DERIVE ожидает параметр CK_ECDH1_DERIVE_PARAMS (Стр. 222) с такими аргументами:
    1. kdf: функция вывода ключа, используемая для общего секретного значения (CKD)
    2. sharedData: некоторые данные совместно используются двумя сторонами
    3. publicData: значение открытого ключа EC другой стороны
  3. Функция DeriveKey ожидает этих аргументов:
    1. Механизм CKM.CKM_ECDH1_DERIVE
    2. ObjectHandle PrivateKey
    3. ObjectAttributes (Страница 338)
      1. CKA.CKA_CLASS -> CKO.CKO_SECRET_KEY
      2. CKA.CKA_KEY_TYPE -> CKK.CKK_GENERIC_SECRET
      3. Но "Тем не менее, поскольку все эти факты неявно присутствуют в механизме, нет необходимости указывать какой-либо из них", поэтому они могут быть нулевыми?

проблема

Поэтому, используя эту информацию, я попытался реализовать метод.

Но я получаю эту ошибку:

Net.Pkcs11Interop.Common.Pkcs11Exception: метод C_DeriveKey возвратил CKR_TEMPLATE_INCOMPLETE

в Session.DeriveKey,

Объяснение CKR_TEMPLATE_INCOMPLETE (Стр. 64):

Если значений атрибутов в предоставленном шаблоне вместе со всеми значениями атрибутов по умолчанию и любыми значениями атрибутов, внесенными в объект самой функцией создания объекта, недостаточно для полного указания объекта для создания, тогда попытка должна завершиться неудачно с кодом ошибки CKR_TEMPLATE_INCOMPLETE.

и здесь (стр. 98)

CKR_TEMPLATE_INCOMPLETE: Шаблон, указанный для создания объекта, является неполным и не имеет некоторых необходимых атрибутов. См. Раздел 10.1 для получения дополнительной информации.

Но я использовал несессерные атрибуты:

  1. CKA.CKA_CLASS -> CKO.CKO_SECRET_KEY
  2. CKA.CKA_KEY_TYPE -> CKK.CKK_GENERIC_SECRET

Идеи?

Код

private const string LibraryPath = @"C:\Windows\System32\opensc-pkcs11.dll";

public static byte[] Derive(string privateEc, string publicEc)
{
    Func<string, Session, CKO, ObjectHandle> getObjectHandle = (label, session, keyType) =>
    {
        var objectAttributes = new List<ObjectAttribute>
        {
            new ObjectAttribute(CKA.CKA_CLASS, keyType),
            new ObjectAttribute(CKA.CKA_LABEL, label),
            new ObjectAttribute(CKA.CKA_TOKEN, true)
        };

        return session.FindAllObjects(objectAttributes).First();
    };

    Func<ObjectHandle, Session, CKA, byte[]> getDataFromObject = (handle, session, type) =>
    {
        var attributes = new List<ulong> {(ulong) type};
        var requiredAttributes = session.GetAttributeValue(handle, attributes);
        return requiredAttributes[0].GetValueAsByteArray();
    };

    using (Pkcs11 pk = new Pkcs11(LibraryPath, false))
    {
        var slot = pk.GetSlotList(false).First();

        using (Session session = slot.OpenSession(false))
        {
            session.Login(CKU.CKU_USER, UserPin);

            var objectPrivate = getObjectHandle(privateEc, session, CKO.CKO_PRIVATE_KEY);
            var objectPublic = getObjectHandle(publicEc, session, CKO.CKO_PUBLIC_KEY);

            var publicKey = getDataFromObject(objectPublic, session, CKA.CKA_VALUE);

            byte[] data = session.GenerateRandom(32);
            var mechanism = new Mechanism(CKM.CKM_ECDH1_DERIVE, new CkEcdh1DeriveParams(1000, data, publicKey));

            var deriveAttributes = new List<ObjectAttribute>
            {
                new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_SECRET_KEY),
                new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_GENERIC_SECRET),
            };

            var derivedKey = session.DeriveKey(mechanism, objectPrivate, deriveAttributes);

            var derivedSecret = getDataFromObject(derivedKey, session, CKA.CKA_VALUE);

            Console.Out.WriteLine(Convert.ToBase64String(derivedSecret));

            return derivedSecret;
        }
    }
}

См. Также суть (тот же код) https://gist.github.com/dhcgn/4ea235cdb20155ec5ea9dc9bbf3c9887

Обновить

Теперь с обновленным списком ObjectAttributes (спасибо за ответ) я получаю исключение Net.Pkcs11Interop.Common.Pkcs11Exception : Method C_DeriveKey returned CKR_DATA_LEN_RANGE,

CKR_DATA_LEN_RANGE: Входные данные в незашифрованном виде для криптографической операции имеют неверную длину. В зависимости от механизма операции это может означать, что данные открытого текста слишком короткие, слишком длинные или не кратны некоторым конкретным размерам блоков. Это возвращаемое значение имеет более высокий приоритет, чем CKR_DATA_INVALID.

За CKA.CKA_VALUE_LEN Я пробовал разные значения без успеха:

CKA_VALUE_LEN
-------------
24 (192)
40 (320)
48 (384)

Я натыкаюсь на открытый ключ, я не уверен, правильно ли я извлекаю открытый ключ. Потому что он имеет длину 664 Bit,

CKA.CKA_VALUE из CKO.CKO_PUBLIC_KEY (664 Bit):

BFEEelKE3TrpE3e3f5nJATxEZrU0UeauhV/dFZXeXz5gqgZjuCtkJaUTainC/Mh357x3FyO7sGoPhzokD34oj5PJs0ItvATIKYtzvwaUkdZlDc0=

Извлечено с pkcs15-tool (864 Bit)

pkcs15-tool.exe --read-public-key 20
-----BEGIN PUBLIC KEY-----
MGowFAYHKoZIzj0CAQYJKyQDAwIIAQEJA1IABHpShN066RN3t3+ZyQE8RGa1NFHm
roVf3RWV3l8+YKoGY7grZCWlE2opwvzId+e8dxcju7BqD4c6JA9+KI+TybNCLbwE
yCmLc78GlJHWZQ3N
-----END PUBLIC KEY-----
  • Почему открытый ключ отличается между pkcs15-tool а также CKO.CKO_PUBLIC_KEY?
  • Какой формат делает CkEcdh1DeriveParams рассчитывать на publicData?
  • Должен ли я извлечь publicData правильный путь? Или как правильно?
  • Должен значение для CKA.CKA_VALUE_LEN равна длине моего ЕС (320 Bit)?

1 ответ

Решение

Это очень хорошее и полное описание проблемы.

CKR_TEMPLATE_INCOMPLETE всегда очень больно иметь дело, потому что почти каждый поставщик библиотек ожидает, что будет предоставлен различный набор атрибутов, и эта ошибка не показывает, какие именно атрибуты отсутствуют.

После быстрого взгляда на исходный код OpenSC я попробую использовать следующий шаблон:

var deriveAttributes = new List<ObjectAttribute>
{
    new ObjectAttribute(CKA.CKA_TOKEN, false),
    new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_SECRET_KEY),
    new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_GENERIC_SECRET),
    new ObjectAttribute(CKA.CKA_SENSITIVE, false),
    new ObjectAttribute(CKA.CKA_EXTRACTABLE, true),
    new ObjectAttribute(CKA.CKA_ENCRYPT, true),
    new ObjectAttribute(CKA.CKA_DECRYPT, true),
    new ObjectAttribute(CKA.CKA_WRAP, true),
    new ObjectAttribute(CKA.CKA_UNWRAP, true),
    new ObjectAttribute(CKA.CKA_VALUE_LEN, ???)
};

Однако я не уверен, каково правильное значение CKA_VALUE_LEN приписывать.

Другие вопросы по тегам