How to verify certificate chain in Flutter? - node.js

I have a mobile app written in Flutter that stores an intermediate cert. Other mobile clients can send certificates to this device and it should decide whether the sent certificate was issued based on the intermediate cert or not. Shortly, I have to verify the integrity of the certificate chain.
I implemented a test function in Node.js/TS but I don't know how I can do the same thing in Flutter.
Here is the example code:
import fs from "fs";
import nodeForge from "node-forge";
function validateCert(certPem: string) {
const pki = nodeForge.pki;
try {
const caCert = fs.readFileSync("pki_int_ca.pem", { encoding: "utf-8" });
const caStore = pki.createCaStore([caCert]);
const certToVerify = pki.certificateFromPem(certPem);
const verified = pki.verifyCertificateChain(caStore, [certToVerify]);
console.log("Certificate Verified: " + verified);
} catch (err) {
console.log(err);
}
}

I am using this library https://github.com/jeroentrappers/x509 to verify certificate chain in flutter.
here is my code:
import 'dart:convert';
import 'dart:typed_data';
import 'package:x509b/x509.dart' as x509;
import 'package:asn1lib/asn1lib.dart';
import 'flutter_log.dart';
//caCert is the root certificate. and serverCert is the certificate need to be verifed
bool verifyCertificate(String caCert, String serverCert) {
// var strX1PublicKeyInfo = "-----BEGIN PUBLIC KEY-----\nSOME PUBLIC KEY\n-----END PUBLIC KEY-----";
// var strX2Certificate = "-----BEGIN CERTIFICATE-----\nSOME CERTIFICATE\n-----END CERTIFICATE-----";
var x1PublicKey = (x509.parsePem(caCert).single as x509.X509Certificate).publicKey as x509.RsaPublicKey;
var x2Certificate = x509.parsePem(serverCert).single as x509.X509Certificate;
var x2CertificateDER = decodePEM(serverCert);
var asn1Parser = ASN1Parser(x2CertificateDER);
var seq = asn1Parser.nextObject() as ASN1Sequence;
var tbsSequence = seq.elements[0] as ASN1Sequence;
var signature = x509.Signature(Uint8List.fromList(x2Certificate.signatureValue!));
var verifier = x1PublicKey.createVerifier(x509.algorithms.signing.rsa.sha256);
return verifier.verify(tbsSequence.encodedBytes, signature);
}
Uint8List decodePEM(String pem) {
var startsWith = [
'-----BEGIN PUBLIC KEY-----',
'-----BEGIN PRIVATE KEY-----',
'-----BEGIN CERTIFICATE-----',
];
var endsWith = [
'-----END PUBLIC KEY-----',
'-----END PRIVATE KEY-----',
'-----END CERTIFICATE-----'
];
pem=pem.trim();
//HACK
for (var s in startsWith) {
if (pem.startsWith(s)) pem = pem.substring(s.length);
}
for (var s in endsWith) {
if (pem.trim().endsWith(s)) {
Log().i("certificate:substring");
pem = pem.trim().split(s)[0];
}
}
//Dart base64 decoder does not support line breaks
pem = pem.replaceAll('\n', '');
pem = pem.replaceAll('\r', '');
return Uint8List.fromList(base64.decode(pem));
}

