crypto: always accept private keys as public keys
Some APIs already accept private keys instead of public keys. This changes all relevant crypto APIs to do so. PR-URL: https://github.com/nodejs/node/pull/25217 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
This commit is contained in:
parent
27a03b84c4
commit
ae2d1f0e05
@ -1379,6 +1379,9 @@ This can be called many times with new data as it is streamed.
|
|||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.92
|
added: v0.1.92
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/25217
|
||||||
|
description: The key can now be a private key.
|
||||||
- version: v8.0.0
|
- version: v8.0.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/11705
|
pr-url: https://github.com/nodejs/node/pull/11705
|
||||||
description: Support for RSASSA-PSS and additional options was added.
|
description: Support for RSASSA-PSS and additional options was added.
|
||||||
@ -1419,6 +1422,9 @@ The `verify` object can not be used again after `verify.verify()` has been
|
|||||||
called. Multiple calls to `verify.verify()` will result in an error being
|
called. Multiple calls to `verify.verify()` will result in an error being
|
||||||
thrown.
|
thrown.
|
||||||
|
|
||||||
|
Because public keys can be derived from private keys, a private key may
|
||||||
|
be passed instead of a public key.
|
||||||
|
|
||||||
## `crypto` module methods and properties
|
## `crypto` module methods and properties
|
||||||
|
|
||||||
### crypto.constants
|
### crypto.constants
|
||||||
@ -1829,6 +1835,10 @@ must be an object with the properties described above.
|
|||||||
### crypto.createPublicKey(key)
|
### crypto.createPublicKey(key)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v11.6.0
|
added: v11.6.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/25217
|
||||||
|
description: The `key` argument can now be a private key.
|
||||||
-->
|
-->
|
||||||
* `key` {Object | string | Buffer}
|
* `key` {Object | string | Buffer}
|
||||||
- `key`: {string | Buffer}
|
- `key`: {string | Buffer}
|
||||||
@ -1843,6 +1853,12 @@ must be an object with the properties described above.
|
|||||||
|
|
||||||
If the format is `'pem'`, the `'key'` may also be an X.509 certificate.
|
If the format is `'pem'`, the `'key'` may also be an X.509 certificate.
|
||||||
|
|
||||||
|
Because public keys can be derived from private keys, a private key may be
|
||||||
|
passed instead of a public key. In that case, this function behaves as if
|
||||||
|
[`crypto.createPrivateKey()`][] had been called, except that the type of the
|
||||||
|
returned `KeyObject` will be `public` and that the private key cannot be
|
||||||
|
extracted from the returned `KeyObject`.
|
||||||
|
|
||||||
### crypto.createSecretKey(key)
|
### crypto.createSecretKey(key)
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v11.6.0
|
added: v11.6.0
|
||||||
|
@ -261,10 +261,6 @@ function prepareAsymmetricKey(key, isPublic, allowKeyObject = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function preparePublicKey(key, allowKeyObject) {
|
|
||||||
return prepareAsymmetricKey(key, true, allowKeyObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
function preparePrivateKey(key, allowKeyObject) {
|
function preparePrivateKey(key, allowKeyObject) {
|
||||||
return prepareAsymmetricKey(key, false, allowKeyObject);
|
return prepareAsymmetricKey(key, false, allowKeyObject);
|
||||||
}
|
}
|
||||||
@ -300,7 +296,7 @@ function createSecretKey(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createPublicKey(key) {
|
function createPublicKey(key) {
|
||||||
const { format, type, data } = preparePublicKey(key, false);
|
const { format, type, data } = preparePublicOrPrivateKey(key, false);
|
||||||
const handle = new KeyObjectHandle(kKeyTypePublic);
|
const handle = new KeyObjectHandle(kKeyTypePublic);
|
||||||
handle.init(data, format, type);
|
handle.init(data, format, type);
|
||||||
return new PublicKeyObject(handle);
|
return new PublicKeyObject(handle);
|
||||||
@ -326,7 +322,6 @@ module.exports = {
|
|||||||
// These are designed for internal use only and should not be exposed.
|
// These are designed for internal use only and should not be exposed.
|
||||||
parsePublicKeyEncoding,
|
parsePublicKeyEncoding,
|
||||||
parsePrivateKeyEncoding,
|
parsePrivateKeyEncoding,
|
||||||
preparePublicKey,
|
|
||||||
preparePrivateKey,
|
preparePrivateKey,
|
||||||
preparePublicOrPrivateKey,
|
preparePublicOrPrivateKey,
|
||||||
prepareSecretKey,
|
prepareSecretKey,
|
||||||
|
@ -19,7 +19,7 @@ const {
|
|||||||
} = require('internal/crypto/util');
|
} = require('internal/crypto/util');
|
||||||
const {
|
const {
|
||||||
preparePrivateKey,
|
preparePrivateKey,
|
||||||
preparePublicKey
|
preparePublicOrPrivateKey
|
||||||
} = require('internal/crypto/keys');
|
} = require('internal/crypto/keys');
|
||||||
const { Writable } = require('stream');
|
const { Writable } = require('stream');
|
||||||
|
|
||||||
@ -112,8 +112,9 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) {
|
|||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
format,
|
format,
|
||||||
type
|
type,
|
||||||
} = preparePublicKey(options, true);
|
passphrase
|
||||||
|
} = preparePublicOrPrivateKey(options, true);
|
||||||
|
|
||||||
sigEncoding = sigEncoding || getDefaultEncoding();
|
sigEncoding = sigEncoding || getDefaultEncoding();
|
||||||
|
|
||||||
@ -125,7 +126,7 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) {
|
|||||||
signature = validateArrayBufferView(toBuf(signature, sigEncoding),
|
signature = validateArrayBufferView(toBuf(signature, sigEncoding),
|
||||||
'signature');
|
'signature');
|
||||||
|
|
||||||
return this[kHandle].verify(data, format, type, signature,
|
return this[kHandle].verify(data, format, type, passphrase, signature,
|
||||||
rsaPadding, pssSaltLength);
|
rsaPadding, pssSaltLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3012,30 +3012,6 @@ static PublicKeyEncodingConfig GetPublicKeyEncodingFromJs(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ManagedEVPPKey GetPublicKeyFromJs(
|
|
||||||
const FunctionCallbackInfo<Value>& args,
|
|
||||||
unsigned int* offset,
|
|
||||||
bool allow_key_object) {
|
|
||||||
if (args[*offset]->IsString() || Buffer::HasInstance(args[*offset])) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
ByteSource key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]);
|
|
||||||
PublicKeyEncodingConfig config =
|
|
||||||
GetPublicKeyEncodingFromJs(args, offset, kKeyContextInput);
|
|
||||||
EVPKeyPointer pkey;
|
|
||||||
ParsePublicKey(&pkey, config, key.get(), key.size());
|
|
||||||
if (!pkey)
|
|
||||||
ThrowCryptoError(env, ERR_get_error(), "Failed to read public key");
|
|
||||||
return ManagedEVPPKey(pkey.release());
|
|
||||||
} else {
|
|
||||||
CHECK(args[*offset]->IsObject() && allow_key_object);
|
|
||||||
KeyObject* key;
|
|
||||||
ASSIGN_OR_RETURN_UNWRAP(&key, args[*offset].As<Object>(), ManagedEVPPKey());
|
|
||||||
CHECK_EQ(key->GetKeyType(), kKeyTypePublic);
|
|
||||||
(*offset) += 3;
|
|
||||||
return key->GetAsymmetricKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs(
|
static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs(
|
||||||
const FunctionCallbackInfo<Value>& args,
|
const FunctionCallbackInfo<Value>& args,
|
||||||
unsigned int* offset,
|
unsigned int* offset,
|
||||||
@ -3397,7 +3373,7 @@ void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
|
|||||||
CHECK_EQ(args.Length(), 3);
|
CHECK_EQ(args.Length(), 3);
|
||||||
|
|
||||||
offset = 0;
|
offset = 0;
|
||||||
pkey = GetPublicKeyFromJs(args, &offset, false);
|
pkey = GetPublicOrPrivateKeyFromJs(args, &offset, false);
|
||||||
if (!pkey)
|
if (!pkey)
|
||||||
return;
|
return;
|
||||||
key->InitPublic(pkey);
|
key->InitPublic(pkey);
|
||||||
@ -4679,7 +4655,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
|
|||||||
ASSIGN_OR_RETURN_UNWRAP(&verify, args.Holder());
|
ASSIGN_OR_RETURN_UNWRAP(&verify, args.Holder());
|
||||||
|
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
ManagedEVPPKey pkey = GetPublicKeyFromJs(args, &offset, true);
|
ManagedEVPPKey pkey = GetPublicOrPrivateKeyFromJs(args, &offset, true);
|
||||||
|
|
||||||
char* hbuf = Buffer::Data(args[offset]);
|
char* hbuf = Buffer::Data(args[offset]);
|
||||||
ssize_t hlen = Buffer::Length(args[offset]);
|
ssize_t hlen = Buffer::Length(args[offset]);
|
||||||
|
@ -31,20 +31,24 @@ function assertApproximateSize(key, expectedSize) {
|
|||||||
function testEncryptDecrypt(publicKey, privateKey) {
|
function testEncryptDecrypt(publicKey, privateKey) {
|
||||||
const message = 'Hello Node.js world!';
|
const message = 'Hello Node.js world!';
|
||||||
const plaintext = Buffer.from(message, 'utf8');
|
const plaintext = Buffer.from(message, 'utf8');
|
||||||
const ciphertext = publicEncrypt(publicKey, plaintext);
|
for (const key of [publicKey, privateKey]) {
|
||||||
|
const ciphertext = publicEncrypt(key, plaintext);
|
||||||
const received = privateDecrypt(privateKey, ciphertext);
|
const received = privateDecrypt(privateKey, ciphertext);
|
||||||
assert.strictEqual(received.toString('utf8'), message);
|
assert.strictEqual(received.toString('utf8'), message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that a key pair can be used for signing / verification.
|
// Tests that a key pair can be used for signing / verification.
|
||||||
function testSignVerify(publicKey, privateKey) {
|
function testSignVerify(publicKey, privateKey) {
|
||||||
const message = 'Hello Node.js world!';
|
const message = 'Hello Node.js world!';
|
||||||
const signature = createSign('SHA256').update(message)
|
const signature = createSign('SHA256').update(message)
|
||||||
.sign(privateKey, 'hex');
|
.sign(privateKey, 'hex');
|
||||||
|
for (const key of [publicKey, privateKey]) {
|
||||||
const okay = createVerify('SHA256').update(message)
|
const okay = createVerify('SHA256').update(message)
|
||||||
.verify(publicKey, signature, 'hex');
|
.verify(key, signature, 'hex');
|
||||||
assert(okay);
|
assert(okay);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Constructs a regular expression for a PEM-encoded key with the given label.
|
// Constructs a regular expression for a PEM-encoded key with the given label.
|
||||||
function getRegExpForPEM(label, cipher) {
|
function getRegExpForPEM(label, cipher) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user