Skip to content

Commit

Permalink
x509: finish JSON interface with extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Jun 10, 2020
1 parent 8afb4a2 commit 0416948
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 8 deletions.
11 changes: 8 additions & 3 deletions lib/encoding/asn1.js
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ class Choice extends Node {
assert(node instanceof Node);
this.node = node;
this.from(...options);
this.types = types;
}

get type() {
Expand All @@ -607,6 +608,10 @@ class Choice extends Node {
throw new Error('Unimplemented.');
}

typeToClass(type) {
return typeToClass(type);
}

getSize(extra) {
return this.node.getSize(extra);
}
Expand Down Expand Up @@ -634,7 +639,7 @@ class Choice extends Node {
if (choices.indexOf(hdr.type) === -1)
throw new Error(`Could not satisfy choice for: ${hdr.type}.`);

const Node = typeToClass(hdr.type);
const Node = this.typeToClass(hdr.type);
const el = new Node();
el.flags = this.flags;

Expand Down Expand Up @@ -680,8 +685,8 @@ class Choice extends Node {
}

fromJSON(json) {
const type = types[json.type.toUpperCase()];
const Node = typeToClass(type);
const type = this.types[json.type.toUpperCase()];
const Node = this.typeToClass(type);
this.node = new Node();
this.node.fromJSON(json.node);
this.node.flags = this.flags;
Expand Down
15 changes: 11 additions & 4 deletions lib/encoding/x509.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,23 @@ class TBSCertificate extends asn1.Sequence {
}

fromJSON(json) {
let sn = json.serialNumber;
if (typeof sn === 'string')
sn = {value: sn, negative: false};

this.version.fromJSON(json.version);
this.serialNumber.fromJSON(json.serialNumber);
this.serialNumber.fromJSON(sn);
this.signature.fromJSON(json.signature);
this.issuer.fromJSON(json.issuer);
this.validity.fromJSON(json.validity);
this.subject.fromJSON(json.subject);
this.subjectPublicKeyInfo.fromJSON(json.subjectPublicKeyInfo);
this.issuerUniqueID.fromJSON(json.issuerUniqueID);
this.subjectUniqueID.fromJSON(json.subjectUniqueID);
this.extensions.fromJSON(json.extensions);
if (json.issuerUniqueID)
this.issuerUniqueID.fromJSON(json.issuerUniqueID);
if (json.subjectUniqueID)
this.subjectUniqueID.fromJSON(json.subjectUniqueID);
if (json.extensions)
this.extensions.fromJSON(json.extensions);

return this;
}
Expand Down
116 changes: 115 additions & 1 deletion test/x509-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const certsData = fs.readFileSync(certs, 'utf8');
const certificate = Path.resolve(__dirname, 'data', 'x509', 'certificate.crt');
const certificateData = fs.readFileSync(certificate, 'utf8');

let certFromJSON;

describe('X509', function() {
if (process.env.BMOCHA_VALGRIND)
this.skip();
Expand All @@ -31,7 +33,7 @@ describe('X509', function() {
assert.bufferEqual(raw1, block.data);
});

it(`should read JSON and write JSON (${i++})`, () => {
it(`should read JSON and write JSON (${i})`, () => {
const crt1 = x509.Certificate.decode(block.data);
const json1 = crt1.getJSON();

Expand Down Expand Up @@ -73,4 +75,116 @@ describe('X509', function() {
assert(r);
});
}

it('should create a self-signed certificate using JSON', () => {
// Create key pair and get JSON for pubkey
const priv = rsa.privateKeyGenerate(2048);
const pub = rsa.publicKeyCreate(priv);
const pubJSON = rsa.publicKeyExport(pub);

// Basic details, leave out optional and more complex stuff
const json = {
version: 2,
serialNumber: 'deadbeef0101',
signature: {
algorithm: 'RSASHA256'
},
issuer: [],
validity: {
notBefore: { type: 'UTCTime', node: '2020-04-20T18:53:25Z' },
notAfter: { type: 'UTCTime', node: '2021-04-20T18:53:25Z' }
},
subject: [],
subjectPublicKeyInfo: {
algorithm: {
algorithm: 'RSAPublicKey'
},
publicKey: {
modulus: pubJSON.n,
publicExponent: pubJSON.e
}
},
extensions: [
{
extnID: 'SubjectAltName',
critical: false,
extnValue: [
{ type: 'DNSName', node: '*.bcoin.io' },
{ type: 'DNSName', node: 'bcoin.io' }
]
},
{
extnID: 'BasicConstraints',
critical: false,
extnValue: {cA: false, pathLenConstraint: 0}
}
]
};

// Create to-be-signed certificate object
const tbs = x509.TBSCertificate.fromJSON(json);

// Use helper functions for the complicated details
tbs.issuer = x509.Entity.fromJSON({
COUNTRY: 'US',
PROVINCE: 'CA',
LOCALITY: 'San Francisco',
ORGANIZATION: 'bcrypto',
ORGANIZATIONALUNIT: 'encodings',
COMMONNAME: 'bcoin.io',
EMAILADDRESS: '[email protected]'
});
tbs.subject = x509.Entity.fromJSON({
COUNTRY: 'US',
PROVINCE: 'CA',
LOCALITY: 'San Francisco',
ORGANIZATION: 'bcrypto',
ORGANIZATIONALUNIT: 'encodings',
COMMONNAME: 'bcoin.io',
EMAILADDRESS: '[email protected]'
});

// Serialize
const msg = sha256.digest(tbs.encode());

// Sign
const sig = rsa.sign('SHA256', msg, priv);

// Complete
certFromJSON = new x509.Certificate();
certFromJSON.tbsCertificate = tbs;
certFromJSON.signatureAlgorithm.fromJSON({algorithm: 'RSASHA256'});
certFromJSON.signature.fromJSON({bits: sig.length * 8, value: sig.toString('hex')});
});

it.skip('should verify with openssl', () => {
const os = require('os');
const {exec} = require('child_process');

// Write file
let tmp = Path.join(os.tmpdir(), 'bcrypto-test.crt');
fs.writeFileSync(tmp, certFromJSON.toPEM());

// Test
exec(`openssl verify -check_ss_sig ${tmp}`, (error, stdout, stderr) => {
assert(!error);
assert.strictEqual('OK\n', stdout.slice(-3));
});

// Sanity check 1: certificate produced by openssl
exec(`openssl verify -check_ss_sig ${certificate}`, (error, stdout, stderr) => {
assert(!error);
assert.strictEqual('OK\n', stdout.slice(-3));
});

// Sanity check 2: malleated signature fails verification
certFromJSON.signature.value[100]++;
tmp = Path.join(os.tmpdir(), 'bcrypto-test2.crt');
fs.writeFileSync(tmp, certFromJSON.toPEM());
exec(`openssl verify -check_ss_sig ${tmp}`, (error, stdout, stderr) => {
assert(error);
const msg = 'certificate signature failure\n';
assert.strictEqual(msg, stdout.slice(-1 * msg.length));
});
});
});

0 comments on commit 0416948

Please sign in to comment.