Browse Source

Add ext_key_usage to getPeerCertificate

v0.7.4-release
Greg Hughes 14 years ago
committed by Ryan Dahl
parent
commit
6c32e155d3
  1. 16
      src/node_crypto.cc
  2. 32
      test/fixtures/keys/Makefile
  3. 15
      test/fixtures/keys/agent4-cert.pem
  4. 10
      test/fixtures/keys/agent4-csr.pem
  5. 9
      test/fixtures/keys/agent4-key.pem
  6. 21
      test/fixtures/keys/agent4.cnf
  7. 2
      test/fixtures/keys/ca2-cert.srl
  8. 153
      test/simple/test-tls-ext-key-usage.js

16
src/node_crypto.cc

@ -30,6 +30,7 @@ static Persistent<String> valid_to_symbol;
static Persistent<String> fingerprint_symbol; static Persistent<String> fingerprint_symbol;
static Persistent<String> name_symbol; static Persistent<String> name_symbol;
static Persistent<String> version_symbol; static Persistent<String> version_symbol;
static Persistent<String> ext_key_usage_symbol;
void SecureContext::Initialize(Handle<Object> target) { void SecureContext::Initialize(Handle<Object> target) {
@ -696,6 +697,20 @@ Handle<Value> Connection::GetPeerCertificate(const Arguments& args) {
info->Set(fingerprint_symbol, String::New(fingerprint)); info->Set(fingerprint_symbol, String::New(fingerprint));
} }
STACK_OF(ASN1_OBJECT) *eku = (STACK_OF(ASN1_OBJECT) *)X509_get_ext_d2i(peer_cert, NID_ext_key_usage, NULL, NULL);
if (eku != NULL) {
Local<Array> ext_key_usage = Array::New();
for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
memset(buf, 0, sizeof(buf));
OBJ_obj2txt(buf, sizeof(buf) - 1, sk_ASN1_OBJECT_value(eku, i), 1);
ext_key_usage->Set(Integer::New(i), String::New(buf));
}
sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
info->Set(ext_key_usage_symbol, ext_key_usage);
}
X509_free(peer_cert); X509_free(peer_cert);
} }
return scope.Close(info); return scope.Close(info);
@ -2683,6 +2698,7 @@ void InitCrypto(Handle<Object> target) {
fingerprint_symbol = NODE_PSYMBOL("fingerprint"); fingerprint_symbol = NODE_PSYMBOL("fingerprint");
name_symbol = NODE_PSYMBOL("name"); name_symbol = NODE_PSYMBOL("name");
version_symbol = NODE_PSYMBOL("version"); version_symbol = NODE_PSYMBOL("version");
ext_key_usage_symbol = NODE_PSYMBOL("ext_key_usage");
} }
} // namespace crypto } // namespace crypto

32
test/fixtures/keys/Makefile

@ -1,4 +1,4 @@
all: agent1-cert.pem agent2-cert.pem agent3-cert.pem all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem
# #
@ -82,13 +82,39 @@ agent3-cert.pem: agent3-csr.pem ca2-cert.pem ca2-key.pem
agent3-verify: agent3-cert.pem ca2-cert.pem agent3-verify: agent3-cert.pem ca2-cert.pem
openssl verify -CAfile ca2-cert.pem agent3-cert.pem openssl verify -CAfile ca2-cert.pem agent3-cert.pem
#
# agent4 is signed by ca2 (client cert)
#
agent4-key.pem:
openssl genrsa -out agent4-key.pem
agent4-csr.pem: agent4.cnf agent4-key.pem
openssl req -new -config agent4.cnf -key agent4-key.pem -out agent4-csr.pem
agent4-cert.pem: agent4-csr.pem ca2-cert.pem ca2-key.pem
openssl x509 -req \
-passin "pass:password" \
-in agent4-csr.pem \
-CA ca2-cert.pem \
-CAkey ca2-key.pem \
-CAcreateserial \
-extfile agent4.cnf \
-extensions ext_key_usage \
-out agent4-cert.pem
agent4-verify: agent4-cert.pem ca2-cert.pem
openssl verify -CAfile ca2-cert.pem agent4-cert.pem
# TODO: agent on CRL # TODO: agent on CRL
clean: clean:
rm -f *.pem *.srl rm -f *.pem *.srl
test: agent1-verify agent2-verify agent3-verify test: agent1-verify agent2-verify agent3-verify agent4-verify
.PHONY: all clean test agent1-verify agent2-verify agent3-verify .PHONY: all clean test agent1-verify agent2-verify agent3-verify agent4-verify