Take a look at the package basic_utils (https://github.com/Ephenodrom/Dart-Basic-Utils). It contains a lot of helper methods on X509 certificates.
dependencies:
basic_utils: ^5.4.3
CertificateChainCheckData checkChain(List<X509CertificateData> x509);
You can parse the PEM files via X509Utils class and pass it as a list. It will check the order of the chain and the signature.
Here is an example from the unit tests :
test('Test checkChain()', () {
var data = X509Utils.pkcs7fromPem(pkcs7);
var result = X509Utils.checkChain(data.certificates!);
expect(result.pairs!.length, 2);
expect(result.pairs!.elementAt(0).isValid(), true);
expect(result.pairs!.elementAt(1).isValid(), true);
var f = File('test_resources/rapid_ssl_broken_chain.pem');
var pem = f.readAsStringSync();
var chain = X509Utils.parseChainString(pem);
result = X509Utils.checkChain(chain);
expect(result.pairs!.length, 1);
expect(result.pairs!.elementAt(0).isValid(), false);
expect(result.pairs!.elementAt(0).dnDataMatch, true);
expect(result.pairs!.elementAt(0).signatureMatch, false);
});

Related

Securing GRPC connections with server.key and server.crt using protobuf-net grpc

I'm trying to make secure grpc calls from the client side but I can't seem to get my code to work; here are the steps and implementations I've taken.
My specs: I'm running visual studio on mac m1 pro and I'm using Protobuf-net Grpc
Step 1 - Install OpenSSL
brew install openssl
Step 2 - Create a certificate and key
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
this asked a couple of questions. and then created the crt and key files.
I've also confirmed that certificate and key pair are created correctly by running the below commands.
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5
Implementations
Server:
Program.cs file
var builder = WebApplication.CreateBuilder(args);
X509Certificate2 cert = new X509Certificate2("/Users/asimgunduz/server.crt", "/Users/asimgunduz/server.key");
builder.WebHost.ConfigureKestrel(opt =>
{
opt.Listen(IPAddress.Any, 5010, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1;
listenOptions.UseHttps(cert);
});
});
Client Side
public static void RegisterGrpcService<TService>(this IServiceCollection Services) where TService : class
{
X509Certificate2 certificate = new X509Certificate2("/Users/asimgunduz/server.crt", "/Users/asimgunduz/server.key");
var socketsHandler = new SocketsHttpHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
EnableMultipleHttp2Connections = true,
};
socketsHandler.SslOptions.RemoteCertificateValidationCallback = (message, cert, chain, errors) =>
{
// Perform custom validation here and return a boolean indicating whether the certificate is valid
return certificate.Equals(cert);
};
var hand = new GrpcWebHandler(GrpcWebMode.GrpcWeb, socketsHandler);
//hand.HttpVersion = HttpVersion.Version11;
Services.AddCodeFirstGrpcClient<TService>(x =>
{
x.ChannelOptionsActions.Add(x => new GrpcChannelOptions
{
HttpHandler = hand,
MaxReceiveMessageSize = null, //30000000
MaxSendMessageSize = null, //30000000
Credentials = ChannelCredentials.Insecure,
UnsafeUseInsecureChannelCallCredentials = true,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new RoundRobinConfig() } }
});
x.Address = new Uri("http://localhost:5010");
})
.ConfigurePrimaryHttpMessageHandler(x => hand);
}
public static void RegisterGrpcServiceWithSsl2<TService>(this IServiceCollection services, string address) where TService : class
{
X509Certificate2 certificate = new X509Certificate2("/Users/asimgunduz/server.crt", "/Users/asimgunduz/server.key");
var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
{
X509Chain x509Chain = new X509Chain();
x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool isChainValid = x509Chain.Build(new X509Certificate2(cert));
return isChainValid;
},
ClientCertificates = new X509Certificate2Collection { certificate }
},
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
EnableMultipleHttp2Connections = true
};
var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
{
HttpHandler = handler,
});
services.AddCodeFirstGrpcClient<TService>(x =>
{
x.Address = new Uri(address);
x.ChannelOptionsActions.Add(options =>
{
options.HttpHandler = handler;
});
});
}
When I run Server it throws the following exception
"The server mode SSL must use a certificate with the associated private key."
any help is much appreciated.
Aright after an hour of hard work, I've managed to make my code work:
just leaving here in case future me or someone else needs it..
Changes made in my code
(SERVER)
program.cs
I've changed port number from 5010 => 7178
//Created certificate with the below method
var certificate =
X509Certificate2.CreateFromPemFile("/Users/asimgunduz/server.crt", Path.ChangeExtension("/Users/asimgunduz/server.crt", "key"));
//Verify is blueprints match
var verf = certificate.Verify();
//and finally add usehttps(with the created certificate)
builder.WebHost.ConfigureKestrel(opt =>
{
opt.ListenLocalhost(7178, o =>
{
o.Protocols = HttpProtocols.Http1;
o.UseHttps(certificate);
});
});
and this is How I updated my extension methods in client side
public static void RegisterGrpcServiceWithSsl2<TService>(this IServiceCollection services, string address) where TService : class
{
var certificate =
X509Certificate2.CreateFromPemFile("/Users/asimgunduz/server.crt", Path.ChangeExtension("/Users/asimgunduz/server.crt", "key"));
var socketsHandler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
{
X509Chain x509Chain = new X509Chain();
x509Chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
bool isChainValid = x509Chain.Build(new X509Certificate2(cert));
return isChainValid;
},
ClientCertificates = new X509Certificate2Collection { certificate }
},
PooledConnectionIdleTimeout = Timeout.InfiniteTimeSpan,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30),
EnableMultipleHttp2Connections = true
};
var hand = new GrpcWebHandler(GrpcWebMode.GrpcWeb, socketsHandler);
hand.HttpVersion = HttpVersion.Version11;
services.AddCodeFirstGrpcClient<TService>(x =>
{
x.ChannelOptionsActions.Add(x => new GrpcChannelOptions
{
HttpHandler = hand,
MaxReceiveMessageSize = null,
MaxSendMessageSize = null,
Credentials = ChannelCredentials.Insecure,
UnsafeUseInsecureChannelCallCredentials = true,
ServiceConfig = new ServiceConfig { LoadBalancingConfigs = { new RoundRobinConfig() } }
});
x.Address = new Uri(address);
})
.ConfigurePrimaryHttpMessageHandler(x => hand);
}

