2

I have a requirement to create a signature which is a URL-Safe-Base-64-encoded SHA256+ECDSA signature of a message. This will be used to consume a remote REST service.

I have been given a HAL browser implementation which connects to them as expected and a test implementation done in SCALA.

    val token = generateToken() // Generates a random numeric token, different for each request
    val lines = line1 + "\n" + line2 + "\n" + line3 + "\n"
    val lineBytes = lines.getBytes()
    try {
        var sig = Signature.getInstance("SHA256withECDSA")
        sig.initSign(privateKey)
        sig.update(lineBytes)
        body.foreach { input => // If there is a body, sign it too
            input.reset()
            var bytes = new Array[Byte](1024)
            while (input.available() > 0) {
                val alloc = input.read(bytes)
                sig.update(bytes, 0, alloc)
            }
        }
        val encoder = new Base64(true)
        val sigString = encoder.encodeAsString(sig.sign()).replace("\r\n", "")
        val headerVal = "authentication.scheme.signed" + " username=" + username + "&token=" + token + "&signature=" + sigString

        request.addHeader("Authorization", headerVal)
    } catch {
        case e : NoSuchAlgorithmException =>
            throw new Error("No support for SHA256withECDSA! Check your Java installation.")
    }

I am trying to generate the same signature using C#.

So far this is what my Signing method looks like

private byte[] SignData(byte[] hashedMessageToSign)
{
    CngKey pkey2 = CngKey.Open(@"C:\OpenSSL-Win64\bin\MyPrivateiKeyInPkcs8Format.pem");

    using (ECDsaCng dsa = new ECDsaCng(pkey2))
    {
        //dsa.HashAlgorithm = CngAlgorithm.ECDsaP256;
        //bob.key = dsa.Key.Export(CngKeyBlobFormat.EccPublicBlob);

        byte[] data = hashedMessageToSign;

        return dsa.SignData(data);
    }
}

I am getting the code building but creating an invalid signature. Here is the calling method

        protected void btnLDiscover_Click(object sender, EventArgs e)
{
    HttpWebRequest request = WebRequest.Create("https://service.provider/path/") as HttpWebRequest;
    request.Method = "GET";
    request.ContentType = "application/bespoke.format+json; version=1";
    //request.Date = new DateTime(2015, 9, 3, 10, 40, 48);
    request.Date = new DateTime(2015, 9, 21, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
    request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
    request.Accept = "application/bespoke.format+json; version=1";
    request.KeepAlive = true;
    request.MaximumAutomaticRedirections = 99;
    //request.PreAuthenticate = true;

    string token = DateTime.Now.Ticks.ToString();
    string messageToSign = "GET /path/\n1\n" + token + "\n";

    string signatureString = Convert.ToBase64String(SignData(Encoding.ASCII.GetBytes(messageToSign)));
    //signatureString = RemoveControlCharacters(signatureString);
    //signatureString = HttpUtility.UrlEncode(signatureString);
    signatureString = signatureString
                        .Replace('+', '-')
                        .Replace('/', '_')
                        .Replace("=", string.Empty);

    request.Headers.Add("Authorization", "authentication.shceme.signed username=someuser&token=" + token + "&signature=" + signatureString);

    HttpWebResponse response = request.GetResponse() as HttpWebResponse;

    Encoding enc = System.Text.Encoding.GetEncoding(65001);
    StreamReader loResponseStream =
    new StreamReader(response.GetResponseStream(), enc);

    string responseString = loResponseStream.ReadToEnd();

    loResponseStream.Close();
    response.Close();

    resultTextBox.Text = responseString;
}

2 Answers 2

3

Just in case someone is having a similar issue... It turned out that Microsoft encodes the above signature in a slightly different way to how java does it. Sorry if I'm using the wrong terminology as I have been out of this space for a little while. Essentially we had to try different signature types will we found one that decoded to the same value in both a c# and java application.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi Zach, I can't recall at the moment but will check my notes from that time.
2

If you don't mind I'm going to skip the part where you perform base 64 encoding in one code fragment but not in the other.

Unlike RSA PKCS#1 v1.5 signatures, ECDSA signatures are not deterministic. In other words, they depend on a random number generator to generate the signatures. The signatures will have a different value after each signing operation. The correctness of the value of these signatures can only be tested by verifying with the public key.

1 Comment

Thanks for you time and contribution Maarten. I have added in the calling method I was using which is where the encoding is done. I have already made an x509 certificate which I sent to the service provider - that's what they are using to validate my signature

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.