Problem: A nodejs application creates a json file which is a license,and an asp.net core application will need to validate the info with public key. The node application uses RSA sha256 to sign it with private key, and .net core app will use public key to validate. THANK YOU. The json file includes the following information:
{
"info":{
"validto":1514678400000,"validfrom":1498608000000,"nodes":10,"email":"[email protected]","name":"TAC Account","phone":"3333333333333","address":"something"
},
"signature":
{
"data":[159,1,86,24,244,199,39,40,251,195,175,80,18,33,232,63,178,213,205,129,150,58,243,154,138,168,61,197,222,50,222,80,33,82,135,243,121,250,176,33,9,25,167,177,235,193,246,236,235,19,187,118,57,64,38,143,42,35,12,207,133,33,166,201,34,76,40,87,242,163,141,14,218,198,247,91,191,132,86,162,194,143,177,147,65,171,160,41,2,244,130,53,82,178,72,39,247,45,242,139,243,59,79,196,12,70,14,202,246,48,231,66,38,94,235,237,204,77,40,252,216,63,41,204,210,228,93,16,201,4,123,104,119,251,56,160,9,105,180,217,129,113,19,166,89,199,203,129,47,218,20,131,94,87,251,193,177,111,151,72,187,79,0,63,0,168,36,147,155,47,5,123,176,203,189,111,199,165,90,12,122,2,148,62,107,115,132,183,76,147,7,238,154,174,198,226,170,204,193,18,197,30,191,189,133,134,33,33,159,14,90,153,219,226,184,149,19,179,210,171,136,39,144,178,229,236,126,11,252,80,65,83,181,117,26,37,231,92,151,110,33,93,239,243,129,58,201,214,231,248,151,23,19,170,0,19],
"type":"Buffer"
}
}
We have tried many things at asp.net core side but no success, the public key given has some unusual characters like \n which not seen typically. Following is one of different things we tried:
using System ;
using System.IO ;
using System.Text ;
using Org.BouncyCastle.Crypto ;
using Org.BouncyCastle.Crypto.Parameters ;
using Org.BouncyCastle.OpenSsl ;
using Org.BouncyCastle.Security ;
namespace ValidateRsa
{
internal class Program
{
private static readonly string info =
@"{""validto"":1514678400000,""validfrom"":1498608000000,""nodes"":10,""email"":""[email protected]"",""name"":""TAC Account"",""phone"":""3333333333333"",""address"":""something""}"
;
private static readonly byte[] signature =
{
159, 1, 86, 24, 244, 199, 39, 40, 251, 195, 175, 80, 18, 33, 232, 63, 178, 213, 205, 129, 150, 58, 243, 154, 138, 168,
61, 197, 222, 50, 222, 80, 33, 82, 135, 243, 121, 250, 176, 33, 9, 25, 167, 177, 235, 193, 246, 236, 235, 19, 187, 118,
57, 64, 38, 143, 42, 35, 12, 207, 133, 33, 166, 201, 34, 76, 40, 87, 242, 163, 141, 14, 218, 198, 247, 91, 191, 132, 86,
162, 194, 143, 177, 147, 65, 171, 160, 41, 2, 244, 130, 53, 82, 178, 72, 39, 247, 45, 242, 139, 243, 59, 79, 196, 12,
70, 14, 202, 246, 48, 231, 66, 38, 94, 235, 237, 204, 77, 40, 252, 216, 63, 41, 204, 210, 228, 93, 16, 201, 4, 123, 104,
119, 251, 56, 160, 9, 105, 180, 217, 129, 113, 19, 166, 89, 199, 203, 129, 47, 218, 20, 131, 94, 87, 251, 193, 177, 111,
151, 72, 187, 79, 0, 63, 0, 168, 36, 147, 155, 47, 5, 123, 176, 203, 189, 111, 199, 165, 90, 12, 122, 2, 148, 62, 107,
115, 132, 183, 76, 147, 7, 238, 154, 174, 198, 226, 170, 204, 193, 18, 197, 30, 191, 189, 133, 134, 33, 33, 159, 14, 90,
153, 219, 226, 184, 149, 19, 179, 210, 171, 136, 39, 144, 178, 229, 236, 126, 11, 252, 80, 65, 83, 181, 117, 26, 37,
231, 92, 151, 110, 33, 93, 239, 243, 129, 58, 201, 214, 231, 248, 151, 23, 19, 170, 0, 19
} ;
private static void Main (string[] args)
{
Console.WriteLine ("Setting up validator...") ;
TextReader text = File.OpenText (@"X:\Scratch\pubkey.asc") ;
var pemr = new PemReader (text) ;
object pem = pemr.ReadObject () ;
text.Close () ;
var keyParams = (RsaKeyParameters) (AsymmetricKeyParameter) pem ;
ISigner sig = SignerUtilities.GetSigner ("SHA-256withRSA") ;
sig.Init (false, keyParams) ;
byte[] infoBytes = Encoding.ASCII.GetBytes (Program.info) ;
sig.BlockUpdate (infoBytes, 0, infoBytes.Length) ;
if (sig.VerifySignature (Program.signature))
Console.WriteLine ("Verified!") ;
else
Console.WriteLine ("Failed!") ;
Console.ReadLine () ;
}
}
}
Public key is in the following format:
-----BEGIN RSA PUBLIC KEY
-----\nMIIBCgKCAQEAqao1ZkAYKDybHSeoy79ySQDcXODByDRaZKT2nYwT8GrYohBle8phB5LgSoQu\nVD7ErRFGHxutcqPrfL3AuTHg874Kmw6/G+25/FdC9uNJzLtCP+Z5mOrF5HlU8dGOOpTeq4y5\n0EPcj//YuO4kScj0wOOp1HMRwxsdVo\nAZUQwMz5w1QIoGL5CoW7RKiL/oQw0Mh0Ju+9ofVbovSzBTo0r7onqw6M0hOJScV86iQ21Ukl\nup/6CmXCMwcYK1Fr5J6YNbeZoQhkII7VazPMgZetJBCfm+iyBPSPARlf13RLM0cHzwIDAQAB\n-----END RSA PUBLIC KEY-----\n
The code where nodejs app is signing the data:
const licenseSchema = new Schema({
info: {
name: String,
address: String,
phone: String,
email: String,
nodes: Number,
validfrom: Schema.Types.Mixed, // validfrom and validto must be either
validto: Schema.Types.Mixed, // valid Date strings or ms since epoch
},
signature: {
type: { type: String, required: true },
data: { type: Schema.Types.Mixed, required: true },
},
});
// updates license.signature
licenseSchema.statics.createAndSignLicense = function createAndSignLicense(object, privateKey, cb) {
let arrayInfo = [];
arrayInfo = [
object.name,
object.address,
object.phone.toString(),
object.email,
object.nodes.toString(),
object.validfrom,
object.validto,
];
const sign = crypto.createSign('RSA-SHA256');
sign.update(JSON.stringify(arrayInfo));
const sig = sign.sign(privateKey);
const LicenseModel = this;
const licenseDoc = new LicenseModel({
info: object,
signature: {
type: 'Buffer',
data: Array.prototype.slice.call(sig, 0),
},
});
licenseDoc.save((err) => {
if (err) { return cb(err); }
return cb(null, licenseDoc);
});
};
There is another nodejs app which is validating the data successfully, example is below, just in .net things are not coming together:
const Schema = mongoose.Schema;
export const Errors = {LicenseValidationError: 'LicenseValidationError'};
const LicenseDescription = {
key: { type: String, unique: true },
info: {
name: String,
address: String,
phone: String,
email: String,
nodes: Number,
validfrom: Schema.Types.Mixed, // validfrom and validto must be either
validto: Schema.Types.Mixed, // valid Date strings or ms since epoch
},
signature: {
type: { type: String, required: true },
data: Schema.Types.Mixed
}
};
// Application state variable representing license validity.
let isLicenseValid = false;
/*
* uses the utils function with a validator to set the system-wide document
* license is a file (buffer) or JSON license.
* This method attempts to validate the input.
*
* See: License.statics.ValidateLicense()
*
* cb: function (err, license) { ... }
*/
const License = utils.createSchema(LicenseDescription, {
validator: function (license, cb) {
validateLicense(license, (err, valid) => {
if (err) {
return cb(err);
}
isLicenseValid = isLicenseWithinDateRange(license);
return cb(null, true);
});
}
});
// license public key, hardcoded.
// Uses RSA-SHA256
const pair = () => ({
public: `-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAqao1ZkAYKDybHSeoy79ySQDcXODByDRaZKT2nYwT8GrYohBle8phB5LgSoQu\nVD7ErRFGHxutcqPrfL3AuTHg874Kmw6/G+25/FdC9uNJzLtCP+Z5mOrF5HlU8dGOOpTeq4y5\n0EPcj//YuO4kScj0wOOp1HMRwxsdVo+G00Q\nAZUQwMz5w1QIoGL5CoW7RKiL/oQw0Mh0Ju+9oV86iQ21Ukl\nup/6CmXCMwcYK17VazPMgZetJBCfm+iyBPSPARlf13RLM0cHzwIDAQAB\n-----END RSA PUBLIC KEY-----\n`
});
/*
* Verifies the supplied license (must be a valid JSON object) against the
* public key (hardcoded).
*
* Returns true if valid, false otherwise.
*/
const verifySignature = license => {
winston.info('Verifying license signature:');
// crypto.verify relies on specific key ordering (undefined behavior, unfortunately).
// To try to force key order, we construct this object (with explicit toString calls
// where applicable).
const infoString = [
license.info.name,
license.info.address,
license.info.phone,
license.info.email,
license.info.nodes.toString(),
new Date(license.info.validfrom).toString(),
new Date(license.info.validto).toString(),
];
const infoMS = [
license.info.name,
license.info.address,
license.info.phone,
license.info.email,
license.info.nodes.toString(),
license.info.validfrom,
license.info.validto,
];
const verifyString = crypto.createVerify('RSA-SHA256'), verifyMS = crypto.createVerify('RSA-SHA256'),
licenseInfoString = JSON.stringify(infoString), licenseInfoMS = JSON.stringify(infoMS);
// Currently the signature must be a buffer and the type field is ignored.
let buf;
try {
buf = new Buffer(license.signature.data);
} catch (parseError) {
return false;
}
verifyString.update(licenseInfoString);
verifyMS.update(licenseInfoMS);
const isLicenseValid = verifyString.verify(pair().public, buf) || verifyMS.verify(pair().public, buf);
winston.info(`The license signature is ${(isLicenseValid) ? 'valid.' : 'invalid.'}`);
return isLicenseValid;
};
There's something wrong with your key file I guess?
Your code works fine.
Here's an example of generating the signature in node:
and here's an example of verifying it using bouncy castle in C# .net core (I've simplified, but its basically exactly what you're doing):
With these two key files I just randomly generated:
and:
...and it works fine:
So my guess?
Either something is wrong with your public key, or the json input you're passing in to the function on both sides isn't quite the same for some reason.
There's nothing wrong with your signing code as far as I can tell.