mirror of
https://github.com/nodejs/node.git
synced 2025-05-14 21:30:13 +00:00
crypto: add SPKAC support
Implements new class 'Certificate' within crypto object for working with SPKAC's (signed public key & challenge) natively.
This commit is contained in:
parent
a555992d5e
commit
7bf46ba4ce
@ -573,6 +573,30 @@ function pbkdf2(password, salt, iterations, keylen, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.Certificate = Certificate;
|
||||||
|
|
||||||
|
function Certificate() {
|
||||||
|
if (!(this instanceof Certificate))
|
||||||
|
return new Certificate();
|
||||||
|
|
||||||
|
this._binding = new binding.Certificate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Certificate.prototype.verifySpkac = function(object) {
|
||||||
|
return this._binding.verifySpkac(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Certificate.prototype.exportPublicKey = function(object, encoding) {
|
||||||
|
return this._binding.exportPublicKey(toBuf(object, encoding));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Certificate.prototype.exportChallenge = function(object, encoding) {
|
||||||
|
return this._binding.exportChallenge(toBuf(object, encoding));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.randomBytes = randomBytes;
|
exports.randomBytes = randomBytes;
|
||||||
exports.pseudoRandomBytes = pseudoRandomBytes;
|
exports.pseudoRandomBytes = pseudoRandomBytes;
|
||||||
|
@ -3581,6 +3581,190 @@ void GetHashes(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Certificate::Initialize(Handle<Object> target) {
|
||||||
|
HandleScope scope(node_isolate);
|
||||||
|
|
||||||
|
Local<FunctionTemplate> t = FunctionTemplate::New(New);
|
||||||
|
|
||||||
|
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
|
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "verifySpkac", VerifySpkac);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "exportPublicKey", ExportPublicKey);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "exportChallenge", ExportChallenge);
|
||||||
|
|
||||||
|
target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Certificate"),
|
||||||
|
t->GetFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Certificate::New(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
new Certificate(args.GetIsolate(), args.This());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Certificate::VerifySpkac(const char* data, unsigned int len) {
|
||||||
|
bool i = 0;
|
||||||
|
EVP_PKEY* pkey = NULL;
|
||||||
|
NETSCAPE_SPKI* spki = NULL;
|
||||||
|
|
||||||
|
spki = NETSCAPE_SPKI_b64_decode(data, len);
|
||||||
|
if (spki == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
pkey = X509_PUBKEY_get(spki->spkac->pubkey);
|
||||||
|
if (pkey == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
i = NETSCAPE_SPKI_verify(spki, pkey) > 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (pkey != NULL)
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
|
||||||
|
if (spki != NULL)
|
||||||
|
NETSCAPE_SPKI_free(spki);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Certificate::VerifySpkac(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
HandleScope scope(node_isolate);
|
||||||
|
|
||||||
|
Certificate* certificate = WeakObject::Unwrap<Certificate>(args.This());
|
||||||
|
bool i = false;
|
||||||
|
|
||||||
|
if (args.Length() < 1)
|
||||||
|
return ThrowTypeError("Missing argument");
|
||||||
|
|
||||||
|
ASSERT_IS_BUFFER(args[0]);
|
||||||
|
|
||||||
|
size_t length = Buffer::Length(args[0]);
|
||||||
|
if (length == 0)
|
||||||
|
return args.GetReturnValue().Set(i);
|
||||||
|
|
||||||
|
char* data = Buffer::Data(args[0]);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
i = certificate->VerifySpkac(data, length) > 0;
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* Certificate::ExportPublicKey(const char* data, int len) {
|
||||||
|
char* buf = NULL;
|
||||||
|
EVP_PKEY* pkey = NULL;
|
||||||
|
NETSCAPE_SPKI* spki = NULL;
|
||||||
|
|
||||||
|
BIO* bio = BIO_new(BIO_s_mem());
|
||||||
|
if (bio == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
spki = NETSCAPE_SPKI_b64_decode(data, len);
|
||||||
|
if (spki == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
pkey = NETSCAPE_SPKI_get_pubkey(spki);
|
||||||
|
if (pkey == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
if (PEM_write_bio_PUBKEY(bio, pkey) <= 0)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
BIO_write(bio, "\0", 1);
|
||||||
|
BUF_MEM* ptr;
|
||||||
|
BIO_get_mem_ptr(bio, &ptr);
|
||||||
|
|
||||||
|
buf = new char[ptr->length];
|
||||||
|
memcpy(buf, ptr->data, ptr->length);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (pkey != NULL)
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
|
||||||
|
if (spki != NULL)
|
||||||
|
NETSCAPE_SPKI_free(spki);
|
||||||
|
|
||||||
|
if (bio != NULL)
|
||||||
|
BIO_free_all(bio);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Certificate::ExportPublicKey(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
HandleScope scope(node_isolate);
|
||||||
|
|
||||||
|
Certificate* certificate = WeakObject::Unwrap<Certificate>(args.This());
|
||||||
|
|
||||||
|
if (args.Length() < 1)
|
||||||
|
return ThrowTypeError("Missing argument");
|
||||||
|
|
||||||
|
ASSERT_IS_BUFFER(args[0]);
|
||||||
|
|
||||||
|
size_t length = Buffer::Length(args[0]);
|
||||||
|
if (length == 0)
|
||||||
|
return args.GetReturnValue().SetEmptyString();
|
||||||
|
|
||||||
|
char* data = Buffer::Data(args[0]);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
const char* pkey = certificate->ExportPublicKey(data, length);
|
||||||
|
if (pkey == NULL)
|
||||||
|
return args.GetReturnValue().SetEmptyString();
|
||||||
|
|
||||||
|
Local<Value> out = Encode(pkey, strlen(pkey), BUFFER);
|
||||||
|
|
||||||
|
delete[] pkey;
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* Certificate::ExportChallenge(const char* data, int len) {
|
||||||
|
NETSCAPE_SPKI* sp = NULL;
|
||||||
|
|
||||||
|
sp = NETSCAPE_SPKI_b64_decode(data, len);
|
||||||
|
if (sp == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
const char* buf = NULL;
|
||||||
|
buf = reinterpret_cast<const char*>(ASN1_STRING_data(sp->spkac->challenge));
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Certificate::ExportChallenge(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
HandleScope scope(node_isolate);
|
||||||
|
|
||||||
|
Certificate* crt = WeakObject::Unwrap<Certificate>(args.This());
|
||||||
|
|
||||||
|
if (args.Length() < 1)
|
||||||
|
return ThrowTypeError("Missing argument");
|
||||||
|
|
||||||
|
ASSERT_IS_BUFFER(args[0]);
|
||||||
|
|
||||||
|
size_t len = Buffer::Length(args[0]);
|
||||||
|
if (len == 0)
|
||||||
|
return args.GetReturnValue().SetEmptyString();
|
||||||
|
|
||||||
|
char* data = Buffer::Data(args[0]);
|
||||||
|
assert(data != NULL);
|
||||||
|
|
||||||
|
const char* cert = crt->ExportChallenge(data, len);
|
||||||
|
if (cert == NULL)
|
||||||
|
return args.GetReturnValue().SetEmptyString();
|
||||||
|
|
||||||
|
Local<Value> outString = Encode(cert, strlen(cert), BUFFER);
|
||||||
|
|
||||||
|
delete[] cert;
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(outString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void InitCryptoOnce() {
|
void InitCryptoOnce() {
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
OpenSSL_add_all_algorithms();
|
OpenSSL_add_all_algorithms();
|
||||||
@ -3621,6 +3805,7 @@ void InitCrypto(Handle<Object> target,
|
|||||||
Hash::Initialize(env, target);
|
Hash::Initialize(env, target);
|
||||||
Sign::Initialize(env, target);
|
Sign::Initialize(env, target);
|
||||||
Verify::Initialize(env, target);
|
Verify::Initialize(env, target);
|
||||||
|
Certificate::Initialize(target);
|
||||||
|
|
||||||
NODE_SET_METHOD(target, "PBKDF2", PBKDF2);
|
NODE_SET_METHOD(target, "PBKDF2", PBKDF2);
|
||||||
NODE_SET_METHOD(target, "randomBytes", RandomBytes<false>);
|
NODE_SET_METHOD(target, "randomBytes", RandomBytes<false>);
|
||||||
|
@ -533,6 +533,26 @@ class DiffieHellman : public WeakObject {
|
|||||||
DH* dh;
|
DH* dh;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Certificate : public WeakObject {
|
||||||
|
public:
|
||||||
|
static void Initialize(v8::Handle<v8::Object> target);
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> CertificateInit(const char* sign_type);
|
||||||
|
bool VerifySpkac(const char* data, unsigned int len);
|
||||||
|
const char* ExportPublicKey(const char* data, int len);
|
||||||
|
const char* ExportChallenge(const char* data, int len);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void VerifySpkac(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void ExportPublicKey(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void ExportChallenge(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
|
||||||
|
Certificate(v8::Isolate* isolate, v8::Local<v8::Object> wrap)
|
||||||
|
: WeakObject(isolate, wrap) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool EntropySource(unsigned char* buffer, size_t length);
|
bool EntropySource(unsigned char* buffer, size_t length);
|
||||||
void InitCrypto(v8::Handle<v8::Object> target);
|
void InitCrypto(v8::Handle<v8::Object> target);
|
||||||
|
|
||||||
|
1
test/fixtures/spkac.fail
vendored
Normal file
1
test/fixtures/spkac.fail
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
MIIB/FAILDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkZmI5YWI4MTQtNjY3Ny00MmE0LWE2MGMtZjkwNWQxYTY5MjRkMA0GCSqGSIb3DQEBBQUAA4GBADu1U9t3eY9O3WOofp1RHX2rkh0TPs1CeS+sNdWUSDmdV5ifaGdeXpDikEnh4QIUIeZehxwgy2EjiZqMjMJHF++KPTzfAnHuuEtpDmIGzBnodZa1qt322iGZwgREvacv78GxJBJvPP3KLe+EDDsERG1aWLJoSZRusadacdzNmdV4
|
6
test/fixtures/spkac.pem
vendored
Normal file
6
test/fixtures/spkac.pem
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcvQh9SKOPv4DwI8LwSaFx02h7
|
||||||
|
l9QCiDs6sF2GfsSTEUG61SnjQ/v4uJiLKBgbVOagj9rkSCwtTez23ATPeGaBj2Zg
|
||||||
|
ipv+tv5IXyqUP8ropXJQ5ELtaXPUN/gvw7cO5EbPHr/7eMhbpw8Gl+AfWxW5hLW8
|
||||||
|
MGw/+AwwjHBOwong/QIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
1
test/fixtures/spkac.valid
vendored
Normal file
1
test/fixtures/spkac.valid
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkZmI5YWI4MTQtNjY3Ny00MmE0LWE2MGMtZjkwNWQxYTY5MjRkMA0GCSqGSIb3DQEBBQUAA4GBADu1U9t3eY9O3WOofp1RHX2rkh0TPs1CeS+sNdWUSDmdV5ifaGdeXpDikEnh4QIUIeZehxwgy2EjiZqMjMJHF++KPTzfAnHuuEtpDmIGzBnodZa1qt322iGZwgREvacv78GxJBJvPP3KLe+EDDsERG1aWLJoSZRusadacdzNmdV4
|
54
test/simple/test-crypto-certificate.js
Normal file
54
test/simple/test-crypto-certificate.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||||
|
// persons to whom the Software is furnished to do so, subject to the
|
||||||
|
// following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included
|
||||||
|
// in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||||
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||||
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
var common = require('../common');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
crypto.DEFAULT_ENCODING = 'buffer';
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
// Test Certificates
|
||||||
|
var spkacValid = fs.readFileSync(common.fixturesDir + '/spkac.valid');
|
||||||
|
var spkacFail = fs.readFileSync(common.fixturesDir + '/spkac.fail');
|
||||||
|
var spkacPem = fs.readFileSync(common.fixturesDir + '/spkac.pem');
|
||||||
|
|
||||||
|
var certificate = new crypto.Certificate();
|
||||||
|
|
||||||
|
assert.equal(certificate.verifySpkac(spkacValid), true);
|
||||||
|
assert.equal(certificate.verifySpkac(spkacFail), false);
|
||||||
|
|
||||||
|
assert.equal(stripLineEndings(certificate.exportPublicKey(spkacValid)
|
||||||
|
.toString('utf8')),
|
||||||
|
stripLineEndings(spkacPem.toString('utf8')));
|
||||||
|
assert.equal(certificate.exportPublicKey(spkacFail), '');
|
||||||
|
|
||||||
|
assert.equal(certificate.exportChallenge(spkacValid)
|
||||||
|
.toString('utf8'),
|
||||||
|
'fb9ab814-6677-42a4-a60c-f905d1a6924d');
|
||||||
|
assert.equal(certificate.exportChallenge(spkacFail), '');
|
||||||
|
|
||||||
|
function stripLineEndings(obj) {
|
||||||
|
return obj.replace(/\n/g, '');
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user