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

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

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

Я получаю ответ от метода "Signdata", однако при открытии файлов PDF, созданных после подписи, выдается ошибка "Signature Invalid". Вот фрагмент кода для вызова метода 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

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 для построения DER-кодированного ECDSA-Sig-Value состав:

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();
}

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

   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();
    }
Другие вопросы по тегам