Skip to content
Advertisement

Why does signing algorithm in C# give different result than the one in Javascript

This is the algorithm for signing the data in C# using a private key from a certificate that is used from both me and the client in order to define an unique key to identify the user:

X509Certificate2 keyStore = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "Certifikatat\" + certPath, certPass, X509KeyStorageFlags.Exportable);
RSA privateKey = keyStore.GetRSAPrivateKey();
byte[] iicSignature = privateKey.SignData(Encoding.ASCII.GetBytes("K31418036C|2022-5-16 13:30:41|406|st271ir481|al492py609|zz463gy579|340"), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
byte[] iic = ((HashAlgorithm)CryptoConfig,CreateFromName("MD5")).ComputeHash(iicSignature);

I then pass the private key to my Javascript using Bouncy Castle:

X509Certificate2 keyStore = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "Certifikatat\" + certPath, certPass, X509KeyStorageFlags.Exportable);
RSA privateKey = keyStore.GetRSAPrivateKey();
var eky = DotNetUtilities.GetRsaKeyPair(privateKey);
Pkcs8Generator pkcs8Gen = new Pkcs8Generator(eky.Private);
Org.BouncyCastle.Utilities.IO.Pem.PemObject pkcs8 = pkcs8Gen.Generate();
PemWriter pemWriter = new PemWriter(new StringWriter());
pemWriter.WriteObject(pkcs8);
pemWriter.Writer.Flush();
return pemWriter.Writer.ToString();

This one is the algorithm used in Javascript:

window.crypto.subtle.importKey(
    "pkcs8",
    pemToArrayBuffer(pkcs8Pem), {
      name: "RSASSA-PKCS1-v1_5",
      hash: {
        name: "SHA-256"
      },
    },
    false, ["sign"]
  )
  .then(function(privateKey) {
    console.log(privateKey);
    // Sign: RSA with SHA256 and PKCS#1 v1.5 padding
    window.crypto.subtle.sign({
          name: "RSASSA-PKCS1-v1_5",
        },
        privateKey,
        new TextEncoder().encode("K31418036C|2022-5-16 13:30:41|406|st271ir481|al492py609|zz463gy579|340")
      )
      .then(function(signature) {
      var iic = md5(signature);
        console.log(ab2b64(signature));
      })
      .catch(function(err) {
        console.error(err);
      });
  })
  .catch(function(err) {
    console.error(err);
  });

function ab2b64(arrayBuffer) {
  return window.btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}

function removeLines(str) {
  str = str.replace("r", "");
  return str.replace("n", "");
}

function base64ToArrayBuffer(b64) {
  var byteString = atob(b64);
  var byteArray = new Uint8Array(byteString.length);
  for (var i = 0; i < byteString.length; i++) {
    byteArray[i] = byteString.charCodeAt(i);
  }
  return byteArray;
}

function pemToArrayBuffer(pem) {
  var b64Lines = removeLines(pem);
  var b64Prefix = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
  var b64Final = b64Prefix.replace('-----END PRIVATE KEY-----', '');
  return base64ToArrayBuffer(b64Final);
}

The signatures returned are different for some reason. I need them to be the same or else it’s all pointless because the client won’t be authenticated.

The results are as follow:

C#:
57CF663ACBEDE6305309682BA7261412

Javascript:
c099d176dcd95c59d748d6066dcd462e

Advertisement

Answer

I had to convert my signature to base64 and then encode it with atob() after that i needed this md5 library to hash the data and then use .toUpperCase() to reproduce the correct result.

The complete code looks like this:

md5(atob(ab2b64(signature))).toUpperCase();

Now i get the same result from both C# and JS.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement