使用SoftHSM 2.2.0(带有SHA256的ECDSA)C#.net从Pkcs11Interop为CKM_ECDSA_SHA256签名PDF

时间:2017-05-16 19:28:42

标签: c# pkcs#11 ecdsa softhsm pkcs11interop

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

我找到了一个CKM枚举, CKM_ECDSA_SHA256 ,我正在创建一个用于调用Session的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是否支持带有SHA256的ECDSA_P256?
  • 如果没有,那么有没有办法启用支持?
  • 如果有支持,请帮我解决这个问题??
  • 看起来它要我通过ECDSA_Param,是否有任何代码片段用于传递ECDSA_Param

2 个答案:

答案 0 :(得分:1)

我认为您需要构建 if(!e_name.getText().toString().isEmpty()||!e_numb.getText().toString().isEmpty()||!e_pangkat.getText().toString().isEmpty() ||!e_tarikh.getText().toString().isEmpty()||!e_masa.getText().toString().isEmpty()) { Toast.makeText(getApplicationContext(), "Cannot empty", Toast.LENGTH_SHORT).show(); return; }else {//another code} 结构并使用ECDSA-Sig-Value变量中的数据填充它。

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签名使用编码   类型:

signedHash
     

ECDSA-Sig-Value在[PKI-ALG]中指定。在CMS中,   ECDSA-Sig-Value是DER编码的并且放置在签名字段中   签名数据。

以下方法使用BouncyCastle库构建DER编码的ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER } 结构:

ECDSA-Sig-Value

答案 1 :(得分:0)

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

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