Подписание PDF-файла из Pkcs11Interop для CKM_ECDSA_SHA256 с использованием SoftHSM 2.2.0 (ECDSA с SHA256) C # .net

Я пытаюсь подписать документы PDF с помощью библиотеки Pkcs11Interop .net. Мне нужно использовать алгоритм шифрования ECDSA с алгоритмом хеширования SHA256. И я использую SoftHSM 2.2.0 для хранения закрытых ключей.

Я нашел перечисление CKM, CKM_ECDSA_SHA256, которое я передаю при создании объекта механизма класса для вызова метода Sign сеанса.

Я получаю ответ от метода «Signdata», однако при открытии файлов Pdf, созданных после подписи, выдает ошибку «Подпись недействительна». Вот фрагмент кода для вызова метода Signdata. Я не получаю никаких ошибок или исключений в коде, однако PDF-файл, как я уже упоминал, показывает недействительную подпись.

private Pkcs11 _pkcs11;
private Slot _slot;
private Session _session;

try
{
   _pkcs11 = new Pkcs11(hsmCryptoApi, true);
}
catch (Pkcs11Exception ex)
{
   if (ex.RV == CKR.CKR_CANT_LOCK)
      _pkcs11 = new Pkcs11(hsmCryptoApi, false);
   else
       throw ex;
}

_slot = FindSlot(_pkcs11, _certificateInformation.TokenLabel);
_session = _slot.OpenSession(true);

using (Mechanism mechanism = new Mechanism(CKM.CKM_ECDSA_SHA256))
{
  _session.Login(CKU.CKU_USER, passowrd);
  byte[] signedHash = _session.Sign(mechanism, GetPrivateKeyHandle(), message);
  _session.Logout();
  return signedHash;
}

private ObjectHandle GetPrivateKeyHandle()
{
  string keyLabel = _certificateInformation.KeyLabel;
  List<ObjectAttribute> searchTemplate = new List<ObjectAttribute>();
  searchTemplate.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY));
  searchTemplate.Add(new ObjectAttribute(CKA.CKA_LABEL, keyLabel));
  List<ObjectHandle> foundObjects = _session.FindAllObjects(searchTemplate);
  return foundObjects[0]; 
}
  • Скажите, пожалуйста, поддерживает ли SoftHSM 2.2.0 ECDSA_P256 с SHA256 или нет ??
  • Если нет, то есть ли возможность включить поддержку ??
  • Если он поддерживает, пожалуйста, помогите мне, как это исправить ??
  • Похоже, он хочет, чтобы я передал ECDSA_Param, есть ли у кого-нибудь фрагмент кода для передачи ECDSA_Param

person Kumar    schedule 16.05.2017    source источник
comment
Я не вижу очевидной проблемы в опубликованном вами коде, поэтому я предполагаю, что ваша проблема может быть вызвана каким-то другим кодом в вашем решении, таким как обработка PDF, построение структуры CMS и т. Д.   -  person jariq    schedule 16.05.2017


Ответы (2)


Я думаю, вам нужно построить ECDSA-Sig-Value структуру и заполнить ее данными из вашей signedHash переменной.

PKCS # 11 v2.20, глава 12.3.1:

Для целей этих механизмов подпись ECDSA представляет собой строку октетов четной длины, которая не более двух октетов nLen, где nLen - длина в октетах порядка n базовой точки. Октеты сигнатуры соответствуют конкатенации значений r и s ECDSA, представленных в виде строки октетов равной длины, не более nLen, с первым старшим байтом. Если r и s имеют разную длину октета, более короткий из них должен быть дополнен начальными нулевыми октетами, чтобы оба имели одинаковую длину октета. Говоря свободно, первая половина подписи - это r, а вторая половина - s. Для подписей, созданных токеном, результирующая подпись всегда имеет длину 2nLen. Для подписей, переданных токену для проверки, подпись может иметь меньшую длину, но должна быть составлена, как указано ранее.

RFC5753, глава 7.2:

При использовании ECDSA с SignedData подписи ECDSA кодируются с использованием типа:

ECDSA-Sig-Value ::= SEQUENCE {
    r INTEGER,
    s INTEGER }

ECDSA-Sig-Value указывается в [PKI-ALG]. В CMS ECDSA-Sig-Value кодируется DER и помещается в поле подписи SignedData.

Следующий метод использует библиотеку BouncyCastle для построения структуры ECDSA-Sig-Value в кодировке DER:

