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.