15
test/fixtures/keys/agent4-cert.pem

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICSDCCAbGgAwIBAgIJAND4S4oV8e77MA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzANBgNVBAoTBkpveWVu
dDEQMA4GA1UECxMHTm9kZS5qczEMMAoGA1UEAxMDY2EyMSAwHgYJKoZIhvcNAQkB
FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xMTAxMjYyMzMzMjZaFw0xMTAyMjUyMzMz
MjZaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzAN
BgNVBAoTBkpveWVudDEQMA4GA1UECxMHTm9kZS5qczEPMA0GA1UEAxMGYWdlbnQ0
MSAwHgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzBcMA0GCSqGSIb3DQEB
AQUAA0sAMEgCQQDAtcMgUqWCoCMI7ACMVbykoMbXvLwNHhB1/cApRFbXUd3SgDEz
RGKrqZkcT8I1b5IlUwWVQOzN7G8LHijrb05hAgMBAAGjFzAVMBMGA1UdJQQMMAoG
CCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4GBAAjwjr91RV7xLD4j+xB4Ab0iMRx3
fIb/vizhnWOHMXHp/CuUZcm0k2/lZqlGpLIUbhuUuglol/GyMYpL0l+4usUU5ayQ
r5vOdRI5fo6WnwAlDvpLTJxN6exB3TxRqPu5WGI5t6NIDThJChpXXTuG9Auw+Lk+
p+Q6Te22clo/XeUj
-----END CERTIFICATE-----

10
test/fixtures/keys/agent4-csr.pem

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH
EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD
EwZhZ2VudDQxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ
KoZIhvcNAQEBBQADSwAwSAJBAMC1wyBSpYKgIwjsAIxVvKSgxte8vA0eEHX9wClE
VtdR3dKAMTNEYqupmRxPwjVvkiVTBZVA7M3sbwseKOtvTmECAwEAAaAlMCMGCSqG
SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB
AB8lvAXSHFf+ABRubFGuTuJse8omIJ1vRXuhY345qiObEDPkSVOj4LYUjBlE6S3V
1TVdfQLBqcLJPY8zG66fjKI=
-----END CERTIFICATE REQUEST-----

9
test/fixtures/keys/agent4-key.pem

@ -0,0 +1,9 @@
-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBAMC1wyBSpYKgIwjsAIxVvKSgxte8vA0eEHX9wClEVtdR3dKAMTNE
YqupmRxPwjVvkiVTBZVA7M3sbwseKOtvTmECAwEAAQI/EVBDN6Q1OoconqSVaAZL
7H6FXtyWCJeq4u7pVMvPAYkxe4MQOqAYmHCQlozJBOjwfpi/09KccZ7Ssi80Tc2d
AiEA3tOQX52YHptUdW5gSm4/y8dlhfita//SPkqexECYDF8CIQDdZmQtguBMvHS/
Mjk5ypRo0mU4G8ZGL7ML1q0GMFKdPwIgP/+VvNCfq1LDrEK6Z0ZJDndDonntHVLJ
iNiXxxgiU5MCIFwrKxszN9NaRTPvYZlod14n8JFqJqHDa8NK7J798PabAiEAlwke
T6UdRvxUZPDW5XRUVftcDygFvF05Hfrr8ziVc88=
-----END RSA PRIVATE KEY-----

21
test/fixtures/keys/agent4.cnf

@ -0,0 +1,21 @@
[ req ]
default_bits = 1024
days = 36500
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
[ req_distinguished_name ]
C = US
ST = CA
L = SF
O = Joyent
OU = Node.js
CN = agent4
emailAddress = ry@tinyclouds.org
[ req_attributes ]
challengePassword = A challenge password
[ ext_key_usage ]
extendedKeyUsage = clientAuth

2
test/fixtures/keys/ca2-cert.srl