public static byte[] ConstructEcdsaSigValue(byte[] rs)
{
    if (rs == null)
        throw new ArgumentNullException(nameof(rs));

    if (rs.Length < 2 || rs.Length % 2 != 0)
        throw new ArgumentException("Invalid length", nameof(rs));

    int halfLen = rs.Length / 2;

    byte[] half1 = new byte[halfLen];
    Array.Copy(rs, 0, half1, 0, halfLen);
    var r = new Org.BouncyCastle.Math.BigInteger(1, half1);

    byte[] half2 = new byte[halfLen];
    Array.Copy(rs, halfLen, half2, 0, halfLen);
    var s = new Org.BouncyCastle.Math.BigInteger(1, half2);

    var derSequence = new Org.BouncyCastle.Asn1.DerSequence(
        new Org.BouncyCastle.Asn1.DerInteger(r),
        new Org.BouncyCastle.Asn1.DerInteger(s));

    return derSequence.GetDerEncoded();
}
person jariq    schedule 17.05.2017
comment
Большое спасибо @jariq за ваш ответ. Однако я не совсем уверен, как использовать ECDSA-Sig-value. Если у вас есть фрагмент кода по этому поводу, это будет мне действительно полезно. Я действительно застрял на этом этапе, я очень ценю любую помощь. Заранее спасибо. - person Kumar; 17.05.2017
comment
@Kumar, к сожалению, в настоящее время у меня нет готового к использованию образца кода, но я постараюсь создать его к концу этой недели. - person jariq; 17.05.2017
comment
Большое спасибо @Jariq за это. Это действительно очень полезно. Мне просто нужно знать, будет ли переменная signedHash моего ввода входными данными для этого метода и будет ли она считаться окончательным и правильным значением переменной signedHash? Надо ли писать что-то вроде ниже .......................................... ...................... return (ConstructEcdsaSigValue (signedHash)); - person Kumar; 18.05.2017
comment
@Kumar да, вы должны использовать return ConstructEcdsaSigValue(signedHash); вместо return signedHash;, но я не уверен, полностью ли это решит вашу проблему. Попробуйте, и вы увидите. - person jariq; 18.05.2017
comment
Спасибо @jariq. Я пробовал, но это не сработало. Я все еще получаю ту же ошибку недействительной подписи. Я хочу упомянуть только одну вещь: я использую SoftHSM 2.2.0. И когда я извлекаю список всех поддерживаемых механизмов, он не показывает CKM_ECDSA_SHA256. Это дает неверную ошибку механизма, если я передаю ее при создании объекта Механизм для SoftHSM. Поэтому вместо этого я передаю CKM_ECDSA. Вы знаете, поддерживает ли SoftHSM CKM_ECDSA_SHA256 или нет? Как вы думаете, это может быть причиной проблемы? - person Kumar; 18.05.2017
comment
@Kumar Я считаю, что вы можете заменить CKM_ECDSA_SHA256, сначала переварив CKM_SHA256, а затем подписав CKM_ECDSA. - person jariq; 19.05.2017
comment
Спасибо @jariq, теперь он работает. Большое спасибо за Вашу помощь. ты суперзвезда. :) - person Kumar; 19.05.2017
comment
@Kumar Я рад, что у тебя все получилось. Вы можете проголосовать / принять мой ответ, чтобы повысить мою репутацию SO :) - person jariq; 19.05.2017
comment
Я сделал это, Джарик, но у меня нет 15 репутации, поэтому он не показывает мой ответ, но учитывает мой голос. Там написано: Спасибо за отзыв! Голоса, отданные теми, у кого репутация менее 15, записываются, но не изменяют общедоступный рейтинг публикации. Еще раз спасибо за помощь @jariq :) - person Kumar; 19.05.2017

Просто подумал о том, чтобы поделиться решением, которое сработало для меня. В вышеупомянутом фрагменте кода я добавил следующие вещи:

   using (Mechanism mechanism = new Mechanism(CKM.CKM_ECDSA))
        {
          _session.Login(CKU.CKU_USER, passowrd);
          byte[] signedHash = _session.Sign(mechanism, GetPrivateKeyHandle(), GetMessageDigest(message));
          _session.Logout();
          return ConstructEcdsaSigValue(signedHash);
        }

    private byte[] GetMessageDigest(byte[] message)
    {
       using (Mechanism mechanism = new Mechanism(CKM_SHA256))
       {
         return _session.Digest(mechanism, message);
        }
    }

    public static byte[] ConstructEcdsaSigValue(byte[] rs)
    {
        if (rs == null)
            throw new ArgumentNullException(nameof(rs));

        if (rs.Length < 2 || rs.Length % 2 != 0)
            throw new ArgumentException("Invalid length", nameof(rs));

        int halfLen = rs.Length / 2;

        byte[] half1 = new byte[halfLen];
        Array.Copy(rs, 0, half1, 0, halfLen);
        var r = new Org.BouncyCastle.Math.BigInteger(1, half1);

        byte[] half2 = new byte[halfLen];
        Array.Copy(rs, halfLen, half2, 0, halfLen);
        var s = new Org.BouncyCastle.Math.BigInteger(1, half2);

        var derSequence = new Org.BouncyCastle.Asn1.DerSequence(
            new Org.BouncyCastle.Asn1.DerInteger(r),
            new Org.BouncyCastle.Asn1.DerInteger(s));

        return derSequence.GetDerEncoded();
    }
person Kumar    schedule 19.05.2017