Ive spent quite a bit of time now trying access a api from C# and gotten to the point that my parameters seems to get through but the api is saying signature mismatch. So given the example code below in NodeJs, how should I be creating the signature in C#?
var nonce=Math.round(new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds()).getTime()/1000);
var key='Your api key';
var secret='Your api secret';
var client_id='Your client id';
var path='/ac_balance'; // /ac_balance /my_pending_orders /cancel_order
var signature_data=nonce+key+client_id;
var hmac = crypto.createHmac('sha256', secret); // crypto is a module to encrypt data
hmac.write(signature_data);
hmac.end();
var signature = new Buffer(hmac.read()).toString('base64');
// if path='/ac_balance'
request.post({url:'https://api.somewebsite.com'+path, form: {key:key,nonce:nonce,signature:signature}}, function(err,httpResponse,body){ });
This is the code I use to create the signature with in C#:
var data = nounce + key + clientId;
var signature = CreateToken(data, secret);
...
private string CreateToken(string message, string secret)
{
secret = secret ?? "";
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
return Convert.ToBase64String(hashmessage);
}
}
Related
I have a working pkcs7 cryptography algorithm in Node.js
var forge = require('node-forge');
var fs = require('fs');
var privateKeyAssociatedWithCert = fs.readFileSync("certs\\confpack.key", 'binary');
const payload = fs.readFileSync("certs\\confpack.cert", 'binary');
const certOrCertPem = forge.pki.certificateFromPem(payload);
// create PKCS#7 signed data with authenticatedAttributes
// attributes include: PKCS#9 content-type, message-digest, and signing-time
var p7 = forge.pkcs7.createSignedData();
p7.content = forge.util.createBuffer('ABCD123');
p7.addCertificate(certOrCertPem);
p7.addSigner({
key: privateKeyAssociatedWithCert,
certificate: certOrCertPem,
digestAlgorithm: forge.pki.oids.sha256,
authenticatedAttributes: [
]
});
// PKCS#7 Sign in detached mode.
// Includes the signature and certificate without the signed data.
p7.sign({ detached: true });
console.log(forge.asn1.toDer(p7.toAsn1()).toHex());
My implementation using .Net and Org.BouncyCastle Nuget have a different result.
X509CertificateParser certParser = new X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate rootCert = certParser.ReadCertificate(File.ReadAllBytes("cert\\confpack.cert"));
AsymmetricKeyParameter signatureKey;
using (var reader = File.OpenText("cert\\confpack.key"))
signatureKey = (AsymmetricKeyParameter)new PemReader(reader).ReadObject();
byte[] signedMessage = null;
CmsProcessable cmsData = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("ABCD123"));
var allCerts = new List<Org.BouncyCastle.X509.X509Certificate>();
allCerts.Add(rootCert);
var storeParams = new X509CollectionStoreParameters(allCerts);
var certStore = X509StoreFactory.Create("Certificate/Collection", storeParams);
CmsSignedDataGenerator cmsGenerator = new CmsSignedDataGenerator();
cmsGenerator.AddSigner(signatureKey, rootCert, NistObjectIdentifiers.IdSha256.Id);
cmsGenerator.AddCertificates(certStore);
CmsSignedData cms = cmsGenerator.Generate(cmsData, true);
signedMessage = cms.GetEncoded();
Debug.WriteLine(Hex.ToHexString(signedMessage));
NodeJs Result : "3082055406092a864886f70"...
.Net Result : "308006092a864886f70"...
What am I missing in .net code? I really need to get the same result.
The Node version generates a detached signature and .net an encapsulated one. For detache signature use:
cmsGenerator.Generate(cmsData, false)
I am working on a bluetooth based application where I create a string in Swift and sign it with RSA private key. The string is sent to a WPF app via bluetooth. The wpf app already has its public key. I want to verify the string the in WPF app. But I am not able to do it. I am getting error for algorithm type.
Following is my Swift code:
func signWithPrivateKey(textToEncrypt: String) -> Data? {
guard let privateKey: SecKey = privateKey else {
return nil
}
let algorithm: SecKeyAlgorithm = .rsaSignatureMessagePSSSHA512
guard SecKeyIsAlgorithmSupported(privateKey, .sign, algorithm) else {
return nil
}
var error: Unmanaged<CFError>?
let data = textToEncrypt.data(using: .utf8)!
guard let signature = SecKeyCreateSignature(privateKey,
algorithm,
data as CFData,
&error) as Data? else {
return nil
}
return signature
}
C# code to verify the string:
var deferral = args.GetDeferral();
var request = await args.GetRequestAsync();
var reader = DataReader.FromBuffer(request.Value);
byte[] fileContent = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(fileContent);
string text = Encoding.UTF8.GetString(fileContent, 0, fileContent.Length);
Debug.WriteLine(Convert.ToBase64String(fileContent));
String EncryptedMessage = Convert.ToBase64String(fileContent);
Debug.WriteLine(text);
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes("Guten Morgan");
//byte[] signedBytes = Convert.FromBase64String(text);
try
{
bool success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA-512"), fileContent);
Debug.WriteLine(success);
}catch (CryptographicException e)
{
Debug.WriteLine(e.Message);
}
}
Getting following error:
System.ArgumentNullException: 'Value cannot be null. (Parameter 'hashAlg')'
I have mentioned the algorithm type as rsaSignatureMessagePSSSHA512 in Swift app and trying to verify it with SHA-512 but getting error.
Can someone please help me how to verify the signature in C#.
Edit:
I have modified the WPF code as following and it is not crashing now, but I am still not able to verify it
Credential credential = CredentialManager.ReadCredential("CredentialManagerTests");
String publicKey = credential.Password;
Debug.WriteLine(publicKey);
var deferral = args.GetDeferral();
var request = await args.GetRequestAsync();
var reader = DataReader.FromBuffer(request.Value);
byte[] fileContent = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(fileContent);
string text = Encoding.UTF8.GetString(fileContent, 0, fileContent.Length);
Debug.WriteLine(Convert.ToBase64String(fileContent));
byte[] privateKeyPkcs1DER = ConvertPKCS1PemToDer(publicKey);
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes("Guten Morgan");
rsa.ImportRSAPublicKey(privateKeyPkcs1DER, out _);
try
{
bool success = rsa.VerifyData(bytesToVerify, fileContent, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
Debug.WriteLine(success);
}
catch (CryptographicException e)
{
Debug.WriteLine(e.Message);
}
}
Edit2:
String to sent: "Guten Morgan"
Signature: "G8ePlCyPeivAHPygMHXeCTszTy9EV4y6Gsku9GQJV71t+M7ATFZjxkh9u0DVjR0ei76a4dixzgXKLZLQNy/mxwrvUvolPHE0HUFAErkzfeHcrB8/QlHlEoEdt9vf34L8KNy6yVOHWC2sytgvw009EsKUr3LYb8kSkCeaCVEEEAwAELt3z/YzfnbwR0GXatlNpmaoLOXh0mEep8Xh5AHf6RhbgS0VBFSXSbz3ALbwu5DtlaIv4P1nFOP80w2NBs7HrHOMdR0PKdgiD+suErCYWKINzToKDsdO8XNaKdw3jekYBRXl6GNfAwQeADCq0MDsvptCRVZ//fzU2fXRcaYsRA=="
Algo: SHA512
**Public Key: **
MIIBCgKCAQEA4lS13W+TXZhUXy5yB2NpeulCVb1ZSaReopeKrahjKmUx4NQxVXruEYCY3LpjZcSy8xiudVG3GBIMnPLtaMbc5WAYDj1M2OwnpNHdQ8SKtZ1tdA6iRjfOXGUa1n8FMIMKU5ynTSAiSoh+8gGrY0L6jTsCSdLO5ZU53LQFHSESM8JuBeNZozolb/cKb38ylercVeVpo8egoA8UqHezK23VUJ23faxMmMZDJxVn5pfFedxBTLxwU65KQY8Z4izaFjuPLoGe5JZkXyYNMcrYloDCBG5m9BFiXVkoLEDmGFGfWLMJcqL+D2CszqJ4h712ZR6LYRNtIbo/HG9KR7XCtap3QwIDAQAB.
Private Key:
MIIEowIBAAKCAQEAuAoCUOejL7EBI0P4nfZQkAr8/a37SR/umJB5sUOyy2IyaWuSX1tBOco4XThTkCYj2ubgbf0fGsQ7FRgF6bwbeynhDYS7ptPkvUHF4BaSUsu2KtKkoslvl67M83ESKtOSm0s0CImw60MDlPDr/1SPhkYgSfoQ81CXytrZPkWtB/YekT2ph3PRqyOeMNl0O88Clt2mdR4dZeZWv6WGaaoP1kr8RY9iB6hrg2RdOFXixIOdo7cP3+3w9s4svnIX7RNLIIrUxJHQUYA0oxkiZ9y0peWR5mElywjkg/AFm8RxZsXjHowrqlYGkFKzPTq2/dhwId0Yit95aYVIDE5Dj9n3nQIDAQABAoIBAAFiDd9mxjqrBVuq/JjPS46xjnInlw5XH5dk6o0y+Yp+u+s/5DM0P9q70s2ciUA5kSZpesFI2C1+0QTZD95QTBKSX38XAsP/rqXfcym6cbIOltleiN8yTVTh+udPb7gDrAPfvk3cHwi9ka7SWquqCoQTTdXQe8UgU2uyVlSZ+HFpXFE55ltp4CbnFBH39Sa8pqbpwIwb2+PCFkx7xVUIJ2t1vJd2yBUjEBFJv0SiUy9DWwbXiyoqtZXtVft8Vm7xhimSDN2o6KcnXYEq2eb3UhOKI09tRsYLA3bF66bvVxvcnykuUpLHFOxhBKfD8Vsh+SWHdv6zT52gkF7KDUxDYLECgYEA25iGkCcYx+cfzIdVmIbqa662IzkgUlCdpX37tBNHjyE+72Ehw6odZ0e3iTewm/vYBI6hyCW+T/G6ksl2enjOxBH0DhaQEYLqhSGq63/XOsFsmW7tH4YmInURv4RL4KEIB8HNGLfhllJt92KsrBNzei+SjdtElQhULhw4Mle6PWUCgYEA1ox8DxPTE8HQ5Jw+7D0ut+FyYkysciMSFYpmFX5qlbMlfl0WRL4mVFv/tOBerahsCDdaaOKJKafQVbJIU8G9U0m8PAttcM4OMjexwHzT0EBdf9cNACAXQP/arJEbf8YkPr7/IwI6YGiMFzDKRqcrYMNzl9rZXQEyJGj6u4x/6dkCgYBYIWiv5eD+KXYLoazqoAro2J9kl4KvRodeaadg5/PqL4+Qhs0EN/vA/XldaqpIj9RsT8oCB5PPhdY5Hv2bvWxOKF5oYQnE3WO9tntgNFhuzj4Ffg1Qf4hCf/V1hWTma/pLEq57YyD4MXDMvh9KmCvaN8l7gSqPHV6betva6HZoOQKBgQCwZMxSsR/nrIAMlRF+tUbF08txWkylgoQJxcHshgUnkySOYgY++n8U+JahpZ7x8/juQGRKu4W+A8Tb0Dp68lywL31deJ/AEQnG69duxLJ5E5JL2wlLQxcbT8AABUWwpb2DARFPPTO1s/8JyglkUWjuo4NUJJB1UNhi6xTKQdeg8QKBgESvEWQKqvk3YCpVwCNv5C+1aAVS6Fq/sGmLlVsbQ5W6EENYDg4pq2fgghH9lcRb1BdHjFm25ikADbGWgBOG1fvrD9KsR9RY98x8XM3EQ5EJ2Y2vjQfWBA2da8nr9ETRzY00IjHCzb5AhISWIit7Itaa9H202v1zZCIUY+O/YGGX
Edit3:
My below method was wrong:
private byte[] ConvertPKCS1PemToDer(string pemContents)
{
return Convert.FromBase64String(pemContents
.TrimStart("-----BEGIN PUBLIC KEY-----".ToCharArray())
.TrimEnd("-----END PUBLIC KEY-----".ToCharArray())
.Replace("\r\n", ""));
}
Instead of BEGIN PUBLIC KEY it was BEGIN PRIVATE KEY.
I have fixed it as mentioned above but getting following error on Convert line in above function:
System.FormatException: 'The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.'
Edit4:
I have now created a new project only to test my code and have added following code:
Private key for reference:
"MIIEogIBAAKCAQEArMO9cJHbKYKkozTtqPI9fhecyiibJrvsv5KLLPlQcIi0uo6yfLD4MUFS5UbwaGATnCrLRKr27iQLWwWKxsJdEQMZDMF6EiCQSXEB2AmSEPW4NOh5kbj05cJoDrvSeac3lllsjPlOaVQSrgmiOFoJ1t98utfOJ6FCzam9DZZR7r/OwTZ0754rAp4dqwkamcTaSkW2zQhr++mw4WZZiKGJCPYKKXguGJbRTtr+dcZ4JwrvdYudCguuCVfa+1D6k0NswWEu58DELZ0XPuLu8qFiIA9MI5K/ECHXwozi8UxmDhG9NJuh+uRZZ4s7LVnkNDN8np6vhuwKNL5rP0msUciItwIDAQABAoIBAAgEaSZoyepbD2fVC3TMPB/2df9dpSUbSxrixhto541UBIPtVzQVkoi6ne2uqMu3LpAB3mKfcYvAkJDImqrkQc/tWNHbG4lCXtGIxrZoMtjKFJQ824/UAknQzpCeP7UjpHRYapEb+qW2/JJDU9oDRd2B4S+FZXdVAPJXjPutCy40IziykyhU9Fu0fwCN/ry764EyI4gtN/WgDHwc2C8B7sriW81KtB8vJW10waoot3MRBfXxxhk3wyiNputBg1KDz+O2+iUrO4p1OKdvL9S9u65MN9Klm+i03aexNrjbMHu/lxDUa6buSgfXh5+Na6PLHur/3aCqfZdpJ/OO7AxarqECgYEA4884gGubJmiq8OayGdpnaZa4L/2NEwOEnN2G09ySXuUhIyOzOJmeiw3R0BTOB0ZRzrk/Ozq3/T2z1ScUGr3a7hmgGBm3azNjJCbYZ51/XJPE7B5UhDFejZPGRT8vVnUzjYah4Eipp4hSIe79Ja17vTFTtY7bGGl11a6VaPvHuCECgYEAwiTAs60JqUIFw8qmBzWwIHybbKSS0Pc8AWdMYLVSnqiQKZVDnyypUDv14+VEMbCdW21ShMCrNK0QTWnqkV7ln91jxPNvQbSoLBTXgpxbvoxg57jg6sdtiUuPjdBBc74kV9oW1zX2saLvPOO4fjUw3z1b+6/v1SjRlhIqtQrsRdcCgYBeDgYz7zmFaB17jKPnzKZ5j8LH/ZUrTn6IDWZHPoAoMc22plyud65flvsTQCO4GS5ZfV4/5ARmx/zhelrwl4Y2W9ofWS7DUdoS6P7b+MjGvjPFkNgwI/n31hU8LdQrjAQW4IkhAp8ZDk1quTNHRRMbj6wR/8MxlwkRih0h1SImQQKBgGnuQdMH9ICNDLYzKXo/mhVvyCJ0fcNVU0F0yqDt7uGxGdAGqLn+VXf474bkvtvaAVI0iVT0B7abQ4zp4NpnDCW5V8nMBgW0/BnpWVnj1M9YqztkjhysqiDCwNZhLoVn1060KchNooh0XdM8cZszjLISOdFPwy3ssscOrIzSI+9LAoGAMDcGC5P9gVBCNd8ox5OlozNwAmHmHwpqFJcOxNx4lCE5DkT+6DEscoLMZ4LkNVzMHb0LmyhDnF24eIn/v7FRb6ZF5YzlpIdQC2tvXaJoyPwLPee7XGGr9IRkupbrNzPWVhTUYK9kJvI7SWPF2DBFK8fiC5S96USVp++kOjp/faE="
String publicKey = "MIIBCgKCAQEArMO9cJHbKYKkozTtqPI9fhecyiibJrvsv5KLLPlQcIi0uo6yfLD4MUFS5UbwaGATnCrLRKr27iQLWwWKxsJdEQMZDMF6EiCQSXEB2AmSEPW4NOh5kbj05cJoDrvSeac3lllsjPlOaVQSrgmiOFoJ1t98utfOJ6FCzam9DZZR7r/OwTZ0754rAp4dqwkamcTaSkW2zQhr++mw4WZZiKGJCPYKKXguGJbRTtr+dcZ4JwrvdYudCguuCVfa+1D6k0NswWEu58DELZ0XPuLu8qFiIA9MI5K/ECHXwozi8UxmDhG9NJuh+uRZZ4s7LVnkNDN8np6vhuwKNL5rP0msUciItwIDAQAB";
String signature = "ntGm9Vx38l26ZSEitc1L+G6YZw3Dt4dmXoQQUjSAcKD1d4bFm8QI6CIncTBQ0+rGWUh7fr2DWQBB1OPgoZRoDs23SGvl+KTqUBo6Ym+kIK4KXwBrMt3YXNFpXuXVk36+4+oBPFhcMAwG4fIG5MnMEHqv/F+NNA71a10irB0frMpwpU/FZDGEbCnvOxGe6qIUsYt/dMbf1UT1AjKqQA0EmNqvIsU5DuHcpScb5xuV8aPJg6vPPKafLa8LbBiCA4dyxUcUYSjSaTQ6D9YoZ3Ich/dRBCyIdw9vESBSrOUGvwOmNl+5X+r2UyFN8RgYwW61M3iYdmkwJYYVUbMA69vCOA==";
byte[] signatureBytes = Encoding.UTF8.GetBytes(signature);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportRSAPublicKey(Convert.FromBase64String(publicKey), out _);
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes("Guten Morgen");
try
{
bool success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signatureBytes);
Debug.WriteLine(success);
}
catch (CryptographicException e)
{
Debug.WriteLine(e.Message);
}
}
But success variable is still false.
I am developing the post data request in c# and using AES GCM encryption and RSA encryption. As compared to the C# code with Node.js / Python for encryption, we found that C# AES GCM encryption does not have tag value in the method.
e.g How do I return below data in C# after encryption.
return {
key: aesKey,
cipher_text: encrypted,
tag,
iv
}
In Python and Node.js using JsEncrypt, forge.js.... and implementations are working fine. But, now developing the same client in the C#, do not find such libraries which I can use directly.
However, I found two classes in C# AesGcm and AesManaged for AES Encryption. But I think AesManages is only for AES not GCM encryption. Therefore, I am using AesGcm class in below example, but not sure.
The code executes the following steps:
Request client certificate from DSG.
Extract the public key from the certificate.
Encrypt PII with AES key.
Encrypt AES key with RSA public key.
Send wrapped key and PII payload to DSG tokenization.
class Program
{
static void Main(string[] args)
{
string _ApiKey = "--- YOUR API KEY ----";
string _SecretKey = " ---- YOUR SECRET KEY --------";
string jsonPlainText = "<JSON TExT>";
#region Certificate
string certificate = GetCertificate();
Certificate rawSigningCert = JsonConvert.DescrializeObject<Certificate>(certificate);
var certBytes = Encoding.UTF8.GetBytes(rawSigningCert.crt);
// X509Certificate2
var x509Certificate2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certBytes);
byte[] publicKey = x509Certificate2.GetPublicKey();
#endregion
#region AES GCM Encryption
byte[] aesGcmKey = new byte[16];
RandomNumberGenerator.Fill(aesGcmKey);
byte[] nonce = new byte[12];
RandomNumberGenerator.Fill(nonce);
byte[] tag = new byte[16];
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(jsonPlainText);
byte[] cipherText = new byte[dataToEncrypt.Length];
using (AesGcm aesGcm = new AesGcm(aesGcmKey))
{
aesGcm.Encrypt(nonce, dataToEncrypt, cipherText, tag);
}
string encryptedCipher = Convert.ToBase64String(cipherText);
var keyString = Convert.ToBase64String(aesGcmKey);
var iVString = Convert.ToBase64String(nonce);
var tagString = Convert.ToBase64String(tag);
string aesGcmEncryptionString = string.Format("{0}:{1}:{2}", keyString, iVString, tagString);
#endregion
#region RSA Encryption
var rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = rsa.ExportParameters(false);
RSAKeyInfo.Modulus = publicKey;
rsa.ImportParameters(RSAKeyInfo);
byte[] encryptedSymmetricKey = RSAEncrypt(Encoding.UTF8.GetBytes(aesGcmEncryptionString), RSAKeyInfo, false);
var enc_key = Convert.ToBase64String(encryptedSymmetricKey);
var requestBodyObject = new { message = encryptedCipher, enckey = enc_key };
string jsonRequestBody = JsonConvert.SerializeObject(requestBodyObject);
#endregion
#region Calculating Message Signature
Random random = new Random();
int ClientRequestId = random.Next(0, 99999999);
var time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var rawSignature = _ApiKey + ClientRequestId + time + jsonRequestBody;
HMACSHA256 hashObject = new HMACSHA256(Encoding.UTF8.GetBytes(_SecretKey));
var signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(rawSignature));
var computedHmac = Convert.ToBase64String(signature);
#endregion
#region Make Request
try
{
var client = new RestClient("< API URL>");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("contentType", "application/json");
request.AddHeader("clientRequestId", ClientRequestId.ToString());
request.AddHeader("apiKey", _ApiKey);
request.AddHeader("timestamp", time.Tostring);
request.AddHeader("messageSignature", computedHmac);
request.AddJsonBody(jsonRequestBody);
request.RequestFormat = DataFormat.Json;
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
#endregion
}
static string GetCertificate()
{
string cer = string.Empty;
// Code to get certicate .crt from the service.
//"<GET CERTICATE HERE>";
return cer;
}
static public byte[] RSAEncrpt(byte[] data, RSAParameters keyInfo, bool doOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(keyInfo);
encryptedData = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
}
return encryptedData;
}
}
class Certificate
{
public string crt { get; set; }
}
How I can trace each steps as service is responding with incorrect message signature. Any way to verify and trace the above code steps?
Help!
Thanks in advance
From what I understood, if I want to sign the SAML request, the signature should be constructed of:
SAMLRequest=VALUE&RelayState=VALUE&SigAlg=VALUE
and from that I should create the signature, this is the piece of code I have used in order to create the signature:
var MSCert = new X509Certificate2(ConfigurationManager.AppSettings["KeyFilepath"], ConfigurationManager.AppSettings["KeyFilePassword"]);
RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)MSCert.PrivateKey;
CspParameters cspParam = new CspParameters();
cspParam.KeyContainerName = rsacsp.CspKeyContainerInfo.KeyContainerName;
cspParam.KeyNumber = rsacsp.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2;
RSACryptoServiceProvider aescsp = new RSACryptoServiceProvider(cspParam);
aescsp.PersistKeyInCsp = false;
byte[] signed = aescsp.SignData(requestBytes, "SHA256");
// For testing purposes - testing the signature
bool isValid = aescsp.VerifyData(requestBytes, "SHA256", signed);
string signatureValue = null;
// Deflate the signature string
using (var output = new MemoryStream())
{
using (var zip = new DeflateStream(output, CompressionMode.Compress))
{
zip.Write(signed, 0, signed.Length);
}
var base64 = Convert.ToBase64String(output.ToArray());
var urlEncode = HttpUtility.UrlEncode(base64);
signatureValue = urlEncode;
}
And still, I see that the validation fails, can some one help me to see why?
I am trying to make use of a REST API using C#.
The API creator has provided below pseudo code for hmac creation.
var key1 = sha1(body);
var key2 = key1 . SECRET_KEY;
var key3 = sha1(key2);
var signature = base64_encode(key3);
In the above pseudo code , body is html request body string , and SECRET_KEY
is the secret key provided by REST API provider.
As per my knowledge , I need to use System.Security.Cryptography.HMACSHA1 class
to implement this.
But am not able to completely implement above logic in C#.
Any suggestions ?
Direct mapping of above code to C# would be something like:
static string ComputeSignature(byte[] body, byte[] secret) {
using (var sha1 = SHA1.Create())
{
var key1 = sha1.ComputeHash(body);
var key2 = key1.Concat(secret).ToArray();
var key3 = sha1.ComputeHash(key2);
return Convert.ToBase64String(key3);
}
}
If you have request body as a string, convert it to byte array using proper encoding, for example:
var body = Encoding.UTF8.GetBytes(bodyAsString);
If you have your secret as string - that depends on how api developer expects it to be converted to byte array. Most likely it's already HEX or base64-encoded string.
The issue to make it work in c# is that you need to take the hex format into consideration and then in some cases for it to work the final result should be lower case (example if you're using this for a quickblox api or something)
private string GetHashedMessage(String _secret)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(_secret);
String _message= "Your message that needs to be hashed";
HMACSHA1 hmacsha1 = new HMACSHA1(keyByte);
byte[] messageBytes = encoding.GetBytes(_message);
byte[] hashmessage = hmacsha1.ComputeHash(messageBytes);
return ByteToString(hashmessage).ToLower();
}
public string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
reference: http://billatnapier.com/security01.aspx