【问题标题】:Signing PDF from Pkcs11Interop for CKM_ECDSA_SHA256 using SoftHSM 2.2.0 (ECDSA with SHA256) C# .net使用 SoftHSM 2.2.0 (ECDSA with SHA256) C# .net 从 Pkcs11Interop 为 CKM_ECDSA_SHA256 签署 PDF
【发布时间】:2017-10-16 00:58:20
【问题描述】:

我正在尝试使用 Pkcs11Interop .net 库签署 Pdf 文档。 我需要使用 ECDSA 加密 算法和 SHA256 哈希算法。 我正在使用 SoftHSM 2.2.0 来存储私钥。

我发现了一个 CKM 枚举,CKM_ECDSA_SHA256,我在创建用于调用 Session 的 Sign 方法的类机制对象时传递了它。

我从“Signdata”方法得到响应,但是,在打开签名后生成的 Pdf 文件时,会出现错误“Signature Invalid”。 这是 Signdata 方法调用的代码 sn-p。 我在代码中没有收到任何错误或异常,但是,我提到的 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 是否支持带有 SHA256 的 ECDSA_P256?
  • 如果没有,有没有办法启用支持?
  • 如果支持,请帮我解决这个问题??
  • 看起来它想让我通过 ECDSA_Param,有没有人有任何代码 sn-p 用于传递 ECDSA_Param

【问题讨论】:

  • 我在您发布的代码中没有发现明显问题,所以我猜测您的问题可能是由您的解决方案中的其他代码引起的,例如 PDF 处理、CMS 结构构建等。

标签: c# pkcs#11 ecdsa softhsm pkcs11interop


【解决方案1】:

我认为您需要构建 ECDSA-Sig-Value 结构并用您的 signedHash 变量中的数据填充它。

PKCS#11 v2.20 第 12.3.1 章:

就这些机制而言,ECDSA 签名是一个八位字节 偶数长度的字符串,最多为 nLen 八位字节的两倍,其中 nLen 是基点阶 n 的八位字节长度。签名 八位字节对应于 ECDSA 值 r 和 s 的串联, 都表示为最多 nLen 的相等长度的八位字节字符串 首先是最高有效字节。如果 r 和 s 有不同的八位字节 长度,两者中较短的一个必须用前导零八位字节填充 使得两者具有相同的八位字节长度。言简意赅,第一个 签名的一半是 r,后半部分是 s。用于签名 由令牌创建,生成的签名总是长度为 2nLen。 对于传递给令牌进行验证的签名,签名可以 长度较短,但必须按照之前指定的方式组合。

RFC5753第7.2章:

当使用带有 SignedData 的 ECDSA 时,ECDSA 签名使用 类型:

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

ECDSA-Sig-Value 在 [PKI-ALG] 中指定。在 CMS 中, ECDSA-Sig-Value 采用 DER 编码并放置在 签名数据。

以下方法使用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();
}

【讨论】:

  • 非常感谢@jariq 的回复。但是,我实际上不确定如何使用 ECDSA-Sig-value。如果您对此有任何代码sn-p,如果对我真的很有帮助。我真的被困在这一点上,我非常感谢任何帮助。提前致谢。
  • @Kumar 遗憾的是,我目前没有现成可用的代码示例,但我会尝试在本周末创建一个。
  • 非常感谢@Jariq。这真的很有帮助。我只需要知道我的输入的 signedHash 变量是否是该方法的输入,并且它会被认为是 signedHash 变量的最终和正确值?我需要写下类似的东西...................................................... ...................... 返回 (ConstructEcdsaSigValue(signedHash));
  • @Kumar 是的,您应该使用return ConstructEcdsaSigValue(signedHash); 而不是return signedHash;,但我不确定它是否能完全解决您的问题。试试看,你会看到的。
  • 谢谢@jariq 我试过这个但是没有用。我仍然收到相同的签名无效错误。我只想在这里提到一件事,我正在使用 SoftHSM 2.2.0。当我提取所有支持机制的列表时,它没有显示 CKM_ECDSA_SHA256。如果我在为 SoftHSM 创建 Mechanism 对象时传递它,它会给出无效的机制错误。所以我改为通过 CKM_ECDSA 。你知道 SoftHSM 是否支持 CKM_ECDSA_SHA256 吗?您认为这可能是导致此问题的原因吗?
【解决方案2】:

只是想分享对我有用的解决方案。在上面提到的代码 sn-p 中,我添加了以下内容:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-12
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    相关资源
    最近更新 更多