Get SHA1 sign of string with DSA private key from PEM file

2.8k views Asked by At

I have a PEM file which includes my DSA private Key and i need to sign a string with this private key to send it to my partner API. (code attached)

For some reason i'm always getting Signature Invalid form my partner API. which means the sign is not in a good format.

My partner offers me to use bouncycastle for c# but i couldn't find any examples of how to sign the string with DSA from external PEM file.

Can i get any examples here?

Thanks.

I've tried to sign the string with a method i wrote:

 public string ComputeSignature(string method, string path,string client_id, string timestamp, string username)
        {


        var data = String.Concat(method, path, client_id, timestamp);
        if (!string.IsNullOrEmpty(username))
        {
            data = data + username;
        }


        string sig;
        var encoding = new ASCIIEncoding();
        byte[] dataInBytes = encoding.GetBytes(data);
        using (System.Security.Cryptography.HMAC Hmac = new HMACSHA1())
        {
            Hmac.Key = encoding.GetBytes(privateKeyClean);

            byte[] hash = Hmac.ComputeHash(dataInBytes, 0, dataInBytes.Length);
            sig = Convert.ToBase64String(hash, 0, hash.Length);
        }
        return sig;


    }
2

There are 2 answers

0
Eliran Eliassy On BEST ANSWER

I solved this issue using bouncycastle:

    private static string ComputeSignature()
    {
        AsymmetricCipherKeyPair asymmetricCipherKeyPair;
        using (TextReader textReader = new StreamReader("myprivatekey.pem"))
        {
            asymmetricCipherKeyPair = (AsymmetricCipherKeyPair)new PemReader(textReader).ReadObject();
        }
        DsaPrivateKeyParameters parameters = (DsaPrivateKeyParameters)asymmetricCipherKeyPair.Private;
        string text = TEXTTOENC;
        if (!string.IsNullOrEmpty(userName))
        {
            text += userName;
        }
        Console.Out.WriteLine("Data: {0}", text);
        byte[] bytes = Encoding.ASCII.GetBytes(text);
        DsaDigestSigner dsaDigestSigner = new DsaDigestSigner(new DsaSigner(), new Sha1Digest());
        dsaDigestSigner.Init(true, parameters);
        dsaDigestSigner.BlockUpdate(bytes, 0, bytes.Length);
        byte[] inArray = dsaDigestSigner.GenerateSignature();
        string text2 = Convert.ToBase64String(inArray);
        Console.WriteLine("Signature: {0}", text2);
        return text2;
    }
1
bartonjs On

The reason that the DSA signature fails here is you aren't doing DSA at all. You're doing HMAC-SHA-1 using the contents of a key file as the HMAC key.

  1. Read the DSA key parameters from the file.

.NET has no intrinsic support for reading PEM key files. But your title mentions BouncyCastle, so you can probably adapt the accepted answer to How to read a PEM RSA private key from .NET to DSA (using the PemReader).

Alternatively, you could use OpenSSL to make a self-signed certificate based off of this key, and put the cert+key into a PFX. .NET can load PFXes just fine.

  1. Populate a DSA object.

If you go the PFX route, cert.GetDSAPrivateKey() will do the right thing (.NET 4.6.2). On versions older than 4.6.2 you can use cert.PrivateKey as DSA, which will only work for FIPS 186-2-compatible DSA (DSA was upgraded in FIPS 186-3).

If you're using BouncyCastle to read the PEM key parameters you can then stick with BouncyCastle or do something like using (DSA dsa = DSA.Create()) { dsa.ImportParameters(parametersFromTheFile); /* other stuff here */ }. (DSA.Create() will give an object limited to FIPS-186-2 on .NET Framework, but it can do FIPS-186-3 on .NET Core).

  1. Sign the data

FIPS-186-2 only allows SHA-1 as the hash algorithm, FIPS-186-3 allows the SHA-2 family (SHA256, SHA384, SHA512). We'll assume that you're using FIPS-186-2/SHA-1 (if not, the substitutions required are hopefully obvious).

BouncyCastle: However they compute signatures. (Sorry, I'm not familiar with their APIs)

.NET 4.6.1 or older:

using (SHA1 hash = SHA1.Create())
{
    signature = dsa.CreateSignature(hash.ComputeHash(dataInBytes));
}

.NET 4.6.2 or newer:

signature = dsa.SignData(dataInBytes, HashAlgorithmName.SHA1);

Soapbox

Then, once that's all said and done (or, perhaps, before): Ask yourself "why am I using DSA?". In FIPS 186-2 (and prior) DSA is limited to a 1024-bit key size and the SHA-1 hash. NIST SP800-57 classifies SHA-1 and DSA-1024 both as having 80 bits of security (Tables 2 and 3). NIST classified 80 bits of security as "Deprecated" for 2011-2013 and "Disallowed" for 2014 and beyond (Table 4). Modern usage of DSA (for entities subject to NIST recommendations) requires FIPS-186-3 support.

ECDSA gets ~keysize/2 bits of security; so the lowest (commonly supported) ECDSA keysize (keys based on NIST P-256/secp256r1) gets 128 bits of security, which NIST rates as good for 2031+.

RSA is also a better choice than DSA, because it has much better breadth of support for signatures still considered secure by NIST.

Though, if you're conforming to a protocol I guess there aren't a lot of options.