Dear Community,
fast forward. After an initial request I receive an random string from a server.
The client(ME) needs to generate an Digital-Signature via C# and send it back to the Node.js API (Express;TypeScript).
Then the server validats the signature and returns true, if the signature is successfully verified or false if not.
To sign the string I use C# default RSA object and SHA256 for Hashing.
The Server knows my public key and user identifier to make sure the digital-signature belongs to me.
Use-Case Tl;Dr:
Client(C#):
Receive a string from the server. Generate an digital-signature(RSA;SHA256) of this string and send it back via API-Request towards the Server.
Server(NodeJS;Express;TypeScript):
Receive the request with the digital-signature of the string inside and validats it.
Send an answer with true if the signature is successfully verified or false if not.
**The Problem:**
The result turns nerver out as true.
Generating an Signature of the string and Verify it localy, both systems (client and server) will return true.
Means if the Client generate a signature of the string and verifys it the process results localy in true.
If the server uses the same Key-Pair as the client and generate a signature of the same string the process will result in true.
BUT using the same Key-Pair both systems generate different signatures for the same string!
If I use the digital-signature of the string generated by the server and send it with the client, the server returns TRUE.
Code is a bit different. Names has changed.
Client (C#)
Request Methode
<!-- language: c# -->
private async Task<string> APIRequestSignature(RSA my_rsa, string randomString)
{
byte[] randomBytes = Encoding.UTF8.GetBytes(randomString);
byte[] signature = Crypto.GetDigitalSignatur256(randomBytes, my_rsa);
MeRequest meRequest = new(identifier, Convert.ToBase64String(signature));
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = APIUri.ValidME,
Content = new StringContent(meRequest.ToString(), Encoding.UTF8, MediaTypeNames.Application.Json),
};
var response = await client.SendAsync(request).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
Console.WriteLine(responseBody);
}
internal class MeRequest: Request
{
private string sign;
public MeRequest(string identifier, string sign) : base(identifier, "")
{
this.sign = sign;
}
public string signature
{
get
{
return this.sign;
}
}
}
Crypto Class:
<!-- language: c# -->
public static byte[] GetDigitalSignatur256(byte[] str, RSA rsa)
{
byte[] hash = Hash256(str);
RSAPKCS1SignatureFormatter rsaFormatter = new(rsa);
rsaFormatter.SetHashAlgorithm("SHA256");
return rsaFormatter.CreateSignature(hash);
}
public static byte[] Hash256(byte[] arr) =>SHA256.Create().ComputeHash(arr);
Messi JavaScript Server Code: (Node.JS;Express;TypeScript)
<!-- language: lang-js -->
import \{Encryption\} from "../class/Encryption";
var randomString = "TESTTESTTESTTEST";
<!-- language: lang-js -->
//If User Exists need Ident and PKey /Get an random encrypted string
UserRoutes.get('/UserExist', function(req, res) {
//Create Hash from randomString
var hash = crypto.createHash("SHA256");
hash.update(randomString, "base64");
var randomStringHash = hash.digest("base64");
//Read Private Key from Client for Test Signature
var clientPrivateKey = fs.readFileSync(config.server.clientKeyPath + "identlarspk.txt", 'utf8');
//Create Sign with Hash from RandomString
var sign = crypto.createSign("SHA256");
sign.update(randomStringHash, "base64");
sign.end();
var signature = sign.sign({
key: clientPrivateKey,
padding: crypto.constants.RSA_PKCS1_PADDING
});
console.log("Signature Created By Server: " + signature);
res.json({
randomString: randomString
})
});
<!-- language: lang-js -->
//Validate User by Ident and Manipulated random encrypted String By Signature
UserRoutes.get('/UserSigning', async function(req, res) {
var encryption = new Encryption();
//Formating Public key
var userPKeyDB = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqdKBCUssRLefK9EzzRKxm+ftQ26PLmI5utmmGY6LgwEnKuIrJw/cWA5Fn+2ebJNAgdH9uFBAh8CEtHHjnfMB0SWCl6Qv8R62x8wJs8xNmoTIVLENe5lvHGi2FLVmzLg1LInfwqfsLBLmCM6re5WtQPX4BiXjfDaeYxYCLg3plfaQyYe9/PCzpswC4U4R3yMKzW3ptj4D+L0BGwp4EKFkh2qGJ1Bnm2PkDwott7CT8a8ef36vS7x1fb0XUuw4b5c87ilQZ+mG85aWDK45R7Tl2ZMebZsAe/kpLIRDsG3nnxdzpBelePFCGWSdCCwvFjygOcLyKXQtqJyp3tHvENGTjQIDAQAB";
var formatedEncryptionKey = await encryption.FormatePKey(userPKeyDB);
//Create Hash from RandomString
var hash = crypto.createHash("SHA256");
hash.update(randomString, "base64");
var randomStringHash = hash.digest("base64");
var bufferRandomStringHash = Buffer.from(randomStringHash, "base64");
var bufferClientSignature = Buffer.from(req.body.signature, "base64");
var verify = crypto.createVerify("SHA256");
verify.update(randomStringHash);
verify.end();
var isValidated = verify.verify(formatedEncryptionKey, bufferClientSignature);
res.json({
token: isValidated
})
});
<!-- language: lang-js -->
//Create start and end points for the Public Key in Encryption Class
public async FormatePKey(pKey: string): Promise<string> {
var bufferStartKey = Buffer.from("-----BEGIN PUBLIC KEY-----\n", "utf8");
var bufferStartKeyUTF8 = bufferStartKey.toString("utf8");
var bufferPubKey = Buffer.from(pKey, "base64");
var pubKeyBase64 = bufferPubKey.toString("base64");
var bufferEndKey = Buffer.from("\n-----END PUBLIC KEY-----", "utf8");
var bufferEndKeyUTF8 = bufferEndKey.toString("utf8");
var formatedPublicKey = bufferStartKeyUTF8 + pubKeyBase64 + bufferEndKeyUTF8;
var bufferPubicKey = Buffer.from(formatedPublicKey, "utf8");
var bufferPublicKeyUTF8 = bufferPubicKey.toString("utf8");
return bufferPublicKeyUTF8;
}
Related
I am C# developer and I have the below code works well in C# application. I am trying to use the same functionality in the VueJS and Node Js application. I have done with serialization and conversion to fromBase64String, but I am stuck here with generating signature to send it to API. please suggest.
byte[] privateKey = Convert.FromBase64String(File.ReadAllText(appConfig.PrivateKey));
using (CngKey signingKey = CngKey.Import(privateKey, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSACng rsaCng = new RSACng(signingKey))
{
request.Data.Signature = Convert.ToBase64String(rsaCng.SignData(Encoding.UTF8.GetBytes(request.Data.Content), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1));
}
var response = (Response)SendRequest(request, 1);
return response;
I have tried the below in Vue and node Js code
var utf8 = (encodeURIComponent(serializedProductInfo));
var dataInfo = [];
for (var i = 0; i < utf8.length; i++) {
dataInfo.push(utf8.charCodeAt(i));
}
let base64Contenet = window.btoa((encodeURIComponent(dataInfo)));
//let base64Contenet = Buffer.toString((encodeURIComponent(dataInfo)));
console.log(base64Contenet);
var privateKey = [];
privateKey = Buffer.from("My PrivateKey", "base64");
var crypto = require('crypto');
var sha1 = crypto.createHash('sha1');
sha1.key = privateKey;
sha1.update(dataInfo);
var hash = sha1.digest();
var signature = crypto.privateEncrypt({ key: privateKey, padding: crypto.constants.RSA_PKCS1_PADDING }, hash);
console.log(signature);
I am getting the below error
vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in v-on handler: "TypeError: Cannot read properties of null (reading '2')"
I want to regenerate a token to verify the request using hmacHash
The resource documentation is in javascript and I have to implement it in C#.
here what I found in their documentation on generating the token.
routes.post("/webhook", (request) => {
const body = request.rawBody;
const signatureTime = "2022-05-17T09:47:51.935Z";
const signingKey = "localyStoredKey";
const hmac = createHmac("sha256", signingKey);
hmac.update(`${signatureTime}${body}`);
const token = hmac.digest("hex");
}
here is what I have done in C#
public async Task<string> GetWebhookAuthenticationTokenAsync(Stream requestBody) // requestBody is from HttpContext.Request.Body
{
var signingKey = "localyStoredKey"; // should be from db
var signatureTime = "2022-05-17T09:47:51.935Z"; // a timestamp
var reader = new StreamReader(requestBody, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
var byteArray1 = Encoding.UTF8.GetBytes($"{signatureTime}");
var byteArray2 = Encoding.UTF8.GetBytes(body);
var concatenatedByteArray = byteArray1.Concat(byteArray2).ToArray();
var stringInMemoryStream = new MemoryStream(concatenatedByteArray);
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signingKey));
var hashBytes = hmac.ComputeHash(stringInMemoryStream);
var token = BitConverter.ToString(hashBytes).Replace("-", String.Empty).ToLower();
return token;
}
This works fine when I send the request body(json) without newline characters(\n).
when I send the formatted json body with the request, the token is different from javascript generated token. What am I missing?
simply if json is {"pro": "duct"} the tokens are matched but when json {\n "pro": "duct"\n} its different.
PS: I found the issue is due to Carriage Return \r in json body. Any help to handle this independently from the platform?
I am trying to post to an API that requires a SHA1 signature
https://www.inkthreadable.co.uk/api-documentation#order-create
The Signature value will need to be generated at your side as
SHA1(request body + Secret Key).
This is the method I am using to create the signature
static string ComputeSignature(byte[] body, byte[] secret)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var key1 = sha1.ComputeHash(body);
var key2 = key1.Concat(secret).ToArray();
var key3 = sha1.ComputeHash(key2);
return Convert.ToBase64String(key3);
}
}
var body = Encoding.UTF8.GetBytes(json);
var secret = Encoding.UTF8.GetBytes(_inkThreadableSettings.Value.SecretKey);
var signature1 = ComputeSignature(body, secret);
I'm using rest client (vs code extension) to post the data. The json section below is what i am using to pass to the ComputeSignature method above (body param)
POST https://www.inkthreadable.co.uk/api/orders.php?AppId=APP-00202123&Signature=cbAJNd4lU+/+OXpN2bsVJczOuo8=
content-type: application/json
{
"brandName":"Inkthreadable",
"comment":"Test order.",
"shipping_address":{
"firstName":"Alex",
"lastName":"Cunliffe",
"company":"Inkthreadable",
"address1":"Unit 501a",
"address2":"Glenfield Park Two",
"city":"Blackburn",
"county":"Lancashire",
"postcode":"BB1 5QH",
"country":"United Kingdom",
"phone1":"+44 (0)1254 777070"
},
"shipping":{
"shippingMethod":"courier"
},
"items":[
{
"pn":"JH001",
"quantity":4,
"retailPrice":20,
"description":"Please print as large as posible",
"label":{
"type":"printed",
"name":"ink-label"
},
"designs":{
"front":"http://animalfair.com/wp-content/uploads/2014/06/little_cute_cat_1920x1080.jpg",
"back":"http://data3.whicdn.com/images/168204223/large.jpg"
}
}
]
}
The response I get is : "Invalid Signature"
I have tried a number of permutations of a compute signature function but not got it working.
Also tried this:
private string ComputeSignature7(byte[] body, byte[] secret)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var key = sha1.ComputeHash(body.Concat(secret).ToArray());
return Convert.ToBase64String(key);
}
}
I have been given the following nodejs code the generates a signature. This signature is generated by a third party and I need to validate their signature on our backend API in C#. Their code is:
const crypto = require("crypto");
const secret = "..."; //do not share!
const actualSignature = crypto
.createHmac("sha256", Buffer.from(secret , "hex"))
.update(request.body, "utf-8")
.digest("hex");
I have tried to implement in C# but I am uanble to get my signature to match theirs. Here's my code so far:
public byte[] GetHash(string body, string secret)
{
var secretBytes = Encoding.UTF8.GetBytes(secret);
var hexSharedSecretBytes = ByteToHexadecimalString(secretBytes);
var hexSecretBytes = Encoding.UTF8.GetBytes(hexSharedSecretBytes);
var hmac = new HMACSHA256(hexSecretBytes);
var bodyBytes = Encoding.UTF8.GetBytes(body);
return hmac.ComputeHash(bodyBytes);
}
private string ByteToHexadecimalString(byte[] buff)
{
return buff.Aggregate("", (current, i) => current + i.ToString("x2"));
}
I would greatly appreciate some help.
I am trying to write an interface to our Phabricator install to allow out internal improvement system to create tasks. However, I cannot figure out why I keep getting a certificate error.
"{\"result\":null,\"error_code\":\"ERR-INVALID-CERTIFICATE\",\"error_info\":\"Your authentication certificate for this server is invalid.\"}"
The following is my code;
private void CreateSession()
{
int token = (int)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds);
var result = this.Do(
"conduit.connect",
new
{
client = this.ClientName,
clientVersion = this.ClientVersion,
clientDescription = "HIS to Fabricator Connector",
user = this.User,
authToken = token,
authSignature = SHA1HashStringForUTF8String(token + this.Certificate)
});
this.m_SessionKey = result.sessionKey;
this.m_ConnectionID = result.connectionID;
}
public static string SHA1HashStringForUTF8String(string s)
{
byte[] bytes = Encoding.UTF8.GetBytes(s);
var sha1 = SHA1.Create();
byte[] hashBytes = sha1.ComputeHash(bytes);
return HexStringFromBytes(hashBytes);
}
public static string HexStringFromBytes(byte[] bytes)
{
var sb = new StringBuilder();
foreach (byte b in bytes)
{
var hex = b.ToString("x2");
sb.Append(hex);
}
return sb.ToString();
}
This returns the following JSON;
"{\"client\":\"HIS\",\"clientVersion\":\"1\",\"clientDescription\":\"HIS to Fabricator Connector\",\"user\":\"KYLIE\",\"authToken\":1449486922,\"authSignature\":\"ec020edbd5082d3971c2c11ef4f4917244fc4a78\"}"
I think the issue is the certificate I am passing. I am using;
api-3ydcae2gtmf6u6uer2zow465j6px
which I obtained from the Conduit API Tokens page.
Any pointers?
You have to get the token via .../conduit/token
Use that token to query .../api/conduit.getcertificate
As result you get the certificate -> profit! :)
PS: it's neither api- nor cli- token to query the certificate! ;)