@ -1 +1 @@
D0F84B8A15F1EEF9 D0F84B8A15F1EEFB

153
test/simple/test-tls-ext-key-usage.js

@ -0,0 +1,153 @@
// There is a bug with 'openssl s_server' which makes it not flush certain
// important events to stdout when done over a pipe. Therefore we skip this
// test for all openssl versions less than 1.0.0.
if (!process.versions.openssl ||
parseInt(process.versions.openssl[0]) < 1) {
console.error("Skipping due to old OpenSSL version.");
process.exit(0);
}
var common = require('../common');
var join = require('path').join;
var net = require('net');
var assert = require('assert');
var fs = require('fs');
var crypto = require('crypto');
var tls = require('tls');
var spawn = require('child_process').spawn;
// FIXME: Avoid the common PORT as this test currently hits a C-level
// assertion error with node_g. The program aborts without HUPing
// the openssl s_server thus causing many tests to fail with
// EADDRINUSE.
var PORT = common.PORT + 5;
var connections = 0;
var keyfn = join(common.fixturesDir, 'keys', 'agent4-key.pem');
var key = fs.readFileSync(keyfn).toString();
var certfn = join(common.fixturesDir, 'keys', 'agent4-cert.pem');
var cert = fs.readFileSync(certfn).toString();
var server = spawn('openssl', ['s_server',
'-accept', PORT,
'-cert', certfn,
'-key', keyfn]);
server.stdout.pipe(process.stdout);
server.stderr.pipe(process.stdout);
var state = 'WAIT-ACCEPT';
var serverStdoutBuffer = '';
server.stdout.setEncoding('utf8');
server.stdout.on('data', function(s) {
serverStdoutBuffer += s;
console.error(state);
switch (state) {
case 'WAIT-ACCEPT':
if (/ACCEPT/g.test(serverStdoutBuffer)) {
// Give s_server half a second to start up.
setTimeout(startClient, 500);
state = 'WAIT-HELLO';
}
break;
case 'WAIT-HELLO':
if (/hello/g.test(serverStdoutBuffer)) {
// End the current SSL connection and exit.
// See s_server(1ssl).
server.stdin.write('Q');
state = 'WAIT-SERVER-CLOSE';
}
break;
default:
break;
}
});
var timeout = setTimeout(function () {
server.kill();
process.exit(1);
}, 5000);
var gotWriteCallback = false;
var serverExitCode = -1;
server.on('exit', function(code) {
serverExitCode = code;
clearTimeout(timeout);
});
function startClient() {
var s = new net.Stream();
var sslcontext = crypto.createCredentials({key: key, cert: cert});
sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
var pair = tls.createSecurePair(sslcontext, false);
assert.ok(pair.encrypted.writable);
assert.ok(pair.cleartext.writable);
pair.encrypted.pipe(s);
s.pipe(pair.encrypted);
s.connect(PORT);
s.on('connect', function() {
console.log('client connected');
});
pair.on('secure', function() {
console.log('client: connected+secure!');
console.log('client pair.cleartext.getPeerCertificate(): %j',
pair.cleartext.getPeerCertificate());
// "TLS Web Client Authentication"
assert.equal(pair.cleartext.getPeerCertificate().ext_key_usage.length, 1)
assert.equal(pair.cleartext.getPeerCertificate().ext_key_usage[0], '1.3.6.1.5.5.7.3.2')
console.log('client pair.cleartext.getCipher(): %j',
pair.cleartext.getCipher());
setTimeout(function() {
pair.cleartext.write('hello\r\n', function () {
gotWriteCallback = true;
});
}, 500);
});
pair.cleartext.on('data', function(d) {
console.log('cleartext: %s', d.toString());
});
s.on('close', function() {
console.log('client close');
});
pair.encrypted.on('error', function(err) {
console.log('encrypted error: ' + err);
});
s.on('error', function(err) {
console.log('socket error: ' + err);
});
pair.on('error', function(err) {
console.log('secure error: ' + err);
});
}
process.on('exit', function() {
assert.equal(0, serverExitCode);
assert.equal('WAIT-SERVER-CLOSE', state);
assert.ok(gotWriteCallback);
});
Loading…
Cancel
Save