Generate digital signature with nodejs for Alipay API

I am implenting ALIPAY API, to my project. In order to make some request I need to generate a signature with RSA private.pem - public.pem. The documentions is only showing JAVA exemple, but I need to implement it in NodeJS. So I try using crypto.sign but cannot get this to work.
Here is the JAVA code :
const g = (content, privateKey, charset) => {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.getEncoder().encodeToString(signed);
} catch (Exception e) {
throw new RuntimeException("the key's format maybe not right");
}
}
Can any one help to convert this into NodeJS code.
HERE IS MY WORKING SOLUTIONS:
/**
* #param {*} // let parameters = new Map(); parameters.set("service", "alipay.overseas.secmerchant.maintain.queryStatus");
*/
signRSA: async (parameters) => {
let unnecessaryParams = Array.from(parameters.entries())
.filter(a1 => a1[1] === undefined || a1[1].length === 0 || a1[1] === "null")
.map(a1 => a1[0]);
unnecessaryParams.forEach(a1 => parameters.delete(a1));
let charset = ["_input_charset", "charset"].filter(a1 => parameters.has(a1)).map(a1 => parameters.get(a1))[0];
charset = charset === undefined ? "UTF-8" : charset;
//MD5,RSA,RSA2
let signContent = Array.from(parameters.entries())
.filter(a1 => {
return a1[0] !== "sign" && a1[0] !== "sign_type";
})
.sort((a1, a2) => a1[0].localeCompare(a2[0]))
.map(a1 => a1[0] + "=" + a1[1]).join("&");
let key = fs.readFileSync('./services/alipay/rsa_private_key.pem');
// let RSA_sign = r.sign_it(signContent, key, "RSA2")
let RSA_sign = crypto.createSign('RSA-SHA256')
RSA_sign.update(signContent)
let s1 = RSA_sign.sign(key, "base64")
parameters.set("sign", s1);
return Array.from(parameters.entries()).map(a1 => a1[0] + "=" + encodeURIComponent(a1[1])).join("&");
}
I've created a Node.js version of this code, it should sign content using the provided private key.
I've also created a verifySignature function to check this, and created the equivalent code in Java (using your code as a template), just to ensure we're getting the same result in both languages.
Node.js
const crypto = require("crypto");
// Sample keys I've generated
const privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAICFIt6pLMSNn9scI/h85V/DamtsFqfN4DB+VSPpGMAcwEByKVsiBzdX7JJsr8uquoVWEmFPwymxUVwIy+MOK03QEaQDns0W/SeVNXyWvDTO2w65vV9hIEO1VyiQiBA8n0yoQGfbn2KBnv7SggJDpy10cF3lx5SdHV96lF+qyBs7AgMBAAECgYAQEJRLQNpXt1xEB0B3zyTc05B53Qj38MQVS7VYx8pQOUfOxnZk7dv5DwSDSRKwYqbuA9RIVbAPhhqlZnQV7a9722QBFcEKLh6/65HCX248t/v6/x1kj9p6rcbLuFuakjKXs4AznWmb4YV2Flh9/qx9keUzdS5/UPJZVU2+grAAAQJBAO/3JjTRgiGQxEdA4MVpfs0lh/+gzbDszL1In5USAPKyGPDqYIktAfF3cBc7BKyK3kBenJi3S5Qv5R3u2Ly6H0cCQQCJG5oAvEt8JPL7ZyxHzdTP7+7hWXRGOyK2Ar0CO3fbg+SGls5liJY3HNHc0lrdyG1A+5Z6AKUNYCgfbOhqmeZtAkEA5CzhUoYJNDuAt7Q0RuLqZM2URPk1vU9d23qr68ajyiKZXrOuuaFnYKDOn/hJmHuvnAua4gggwLbOKSlNRB/CzwJAKedztBHYiELKuKeZ0wBHsJ3GRr2OWgCs5TAFEG+YfFDdQX1J66JJNuLqCTGJcAtXyOqb3QHhcCsZWDFy/1G2KQJBAKRnm7D0BHLOK37Xp3MxGva4tFP6VPdqjgIVBW3PM8BcwTcw8VML3kkFh2y0gJdLRxzFwDDJgfG6Cxp8i1gqB+4=";
const publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAhSLeqSzEjZ/bHCP4fOVfw2prbBanzeAwflUj6RjAHMBAcilbIgc3V+ySbK/LqrqFVhJhT8MpsVFcCMvjDitN0BGkA57NFv0nlTV8lrw0ztsOub1fYSBDtVcokIgQPJ9MqEBn259igZ7+0oICQ6ctdHBd5ceUnR1fepRfqsgbOwIDAQAB";
const content = "We are such stuff as dreams are made on, and our little life is rounded with a sleep";
const signature = signContent(content, base64KeyToPEM(privateKey, "PRIVATE"), "utf8");
console.log("Signature:", signature);
console.log("Verify signature:", verifySignature(content, base64KeyToPEM(publicKey, "PUBLIC"), signature, "utf8"));
function signContent(content, privateKey, encoding) {
const sign = crypto.createSign("SHA256");
sign.write(content, encoding);
sign.end();
return sign.sign(privateKey, "base64");
}
function verifySignature(content, publicKey, signature, encoding) {
const verify = crypto.createVerify("SHA256");
verify.write(content, encoding);
verify.end();
return verify.verify(publicKey, Buffer.from(signature, "base64"));
}
function base64KeyToPEM(base64Key, keyType) {
return [`-----BEGIN ${keyType} KEY-----`, ...splitStringIntoChunks(base64Key, 64), `-----END ${keyType} KEY-----`].join("\n");
}
function splitStringIntoChunks(input, chunkSize) {
const chunkCount = Math.ceil(input.length / chunkSize)
return Array.from( { length: chunkCount } ).map((v, chunkIndex) => input.substr(chunkIndex * chunkSize, chunkSize));
}
Java
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
public class DigitalSignature {
public static void main(String[] args)
{
try {
// Sample keys I've generated
final String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAICFIt6pLMSNn9scI/h85V/DamtsFqfN4DB+VSPpGMAcwEByKVsiBzdX7JJsr8uquoVWEmFPwymxUVwIy+MOK03QEaQDns0W/SeVNXyWvDTO2w65vV9hIEO1VyiQiBA8n0yoQGfbn2KBnv7SggJDpy10cF3lx5SdHV96lF+qyBs7AgMBAAECgYAQEJRLQNpXt1xEB0B3zyTc05B53Qj38MQVS7VYx8pQOUfOxnZk7dv5DwSDSRKwYqbuA9RIVbAPhhqlZnQV7a9722QBFcEKLh6/65HCX248t/v6/x1kj9p6rcbLuFuakjKXs4AznWmb4YV2Flh9/qx9keUzdS5/UPJZVU2+grAAAQJBAO/3JjTRgiGQxEdA4MVpfs0lh/+gzbDszL1In5USAPKyGPDqYIktAfF3cBc7BKyK3kBenJi3S5Qv5R3u2Ly6H0cCQQCJG5oAvEt8JPL7ZyxHzdTP7+7hWXRGOyK2Ar0CO3fbg+SGls5liJY3HNHc0lrdyG1A+5Z6AKUNYCgfbOhqmeZtAkEA5CzhUoYJNDuAt7Q0RuLqZM2URPk1vU9d23qr68ajyiKZXrOuuaFnYKDOn/hJmHuvnAua4gggwLbOKSlNRB/CzwJAKedztBHYiELKuKeZ0wBHsJ3GRr2OWgCs5TAFEG+YfFDdQX1J66JJNuLqCTGJcAtXyOqb3QHhcCsZWDFy/1G2KQJBAKRnm7D0BHLOK37Xp3MxGva4tFP6VPdqjgIVBW3PM8BcwTcw8VML3kkFh2y0gJdLRxzFwDDJgfG6Cxp8i1gqB+4=";
final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAhSLeqSzEjZ/bHCP4fOVfw2prbBanzeAwflUj6RjAHMBAcilbIgc3V+ySbK/LqrqFVhJhT8MpsVFcCMvjDitN0BGkA57NFv0nlTV8lrw0ztsOub1fYSBDtVcokIgQPJ9MqEBn259igZ7+0oICQ6ctdHBd5ceUnR1fepRfqsgbOwIDAQAB";
final String content = "We are such stuff as dreams are made on, and our little life is rounded with a sleep";
String signature = signContent(content, privateKey, "UTF-8");
System.out.println("Signature: " + signature);
System.out.println("verifySignature: " + verifySignature(signature, content, getPublicKeyFromBase64(publicKey), "UTF-8"));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
private static PublicKey getPublicKeyFromBase64(String publicKeyBase64) throws Exception {
return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64)));
}
public static String signContent(String content, String privateKey, String charset) throws Exception
{
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.getEncoder().encodeToString(signed);
} catch (Exception e) {
throw new RuntimeException("signContent: Exception occurred: " + e.getMessage());
}
}
public static Boolean verifySignature(String signatureBase64, String content, PublicKey publicKey, String charset) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(content.getBytes(charset));
return signature.verify(Base64.getDecoder().decode(signatureBase64));
}
}

