3

I am trying to sign Pdf documents using Pkcs11Interop .net library. I need to use ECDSA encryption algorithm with SHA256 hash algorithm. And I am using SoftHSM 2.2.0 for storing private keys.

I found an CKM enum, CKM_ECDSA_SHA256, which I am passing while creating an object of class mechanism for calling the Sign method of Session.

I am getting the response from the "Signdata" method, however, on opening the Pdf files generated after signing give an error "Signature Invalid". Here is the code snippet for Signdata method call. I don't get any error or exception in the code, however, the pdf as I have mentioned are showing signature invalid.

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]; 
}
  • Please tell me if SoftHSM 2.2.0 supports ECDSA_P256 with SHA256 or not ??
  • If not, then is there any way to enable the support ??
  • If it does support, please help me how to fix this ??
  • It looks like it want me to pass ECDSA_Param, does anybody have any code snippet for passing the ECDSA_Param
jariq
  • 11,681
  • 3
  • 33
  • 52
Kumar
  • 63
  • 1
  • 10
  • I see no obvious problem in the code you have posted so my guess is that your problem might be caused by some other code in your solution such as PDF processing, CMS structure building etc. – jariq May 16 '17 at 19:56

2 Answers2

3

I think you need to construct ECDSA-Sig-Value structure and fill it with the data from your signedHash variable.

PKCS#11 v2.20 chapter 12.3.1:

For the purposes of these mechanisms, an ECDSA signature is an octet string of even length which is at most two times nLen octets, where nLen is the length in octets of the base point order n. The signature octets correspond to the concatenation of the ECDSA values r and s, both represented as an octet string of equal length of at most nLen with the most significant byte first. If r and s have different octet length, the shorter of both must be padded with leading zero octets such that both have the same octet length. Loosely spoken, the first half of the signature is r and the second half is s. For signatures created by a token, the resulting signature is always of length 2nLen. For signatures passed to a token for verification, the signature may have a shorter length but must be composed as specified before.

RFC5753 chapter 7.2:

When using ECDSA with SignedData, ECDSA signatures are encoded using the type:

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

ECDSA-Sig-Value is specified in [PKI-ALG]. Within CMS, ECDSA-Sig-Value is DER-encoded and placed within a signature field of SignedData.

Following method uses BouncyCastle library to constructs DER-encoded ECDSA-Sig-Value structure:

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
  • 11,681
  • 3
  • 33
  • 52
  • Thanks a lot @jariq for your reply. However, I am not actually sure how to use ECDSA-Sig-value. If you have any code snippet regarding this, if will be really helpful for me. I am really stuck at this point, I really appreciate any help. Thanks in Advance. – Kumar May 17 '17 at 09:39
  • @Kumar sadly I don't have a ready-to-use-code-sample available currently but I'll try to create one by the end of this week. – jariq May 17 '17 at 10:45
  • Thank you very much @Jariq for this. It is really very helpful. I just need to know is the signedHash variable of my input would the input for this method and its would be considered the final and correct value of signedHash variable ? Do I need to write something like below ................................................................ return (ConstructEcdsaSigValue(signedHash)); – Kumar May 18 '17 at 09:22
  • @Kumar yes you should use `return ConstructEcdsaSigValue(signedHash);` instead of `return signedHash;` but I am not sure whether it will solve your problem completely. Try it and you will see. – jariq May 18 '17 at 10:42
  • Thanks @jariq I tried this however it didn't work. I am still getting the same signature invalid error. Just one thing I want to mention here that I am using SoftHSM 2.2.0. And when I extract the list of all the supported mechanism, it doesn't show CKM_ECDSA_SHA256. It gives invalid mechanism error if I pass it while creating Mechanism object for SoftHSM. So I am passing CKM_ECDSA instead. Do you know whether SoftHSM supports CKM_ECDSA_SHA256 or not ? Do you think it might be the cause of this issue ? – Kumar May 18 '17 at 16:14
  • @Kumar I believe you can replace `CKM_ECDSA_SHA256` by digesting with `CKM_SHA256` first followed by signing with `CKM_ECDSA`. – jariq May 19 '17 at 06:27
  • Thanks @jariq it is working now. Thank you very much for your help. you are a superstar. :) – Kumar May 19 '17 at 13:27
  • @Kumar I'm happy you got it working. You can upvote/accept my answer to increase my SO reputation :) – jariq May 19 '17 at 14:35
  • I have done that Jariq, but I don't have 15 reputation so it doesn't show my response however it does count my vote. It says: "Thanks for the feedback! Votes cast by those with less than 15 reputation are recorded, but do not change the publicly displayed post score." Thanks again for help @jariq :) – Kumar May 19 '17 at 17:42
0

Just thought of sharing the solution which worked for me. In the above mentioned code snippet I added the below things:

   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();
    }
Kumar
  • 63
  • 1
  • 10