Refactor checkServerIdentity callback to pass in cert as an object with raw DER buffer.

This commit is contained in:
Ian Haken 2018-07-19 12:29:57 -07:00
parent ac0718883a
commit 34930310d2
3 changed files with 55 additions and 2 deletions

View File

@ -44,6 +44,7 @@
"istanbul": "^0.4.4",
"lodash": "^4.17.4",
"minimist": "^1.1.0",
"node-forge": "^0.7.5",
"poisson-process": "^0.2.1"
},
"engines": {

View File

@ -76,6 +76,31 @@ var _ = require('lodash');
* @see https://github.com/google/google-auth-library-nodejs
*/
const PEM_CERT_HEADER = "-----BEGIN CERTIFICATE-----";
const PEM_CERT_FOOTER = "-----END CERTIFICATE-----";
function wrapCheckServerIdentityCallback(callback) {
return function(hostname, cert) {
// Parse cert from pem to a version that matches the tls.checkServerIdentity
// format.
// https://nodejs.org/api/tls.html#tls_tls_checkserveridentity_hostname_cert
var pemHeaderIndex = cert.indexOf(PEM_CERT_HEADER);
if (pemHeaderIndex === -1) {
return new Error("Unable to parse certificate PEM.");
}
cert = cert.substring(pemHeaderIndex);
var pemFooterIndex = cert.indexOf(PEM_CERT_FOOTER);
if (pemFooterIndex === -1) {
return new Error("Unable to parse certificate PEM.");
}
cert = cert.substring(PEM_CERT_HEADER.length, pemFooterIndex);
var rawBuffer = new Buffer(cert.replace("\n", "").replace(" ", ""), "base64");
return callback(hostname, { raw: rawBuffer });
}
}
/**
* Create an SSL Credentials object. If using a client-side certificate, both
* the second and third arguments must be passed. Additional peer verification
@ -93,7 +118,24 @@ var _ = require('lodash');
* fails and otherwise return undefined.
* @return {grpc.credentials~ChannelCredentials} The SSL Credentials object
*/
exports.createSsl = ChannelCredentials.createSsl;
exports.createSsl = function(root_certs, private_key, cert_chain, verify_options) {
// The checkServerIdentity callback from gRPC core will receive the cert as a PEM.
// To better match the checkServerIdentity callback of Node, we wrap the callback
// to decode the PEM and populate a cert object.
if (verify_options && verify_options.checkServerIdentity) {
if (typeof verify_options.checkServerIdentity !== 'function') {
throw new TypeError("Value of checkServerIdentity must be a function.");
}
// Make a shallow clone of verify_options so our modification of the callback
// isn't reflected to the caller
var updated_verify_options = Object.assign({}, verify_options);
updated_verify_options.checkServerIdentity = wrapCheckServerIdentityCallback(
verify_options.checkServerIdentity);
arguments[3] = updated_verify_options;
}
return ChannelCredentials.createSsl.apply(this, arguments);
}
/**
* @callback grpc.credentials~metadataCallback

View File

@ -21,6 +21,7 @@
var assert = require('assert');
var fs = require('fs');
var path = require('path');
var forge = require('node-forge');
var grpc = require('..');
@ -292,7 +293,16 @@ describe('client credentials', function() {
client.unary({}, function(err, data) {
assert.ifError(err);
assert.equal(callback_host, 'foo.test.google.fr');
assert.equal(callback_cert, pem_data);
// The roundabout forge APIs for converting PEM to a node DER Buffer
var expected_der = new Buffer(forge.asn1.toDer(
forge.pki.certificateToAsn1(forge.pki.certificateFromPem(pem_data)))
.getBytes(), 'binary');
// Assert the buffers are equal by converting them to hex strings
assert.equal(callback_cert.raw.toString('hex'), expected_der.toString('hex'));
// Documented behavior of callback cert is that raw should be its only property
assert.equal(Object.keys(callback_cert).length, 1);
done();
});
});