Sign / Verify with nodejs crypto always false

I need to sign and verify messages in node.js. I followed the crypto package document but I always get a false on the verify method, which indicates that the signature is incorrect.
The RSA public key and private key I'm using are working well for encrypt / decrypt messages with the same crypto package.
Here's the code:
var crypto = require("crypto");
var eol = require('os').EOL;
The sign and verify methods:
function RSASign(privateKey, data) {
const sign = crypto.createSign('RSA-SHA256');
sign.update(data);
var sig = sign.sign(privateKey, 'hex')
console.log(sig);
return sig;
}
function RSAVerify(publicKey, signature, data) {
const verify = crypto.createVerify('RSA-SHA256');
verify.update(data);
console.log(verify.verify(publicKey, signature));
}
I'm calling the methods using
var dataToSign = "some data";
var sig = RSASign(privateKey, dataToSign);
RSAVerify(publicKey, sig, dataToSign);
The public key:
var pubStr = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbbBSVpWzSmCGVeezhuVFgUEYowUxgX/SnFdymGRCHGc77d5I0xkMAnIOWbI2MmP8j/7sdfPuUF0V5zw+Hd/7iZ6vs2k4JRKdprrB/zSC4GGqCDpDkbRYydcw3kwDgKkHhDp6NwIKvvl87WsnFozi487tGPQ8NO15hngwsV7DrawIDAQAB';
var publickKey = '-----BEGIN PUBLIC KEY-----' + eol + pubStr + eol + '-----END PUBLIC KEY-----';
The private key:
var p = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJtsFJWlbNKYIZV57OG5UWBQRijBTGBf9KcV3KYZEIcZzvt3kjTGQwCcg5ZsjYyY/yP/ux18+5QXRXnPD4d3/uJnq+zaTglEp2musH/NILgYaoIOkORtFjJ1zDeTAOAqQeEOno3Agq++XztaycWjOLjzu0Y9Dw07XmGeDCxXsOtrAgMBAAECgYAV13iFFzxV1B9UHFBX4G05Nc7GR3PuT03YdVAO35LdCZl26XTYicw8t8IeT58M1St16ahoGnpYc3TGC31JMmnVOr58At0jbd4JQgvUaE+2jVvgp0Lc6n/jN+7NYBGlEy44ZpIRbB1Biu7khCZ0D+8PZsDMi6WJK4jgI5Gf/aXvkQJBAOe6809U/1wEVDJFHZ6A++WI/8iebXSbw9hDa4a9qoXv8bsMjYkDiblD3UPRlTEdFsAOA/YuGdah+fKE7jKdKkcCQQCrszD8Z1MYWYE4dMQTRPxEKHGQZd5HHkTQu9l6FV7Bv2ga9wLhT4QTb/5U7WYGgbfxhFzklxoqsmhTJNuLlyO9AkBrA1nDZBQ9MT6UrHheL2Ckgpzkz8zqUdiicZghdEtgaQtv/v8JrBmY9e8jl5DXSoCsFozbzjReexTLW3oI462XAkEAnTQ/kZl4tz6b1XjzXUE4R59P+wmJ7kuEbijQAbs3OuVpB+dJN8l5/+H2VwPU+fgi1np+Ir1GM/mNEzMX4ELNcQJBAIk1s3Y7ep2gK0p4js4f9HU0u2vH25+6bu+y6JFfvIBd8OR1bCFEe3FIui1H/ohh0Eoo3ZgJZ/5JjwfsqJzOoBs=';
var privateKey = '-----BEGIN PRIVATE KEY-----' + eol + p + eol + '-----END PRIVATE KEY-----'
What am I missing?
While verifying verify.verify(publicKey, signature,'hex'),you have to include the signatureEncoding that you have used during encoding
verify signature
function RSAVerify(publicKey, signature, data) {
const verify = crypto.createVerify('RSA-SHA256');
verify.update(data);
console.log(verify.verify(publicKey, signature,'hex'));
}

