问题: nodejs 应用程序创建一个作为许可证的 json 文件,而 asp.net 核心应用程序需要使用公钥验证信息。节点应用程序使用 RSA sha256 使用私钥对其进行签名,.net 核心应用程序将使用公钥进行验证。谢谢你。json 文件包含以下信息:
{
"info":{
"validto":1514678400000,"validfrom":1498608000000,"nodes":10,"email":"person@example.com","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"
}
}
我们在 asp.net 核心方面尝试了很多东西,但没有成功,给定的公钥有一些不寻常的字符,如 \n,通常看不到。以下是我们尝试过的不同方法之一:
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"":""person@example.com"",""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 () ;
}
}
}
公钥格式如下:
-----BEGIN RSA PUBLIC KEY
-----\nMIIBCgKCAQEAqao1ZkAYKDybHSeoy79ySQDcXODByDRaZKT2nYwT8GrYohBle8phB5LgSoQu\nVD7ErRFGHxutcqPrfL3AuTHg874Kmw6/G+25/FdC9uNJzLtCP+Z5mOrF5HlU8dGOOpTeq4y5\n0EPcj//YuO4kScj0wOOp1HMRwxsdVo\nAZUQwMz5w1QIoGL5CoW7RKiL/oQw0Mh0Ju+9ofVbovSzBTo0r7onqw6M0hOJScV86iQ21Ukl\nup/6CmXCMwcYK1Fr5J6YNbeZoQhkII7VazPMgZetJBCfm+iyBPSPARlf13RLM0cHzwIDAQAB\n-----END RSA PUBLIC KEY-----\n
nodejs 应用程序对数据进行签名的代码:
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);
});
};
还有另一个 nodejs 应用程序正在成功验证数据,示例如下,只是在 .net 中,事情没有走到一起:
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;
};