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;
}