Generate Key for signing within nodejs

I have what seems to be a rather simple problem, but I never had to deal with cryptography in nodejs before.
I want to implement a system which generates a new keypair about every 4 months, which are used for signing and verifying a generated result.
This is my current code:
'use strict';
const fs = require('fs');
const crypto = require('crypto');
const algorithm = 'RSA-SHA512';
const sign = crypto.createSign(algorithm);
const verify = crypto.createVerify(algorithm);
const base64 = 'base64';
const keyDir = './keys/';
const validator = {};
validator.check = function(dataArray, signature, date){
verify.update(Buffer.from(dataArray));
return verify.verify(getPublicKey(date), signature);
};
validator.sign = function(dice){
sign.update(Buffer.from(dice));
return sign.sign(getPrivateKey(), base64);//Error happens here
};
validator.getPublicKey = function(date){
date = toDateObject(date);
for(current of getFilesDescending()){
if(fileNameToDate(current).getMilliseconds() <= date.getMilliseconds()){
const prefix = '-----BEGIN RSA PUBLIC KEY-----';
const suffix = '-----END RSA PUBLIC KEY-----';
return prefix + fs.readFileSync(keyDir + fileName, 'utf8').split('\n')[0] + suffix;
}
}
}
function fileNameToDate(fileName){
const array = fileName.split("-");
return new Date(array[0], parseInt(array[1]) - 1);
}
function getPrivateKey(){
const file = getFilesDescending()[0];
if(!file || monthDiff(new Date(), fileNameToDate(file)) > 4){
generateKeys();
return getPrivateKey();
}
const prefix = '-----BEGIN RSA PRIVATE KEY-----';
const suffix = '-----END RSA PRIVATE KEY-----';
return prefix + fs.readFileSync(keyDir + file, 'utf8').split('\n')[1] + suffix;
}
function monthDiff(d1, d2) {
var months;
months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
months += d2.getMonth();
return months;
}
function getFilesDescending(){
return fs.readdirSync(keyDir).sort().reverse();
}
function getMonth(date){
return ('0' + (date.getMonth()+1)).slice(-2)
}
function generateKeys(){
const fileName = getFileName();
if(!fs.existsSync(fileName)){
const diffieHell = crypto.createDiffieHellman(1024);//TODO change this value to a stronger one
diffieHell.generateKeys(base64);
fs.writeFileSync(fileName, diffieHell.getPublicKey(base64) + '\n' + diffieHell.getPrivateKey(base64));
return true;
}
return false;
}
function getFileName(){
const now = new Date();
return keyDir + now.getFullYear() + '-' + getMonth(now);
}
function toDateObject(date){
return Date.from(date) || new Date();
}
module.exports = validator;
Basically whenever the sign method is invoked, the code checks if there is a keypair fiƱe which was generated within the last 4 months and if it is not generate such a keypair and use that one. The data param is a ISO string which is returned by Date.toISOString().
I'm getting the following error: Error: error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long.
2 Questions: I'm probaly doing something really obvious wrong here, what should I do instead?
And should I dump my attempt entirely and use HTTPS certificates instead?
I'd prefer not to because this makes local testing a lot harder.

RSA signing with imported private key

I bought an SSL certificate b/c arvixe does not support self-signed ones. I have a private key in a .pem file which I'd like to use to RSA sign bank transaction parameters. So far, I have not found a way it can be done.
certificate.PrivateKey throws a Keyset not found exception. Using bouncy castle to import the private key (.pem) file works fine, right up to the point where I need to convert to RSACryptoServiceProvider. At that point, DotNetUtilities.ToRSA throws a File Not Found exception. There must be a better way to do this!!!
Here is the relevant snippet from my code:
public string SignRsa(string stringToSign)
{
var encoder = new ASCIIEncoding();
var binData = encoder.GetBytes(stringToSign);
byte[] binSignature;
if (Request.Url.OriginalString.IndexOf("localhost", StringComparison.Ordinal) < 0)
{
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates;
var signed = string.Empty;
X509Certificate2 ipCert = certificates.Find(X509FindType.FindBySubjectName, "www.ingyenpiac.com", false).OfType<X509Certificate2>().First();
RSACryptoServiceProvider rsaCsp;
if (ipCert != null)
{
AsymmetricCipherKeyPair keyPair;
using (var reader = System.IO.File.OpenText(Server.MapPath("~/App_Data/private_key.pem"))) // file containing RSA PKCS1 private key
keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPrivate = Convert.ToBase64String(serializedPrivateBytes);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
string serializedPublic = Convert.ToBase64String(serializedPublicBytes);
RsaPrivateCrtKeyParameters privateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(serializedPrivate));
RsaKeyParameters publicKey = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(serializedPublic));
var kpp = keyPair.Private as RsaPrivateCrtKeyParameters;
var ppk = DotNetUtilities.ToRSA(kpp); // <==== File not found exception!!!! WTF???
RSACryptoServiceProvider tempRcsp = (RSACryptoServiceProvider)ppk;
RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(new CspParameters(1, "Microsoft Strong Cryptographic Provider", new Guid().ToString(), new CryptoKeySecurity(), null));
rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
ipCert.PrivateKey = rcsp;
if (ipCert.Verify())
{
rsaCsp = (RSACryptoServiceProvider)ipCert.PrivateKey;
}
else
throw new ApplicationException("Certificate failed to verify.");
}
else
throw new ApplicationException("SignRsa: No certifciate found");
using (var sha = new SHA1CryptoServiceProvider())
{
binSignature = rsaCsp.SignData(binData, sha);
}
if (rsaCsp.VerifyData(binData, new SHA1CryptoServiceProvider(), binSignature))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
store.Close();
return signed;
}
return null;
}
I sure hope someone can help me with this!
With .NET 4.6 this code can get simplified a bit:
public string SignRsa(string stringToSign)
{
var signed = string.Empty;
using (var ipCert = new X509Certificate2(Server.MapPath("~/App_Data/pfxFile.pfx"), "password"))
using (var RSA = ipCert.GetRSAPrivateKey())
{
// Note, if the cert was not RSA, or had no private key, RSA
// will be null. But you didn't check it, so I won't.
var binData = System.Text.Encoding.ASCII.GetBytes(stringToSign);
byte[] binSignature = RSA.SignData(binData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
// Not sure why you want to re-verify the signature, but OK:
if (RSA.VerifyData(binData, binSignature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
return signed;
}
}
Wow! I really overcomplicated things. No need for bouncy castle, or to do contortions. The answer was quite simple, shown below:
public string SignRsa(string stringToSign)
{
ASCIIEncoding encoder;
var signed = string.Empty;
var ipCert = new X509Certificate2(Server.MapPath("~/App_Data/pfxFile.pfx"), "password");
var RSA = (RSACryptoServiceProvider)ipCert.PrivateKey;
encoder = new ASCIIEncoding();
var binData = encoder.GetBytes(stringToSign);
byte[] binSignature;
using (var sha1 = new SHA1CryptoServiceProvider())
binSignature = RSA.SignData(binData, sha1);
if (RSA.VerifyData(binData, new SHA1CryptoServiceProvider(), binSignature))
signed = BitConverter.ToString(binSignature).Replace("-", string.Empty);
return signed;
}

Resources