crypto: add support for RSA-PSS keys

This commit adds support for RSA-PSS keys, including
- KeyObjects of type rsa-pss,
- key pair generation for RSA-PSS, and
- signing and verification using RSA-PSS keys.

PR-URL: https://github.com/nodejs/node/pull/26960
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
This commit is contained in:
Tobias Nießen 2019-03-16 23:51:26 +01:00 committed by Daniel Bevenius
parent d834275a48
commit 969bd1eb7b
16 changed files with 535 additions and 89 deletions

View File

@ -1129,6 +1129,9 @@ passing keys as strings or `Buffer`s due to improved security features.
<!-- YAML
added: v11.6.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/26960
description: Added support for `'rsa-pss'`
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/26786
description: This property now returns `undefined` for KeyObject
@ -1142,8 +1145,18 @@ changes:
-->
* {string}
For asymmetric keys, this property represents the type of the embedded key
(`'rsa'`, `'dsa'`, `'ec'`, `'ed25519'`, `'ed448'`, `'x25519'` or `'x448'`).
For asymmetric keys, this property represents the type of the key. Supported key
types are:
* `'rsa'` (OID 1.2.840.113549.1.1.1)
* `'rsa-pss'` (OID 1.2.840.113549.1.1.10)
* `'dsa'` (OID 1.2.840.10040.4.1)
* `'ec'` (OID 1.2.840.10045.2.1)
* `'x25519'` (OID 1.3.101.110)
* `'x448'` (OID 1.3.101.111)
* `'ed25519'` (OID 1.3.101.112)
* `'ed448'` (OID 1.3.101.113)
This property is `undefined` for unrecognized `KeyObject` types and symmetric
keys.
@ -1271,6 +1284,9 @@ console.log(verify.verify(publicKey, signature));
<!-- YAML
added: v0.1.92
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/26960
description: This function now supports RSA-PSS keys.
- version: v11.6.0
pr-url: https://github.com/nodejs/node/pull/24234
description: This function now supports key objects.
@ -1296,7 +1312,9 @@ object, the following additional properties can be passed:
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function
used to sign the message as specified in section 3.1 of [RFC 4055][].
used to sign the message as specified in section 3.1 of [RFC 4055][], unless
an MGF1 hash function has been specified as part of the key in compliance with
section 3.3 of [RFC 4055][].
* `saltLength`: {integer} - salt length for when padding is
`RSA_PKCS1_PSS_PADDING`. The special value
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
@ -1369,6 +1387,9 @@ This can be called many times with new data as it is streamed.
<!-- YAML
added: v0.1.92
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/26960
description: This function now supports RSA-PSS keys.
- version: v11.7.0
pr-url: https://github.com/nodejs/node/pull/25217
description: The key can now be a private key.
@ -1395,7 +1416,9 @@ object, the following additional properties can be passed:
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function
used to verify the message as specified in section 3.1 of [RFC 4055][].
used to verify the message as specified in section 3.1 of [RFC 4055][], unless
an MGF1 hash function has been specified as part of the key in compliance with
section 3.3 of [RFC 4055][].
* `saltLength`: {integer} - salt length for when padding is
`RSA_PKCS1_PSS_PADDING`. The special value
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest

View File

@ -3,6 +3,7 @@
const { AsyncWrap, Providers } = internalBinding('async_wrap');
const {
generateKeyPairRSA,
generateKeyPairRSAPSS,
generateKeyPairDSA,
generateKeyPairEC,
generateKeyPairNid,
@ -141,6 +142,7 @@ function check(type, options, callback) {
let impl;
switch (type) {
case 'rsa':
case 'rsa-pss':
{
const { modulusLength } = needOptions();
if (!isUint32(modulusLength))
@ -153,10 +155,27 @@ function check(type, options, callback) {
throw new ERR_INVALID_OPT_VALUE('publicExponent', publicExponent);
}
if (type === 'rsa') {
impl = (wrap) => generateKeyPairRSA(modulusLength, publicExponent,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
break;
}
const { hash, mgf1Hash, saltLength } = options;
if (hash !== undefined && typeof hash !== 'string')
throw new ERR_INVALID_OPT_VALUE('hash', hash);
if (mgf1Hash !== undefined && typeof mgf1Hash !== 'string')
throw new ERR_INVALID_OPT_VALUE('mgf1Hash', mgf1Hash);
if (saltLength !== undefined && !isUint32(saltLength))
throw new ERR_INVALID_OPT_VALUE('saltLength', saltLength);
impl = (wrap) => generateKeyPairRSAPSS(modulusLength, publicExponent,
hash, mgf1Hash, saltLength,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
case 'dsa':
@ -225,8 +244,7 @@ function check(type, options, callback) {
break;
default:
throw new ERR_INVALID_ARG_VALUE('type', type,
"must be one of 'rsa', 'dsa', 'ec', " +
"'ed25519', 'ed448', 'x25519', 'x448'");
'must be a supported key type');
}
if (options) {

View File

@ -12,10 +12,6 @@ const {
signOneShot: _signOneShot,
verifyOneShot: _verifyOneShot
} = internalBinding('crypto');
const {
RSA_PSS_SALTLEN_AUTO,
RSA_PKCS1_PADDING
} = internalBinding('constants').crypto;
const {
getDefaultEncoding,
kHandle,
@ -56,14 +52,14 @@ Sign.prototype.update = function update(data, encoding) {
};
function getPadding(options) {
return getIntOption('padding', RSA_PKCS1_PADDING, options);
return getIntOption('padding', options);
}
function getSaltLength(options) {
return getIntOption('saltLength', RSA_PSS_SALTLEN_AUTO, options);
return getIntOption('saltLength', options);
}
function getIntOption(name, defaultValue, options) {
function getIntOption(name, options) {
const value = options[name];
if (value !== undefined) {
if (value === value >> 0) {
@ -72,7 +68,7 @@ function getIntOption(name, defaultValue, options) {
throw new ERR_INVALID_OPT_VALUE(name, value);
}
}
return defaultValue;
return undefined;
}
Sign.prototype.sign = function sign(options, encoding) {

View File

@ -161,6 +161,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
V(crypto_x25519_string, "x25519") \
V(crypto_x448_string, "x448") \
V(crypto_rsa_string, "rsa") \
V(crypto_rsa_pss_string, "rsa-pss") \
V(cwd_string, "cwd") \
V(data_string, "data") \
V(dest_string, "dest") \

View File

@ -3690,6 +3690,8 @@ Local<Value> KeyObject::GetAsymmetricKeyType() const {
switch (EVP_PKEY_id(this->asymmetric_key_.get())) {
case EVP_PKEY_RSA:
return env()->crypto_rsa_string();
case EVP_PKEY_RSA_PSS:
return env()->crypto_rsa_pss_string();
case EVP_PKEY_DSA:
return env()->crypto_dsa_string();
case EVP_PKEY_EC:
@ -4684,13 +4686,14 @@ void SignBase::CheckThrow(SignBase::Error error) {
static bool ApplyRSAOptions(const ManagedEVPPKey& pkey,
EVP_PKEY_CTX* pkctx,
int padding,
int salt_len) {
const Maybe<int>& salt_len) {
if (EVP_PKEY_id(pkey.get()) == EVP_PKEY_RSA ||
EVP_PKEY_id(pkey.get()) == EVP_PKEY_RSA2) {
EVP_PKEY_id(pkey.get()) == EVP_PKEY_RSA2 ||
EVP_PKEY_id(pkey.get()) == EVP_PKEY_RSA_PSS) {
if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0)
return false;
if (padding == RSA_PKCS1_PSS_PADDING) {
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len) <= 0)
if (padding == RSA_PKCS1_PSS_PADDING && salt_len.IsJust()) {
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len.FromJust()) <= 0)
return false;
}
}
@ -4741,11 +4744,16 @@ void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
sign->CheckThrow(err);
}
static int GetDefaultSignPadding(const ManagedEVPPKey& key) {
return EVP_PKEY_id(key.get()) == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING :
RSA_PKCS1_PADDING;
}
static AllocatedBuffer Node_SignFinal(Environment* env,
EVPMDPointer&& mdctx,
const ManagedEVPPKey& pkey,
int padding,
int pss_salt_len) {
Maybe<int> pss_salt_len) {
unsigned char m[EVP_MAX_MD_SIZE];
unsigned int m_len;
@ -4778,7 +4786,7 @@ static AllocatedBuffer Node_SignFinal(Environment* env,
Sign::SignResult Sign::SignFinal(
const ManagedEVPPKey& pkey,
int padding,
int salt_len) {
const Maybe<int>& salt_len) {
if (!mdctx_)
return SignResult(kSignNotInitialised);
@ -4829,11 +4837,17 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
if (!key)
return;
int padding = GetDefaultSignPadding(key);
if (!args[offset]->IsUndefined()) {
CHECK(args[offset]->IsInt32());
int padding = args[offset].As<Int32>()->Value();
padding = args[offset].As<Int32>()->Value();
}
Maybe<int> salt_len = Nothing<int>();
if (!args[offset + 1]->IsUndefined()) {
CHECK(args[offset + 1]->IsInt32());
int salt_len = args[offset + 1].As<Int32>()->Value();
salt_len = Just<int>(args[offset + 1].As<Int32>()->Value());
}
SignResult ret = sign->SignFinal(
key,
@ -4894,11 +4908,17 @@ void SignOneShot(const FunctionCallbackInfo<Value>& args) {
return CheckThrow(env, SignBase::Error::kSignUnknownDigest);
}
int rsa_padding = GetDefaultSignPadding(key);
if (!args[offset + 2]->IsUndefined()) {
CHECK(args[offset + 2]->IsInt32());
int rsa_padding = args[offset + 2].As<Int32>()->Value();
rsa_padding = args[offset + 2].As<Int32>()->Value();
}
Maybe<int> rsa_salt_len = Nothing<int>();
if (!args[offset + 3]->IsUndefined()) {
CHECK(args[offset + 3]->IsInt32());
int rsa_salt_len = args[offset + 3].As<Int32>()->Value();
rsa_salt_len = Just<int>(args[offset + 3].As<Int32>()->Value());
}
EVP_PKEY_CTX* pkctx = nullptr;
EVPMDPointer mdctx(EVP_MD_CTX_new());
@ -4976,7 +4996,7 @@ SignBase::Error Verify::VerifyFinal(const ManagedEVPPKey& pkey,
const char* sig,
int siglen,
int padding,
int saltlen,
const Maybe<int>& saltlen,
bool* verify_result) {
if (!mdctx_)
return kSignNotInitialised;
@ -5020,11 +5040,17 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
ArrayBufferViewContents<char> hbuf(args[offset]);
int padding = GetDefaultSignPadding(pkey);
if (!args[offset + 1]->IsUndefined()) {
CHECK(args[offset + 1]->IsInt32());
int padding = args[offset + 1].As<Int32>()->Value();
padding = args[offset + 1].As<Int32>()->Value();
}
Maybe<int> salt_len = Nothing<int>();
if (!args[offset + 2]->IsUndefined()) {
CHECK(args[offset + 2]->IsInt32());
int salt_len = args[offset + 2].As<Int32>()->Value();
salt_len = Just<int>(args[offset + 2].As<Int32>()->Value());
}
bool verify_result;
Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding,
@ -5057,11 +5083,17 @@ void VerifyOneShot(const FunctionCallbackInfo<Value>& args) {
return CheckThrow(env, SignBase::Error::kSignUnknownDigest);
}
int rsa_padding = GetDefaultSignPadding(key);
if (!args[offset + 3]->IsUndefined()) {
CHECK(args[offset + 3]->IsInt32());
int rsa_padding = args[offset + 3].As<Int32>()->Value();
rsa_padding = args[offset + 3].As<Int32>()->Value();
}
Maybe<int> rsa_salt_len = Nothing<int>();
if (!args[offset + 4]->IsUndefined()) {
CHECK(args[offset + 4]->IsInt32());
int rsa_salt_len = args[offset + 4].As<Int32>()->Value();
rsa_salt_len = Just<int>(args[offset + 4].As<Int32>()->Value());
}
EVP_PKEY_CTX* pkctx = nullptr;
EVPMDPointer mdctx(EVP_MD_CTX_new());
@ -6064,6 +6096,48 @@ class RSAKeyPairGenerationConfig : public KeyPairGenerationConfig {
const unsigned int exponent_;
};
class RSAPSSKeyPairGenerationConfig : public RSAKeyPairGenerationConfig {
public:
RSAPSSKeyPairGenerationConfig(unsigned int modulus_bits,
unsigned int exponent,
const EVP_MD* md,
const EVP_MD* mgf1_md,
int saltlen)
: RSAKeyPairGenerationConfig(modulus_bits, exponent),
md_(md), mgf1_md_(mgf1_md), saltlen_(saltlen) {}
EVPKeyCtxPointer Setup() override {
return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, nullptr));
}
bool Configure(const EVPKeyCtxPointer& ctx) override {
if (!RSAKeyPairGenerationConfig::Configure(ctx))
return false;
if (md_ != nullptr) {
if (EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx.get(), md_) <= 0)
return false;
}
if (mgf1_md_ != nullptr) {
if (EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx.get(), mgf1_md_) <= 0)
return false;
}
if (saltlen_ >= 0) {
if (EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx.get(), saltlen_) <= 0)
return false;
}
return true;
}
private:
const EVP_MD* md_;
const EVP_MD* mgf1_md_;
const int saltlen_;
};
class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig {
public:
DSAKeyPairGenerationConfig(unsigned int modulus_bits, int divisor_bits)
@ -6293,6 +6367,44 @@ void GenerateKeyPairRSA(const FunctionCallbackInfo<Value>& args) {
GenerateKeyPair(args, 2, std::move(config));
}
void GenerateKeyPairRSAPSS(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
CHECK(args[0]->IsUint32());
const uint32_t modulus_bits = args[0].As<Uint32>()->Value();
CHECK(args[1]->IsUint32());
const uint32_t exponent = args[1].As<Uint32>()->Value();
const EVP_MD* md = nullptr;
if (!args[2]->IsUndefined()) {
CHECK(args[2]->IsString());
String::Utf8Value md_name(env->isolate(), args[2].As<String>());
md = EVP_get_digestbyname(*md_name);
if (md == nullptr)
return env->ThrowTypeError("Digest method not supported");
}
const EVP_MD* mgf1_md = nullptr;
if (!args[3]->IsUndefined()) {
CHECK(args[3]->IsString());
String::Utf8Value mgf1_md_name(env->isolate(), args[3].As<String>());
mgf1_md = EVP_get_digestbyname(*mgf1_md_name);
if (mgf1_md == nullptr)
return env->ThrowTypeError("Digest method not supported");
}
int saltlen = -1;
if (!args[4]->IsUndefined()) {
CHECK(args[4]->IsInt32());
saltlen = args[4].As<Int32>()->Value();
}
std::unique_ptr<KeyPairGenerationConfig> config(
new RSAPSSKeyPairGenerationConfig(modulus_bits, exponent,
md, mgf1_md, saltlen));
GenerateKeyPair(args, 5, std::move(config));
}
void GenerateKeyPairDSA(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsUint32());
const uint32_t modulus_bits = args[0].As<Uint32>()->Value();
@ -6748,6 +6860,7 @@ void Initialize(Local<Object> target,
env->SetMethod(target, "pbkdf2", PBKDF2);
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
env->SetMethod(target, "generateKeyPairRSAPSS", GenerateKeyPairRSAPSS);
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
env->SetMethod(target, "generateKeyPairNid", GenerateKeyPairNid);

View File

@ -670,7 +670,7 @@ class Sign : public SignBase {
SignResult SignFinal(
const ManagedEVPPKey& pkey,
int padding,
int saltlen);
const v8::Maybe<int>& saltlen);
protected:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
@ -691,7 +691,7 @@ class Verify : public SignBase {
const char* sig,
int siglen,
int padding,
int saltlen,
const v8::Maybe<int>& saltlen,
bool* verify_result);
protected:

View File

@ -37,6 +37,12 @@ all: \
rsa_public_1024.pem \
rsa_public_2048.pem \
rsa_public_4096.pem \
rsa_pss_private_2048.pem \
rsa_pss_private_2048_sha256_sha256_16.pem \
rsa_pss_private_2048_sha512_sha256_20.pem \
rsa_pss_public_2048.pem \
rsa_pss_public_2048_sha256_sha256_16.pem \
rsa_pss_public_2048_sha512_sha256_20.pem \
#
# Create Certificate Authority: ca1
@ -574,6 +580,24 @@ rsa_public_2048.pem: rsa_private_2048.pem
rsa_public_4096.pem: rsa_private_4096.pem
openssl rsa -in rsa_private_4096.pem -pubout -out rsa_public_4096.pem
rsa_pss_private_2048.pem:
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out rsa_pss_private_2048.pem
rsa_pss_private_2048_sha256_sha256_16.pem:
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -pkeyopt rsa_pss_keygen_md:sha256 -pkeyopt rsa_pss_keygen_mgf1_md:sha256 -pkeyopt rsa_pss_keygen_saltlen:16 -out rsa_pss_private_2048_sha256_sha256_16.pem
rsa_pss_private_2048_sha512_sha256_20.pem:
openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -pkeyopt rsa_pss_keygen_md:sha512 -pkeyopt rsa_pss_keygen_mgf1_md:sha256 -pkeyopt rsa_pss_keygen_saltlen:20 -out rsa_pss_private_2048_sha512_sha256_20.pem
rsa_pss_public_2048.pem: rsa_pss_private_2048.pem
openssl pkey -in rsa_pss_private_2048.pem -pubout -out rsa_pss_public_2048.pem
rsa_pss_public_2048_sha256_sha256_16.pem: rsa_pss_private_2048_sha256_sha256_16.pem
openssl pkey -in rsa_pss_private_2048_sha256_sha256_16.pem -pubout -out rsa_pss_public_2048_sha256_sha256_16.pem
rsa_pss_public_2048_sha512_sha256_20.pem: rsa_pss_private_2048_sha512_sha256_20.pem
openssl pkey -in rsa_pss_private_2048_sha512_sha256_20.pem -pubout -out rsa_pss_public_2048_sha512_sha256_20.pem
clean:
rm -f *.pfx *.pem *.srl ca2-database.txt ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem
@> fake-startcom-root-database.txt

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADALBgkqhkiG9w0BAQoEggSoMIIEpAIBAAKCAQEAy4OMdS84PlgI5CRL
bdbud9Ru7vprFr2YNNUmdT7D3YgApiv8CjzKXLiVDnbMET+lwmtag/EcZsxVCKov
su30pYASBriHOiMVYui9+ZaJoQ9yI6lOjG1RbuUBJXNSjHBJxqBqmcgZOb1mdRr/
eXzpAMWJ3hfuLojU2+zUSJ3/rvepepcLFG2q9nA0+PJskJ7Pnh3L0ydnv3U3hduM
n5OVfm/Jx1FPyZpD184tJff+N+MY3s3hIcfuOnL9Pl4RPGeaTC4T1o460NaG6bG7
c2Whg6NOaVgaFIaiNbrTTNCpVjeTyalsTXYlQQ3hiKjst0Q7pfFEkJDo8qiqLad1
Msl59wIDAQABAoIBAQC6G8aqs0/f02nuGDLSc6cH9kCsUlz0ItW6GuJcfdVoFSNi
0v5d7lGwkSveWk0ryOSw8rOHzUqHx3xLvDZ6jpkXcBMMClu/kq3QEb8JK90YaKOc
cQvf52h83Pc7ZEatH1KYTcKudwp6fvXfSZ0vYEdD6WG2tHOgIollxSIsdjCHs1qi
7baNHdK9T4DveuEZNcZ+LraZ1haHmFeqIPcy+KvpGuTaLCg5FPhH2jsIkw9apr7i
iFLi+IJ7S5Bn/8XShmJWk4hPyx0jtIkC5r2iJnHf4x+XYWZfdo7oew3Dg6Pa7T6r
I164Nnaw0u0LvO4gQdvYaJ/j9A602nHTp7Tsq8chAoGBAOtVHgIqpmdzwR5KjotW
LuGXDdO9X6Xfge9ca2MlWH1jOj+zqEV7JtrjnZAzzOgP2kgqzpIR71Njs8wkaxTJ
Tle0Ke6R/ghU9YOQgRByKjqJfQXHZnYFPsMg0diNYLroJ4SG8LO4+2SygTYZ4eKL
qU0bda3QvQ7FL+rTNQBy01b9AoGBAN1jEQI80JxCT7AMvXE6nObIhbkASHte4yOE
1CBwcOuBEGcuvMOvQVMzKITgea6+kgsu4ids4dM5PTPapQgpKqIIQ2/eSesaf56g
73clGGSTPHJP0v+EfKg4+GYJf8o2swT0xDHkgWLgjjdsncB9hATc2j6DvHeau18d
pgCLz9kDAoGAXl/SGvhTp2UqayVnKMW1I07agrGNLA4II5+iiS4u4InskCNSNhr/
KATj6TJ82AuTdCGGmdmLapuvPQzVzI42VsGvlzcA8wJvOwW2XIwMF1GPy8N9eZL8
6m+89+Uqh4oWXvVmjgx+9JEJdFLI3Xs4t+1tMfll+AhoAPoWZUmnK1kCgYAvEBxR
iXQfg8lE97BeHcO1G/OxfGnsMCPBLT+bFcwrhGhkRv9B6kPM2BdJCB9WEpUhY3oY
P4FSUdy85UIoFfhGMdOEOJEmNZ/jrPq7LVueJd63vlhwkU2exV2o82QDLNWpvA7p
PFZ1Gp+hEKoIfaZPElQi7gZmtrIWaksb2pz42QKBgQCct9NP2qJfqeS4206RDnfv
M238/O2lNhLWdSwY0g+tcN+I1sGs3+4vvrm95cxwAmXZyIM11wjdMcAPNxibodY7
vufsebPHDBA0j0yuTjGkXefUKd1GdO88i5fppzxB7prDX9//DsWWrFhIMMRNYe0Q
aeHd/NPuHcjZKcnaVBgukQ==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,29 @@
-----BEGIN PRIVATE KEY-----
MIIE7wIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3
DQEBCDALBglghkgBZQMEAgGiAwIBEASCBKkwggSlAgEAAoIBAQDfqNM4C+QtD73i
ILqOkqfV8ha3O19jpX8UujIk1Z72bbbuwEzh0+sBw0dD0N8CgkXnePOEEd6q7HNm
byCNqRpDK6NDvaCMDWgEaD/PlHkRntvKh81IXSMC5imjRfOcZIE/Gnw7h8tanab0
n75+ODvLJrmEWUG2q79Im1mWMx7Spod+Np6XEY+7I7nAUUWivr35Yx5DeyxY8rxF
GpsLtGsi7JNQO4aHyeBpj8tz0Fhv23uPywE2nGmPHfnkXWbrTcHGbzYBgEbeSH9K
UkRwczqDXNOPhtfaEHEFTm0MoeKCnJe1VOjSywev77dV1KZfpVh3Kh0ZRQIe9YOV
Jhj4lMx3AgMBAAECggEBAIc+IgK5Bg/NfgeXvNdrjPuM+PlxeHvb3h1dfebSGd5v
d3elZpgDug6F07kJO2Db/4M5mx7YY2m9swZU2j1u7MeDQqU6rDMkBCruEu/lmtPx
2Hv+ZD6Gux4MqU7mhKmkCJds34Rr16aCwCsZ0WmnfViZoQKLqnXYIsG31pNBdDjx
gke0HhX1LkA9yTVwlk8xOaHPqI4KfsFAyoiiHzyttGDexzb1PzmM0pybAPDMhpN/
wXp2kLjyzmUmPe2Y2yva69WVWo7qS6joKjY75MQ1t20HYgEL69IApvCPu4CANfi9
j3FAaV/+WrnhKCi6QyUi5PCI/+AJLsjNQmqTXIdBEoECgYEA+XsgFbeZ6+ZzEHa7
HyFH6kiyBLd0q7w+ZLPsoOmEApDaP3yXSC7eJU7M/tPUPj8VQMMSK2D6fgmUDwhb
3mEXFZxf67UlPFsFjweYcBihwy4r8QKBwury6dEbHPSUq4mXFJF5XRQdGqRGkr/F
8OLZ0MwmHLUzczA67PxP/wF8TsECgYEA5YD4RxxJJYfAk1rFbqHRhNB8UeYVL+Wo
wsRET1JeFg+S5grLGUga+KBB8Jn7Ahaip7MVE0Iud1cgaDi4WBEJsbJ6oJTlHJEg
Jq7QAaBafwjeSCSnaEsVBVNvriy2WF7uAomLSKmW6uSUOBBFFt4+G+akG56EfOPc
7YKBfsf5ITcCgYBvjVZzX307tfeNTQmuibsWTxsKcN2CTNG5RZpw+PlGDG8KJDOg
2xQJqoqPBzjH/H0MUC03qE1ZPf8uGZa6gL9JsnpRctYLfselhMfsl5b9JxAO3AgZ
l+S2GAH/mH1BlmwvjjyuGehJmVrVE1r2sviiHCaOf5dZ0h8HCGrco1VqAQKBgQCf
fYkMofOTSUvjG2mpAHuCOQCsSaDfsFIfSBXQqgUIf7ouc8HAyAM2VOh+NAPj56cR
s7opsAxqkvnKc+BoEy8Rdl8RyWeO+qvFNicHelBph9gxeod8SvFIyjsKZ7gwoYf1
63AIBxMCGeeHLodU5Q10hkv1hau8vv2BcPhdCstu8QKBgQDgO4Rr36Pa5rjbNbGN
PsJqALjthTeU0yaU8hdC7Hc7B/T6npEIhw3s1eNq7e0eeDltqz7tDnY8qysazbQZ
p1cV5TJ8+gtwGmDoADBnU1NXqX4Squfml6OhNEucpTdjux7JdLVmmQFraOT2Eu5f
9uuNtA+d8uhBEXhskuvEC552ug==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,29 @@
-----BEGIN PRIVATE KEY-----
MIIE5wIBADA4BgkqhkiG9w0BAQowK6ANMAsGCWCGSAFlAwQCA6EaMBgGCSqGSIb3
DQEBCDALBglghkgBZQMEAgEEggSmMIIEogIBAAKCAQEAvM9NgMCDqy5dqj5Ua2cZ
cc4zVr+fCF34bZn63OBeYG8RTJKM3j36lO/yamtfDctDhb87b45CS6ipEr8J57I9
WF55TNngsn6GNpXgwAe0eFXUnKonuqnGEC7x8r3vkAg99PBKhAtFc5oTaaZDAFKM
zc5dIC/J0Y+kxhqjCPNI0ydQgZmKBrmYjM9cvbOYgRQL10GrWeJ+XHk2E33endaF
+4dwjgyrzInt/l6OTkiCL8F59J/htk1GPru9BT6w5yOS/vH6q6FD6uizULVznytd
lHjgnrVaHJmsqVjrYQa9OAZj9GBrTelBWvQ9b6+FBHUFHBp8HSp82lWCZThPrcZ/
QwIDAQABAoIBADDzUfWic8CKuc/sbviVdzxRKHBCJ9oEeub3d9mR9gXsZcDDcfAg
g3nfp6q9gZxS6YOga6llaXyyEnuAufGu/UaO38Xz6tR8BxHZ07YViU11ezTOzJQR
df82HJZBdf2SlXWOYtNPFMd+16+ZYl+QB19INE6m9Rz2r9KIj2I/qM7NPPVhDRF0
G4O0Yf2vaPhjoIaewn7xtQ6wmX7pAGcd8dmYEIGGkBi8aY3BVwrRK1X4AmD+oSmE
wXqR6MQIzD4KdypL4UD1Cb4GoFeVRclXvegOG+EKl0iD+mjTodB4yjoJh98NYe3+
GtpR/2u3Ltq8RqWpIg8ryShQk/MIqGJ5KpkCgYEA80uNEYWlBt6QGNvVIYrhnw+V
2nLJWedioKV4H1sr9OLF/7WFOUfsaflpVybnmwfNV15lEyHb/m/sCM9jTrNk1t/q
qhRnvtmy3kntxWBeoA2o0TRg/XZKWjabZsr/4UE/Ztws2opOvl9x05IYeZlU+PbZ
B1lX2e+vtMOpllvRr28CgYEAxqtfrYv8brp/fAUqGu/MtdHxQdU1+vE+auN17gam
ZM6ojIeasX4k2z0Rd/+8Dga9wPgO7hjtFZ2NWD5UwfBiw5U2PVZ6bp3iKSBPGHEh
RsIR+nw8pFIgsQKoYnVK58tEnfQ31GSupKpYybHbaL5SdId+mfXU86SbKX/MefZX
g20CgYBjn8hAKI2O5ovy4fHALnJ9A5DFRsOUgN8uERPDIz44pLOXJelLr1vreSnd
ehzUqrk20Xxp/S9sXMA2S1XK4EKmikI5KungiJxp0bP/Yprcxzsdj2k34LxJfJrd
2Lo2rtUbdYUYaBIeek7N58EF6feVit8L11XV9APq7UQAQdD3GQKBgBmxuGIdpLw9
apeDo3pwYS1yxZ0aEi0uXkA8wtfSDFslTy89qogiJGomb8fxT0URIiF+849fseoF
wm4TQasDiAJ7ndQ5BwSfbsya3R/wIbmhB+o5fy5RYOED0vtI6DMqWumC2GWjz+KE
FY+gbRwS4V8o1vrajHwmYdrwKGXtskvRAoGADe/EdlCj98r6Zle9giF1C0GDIDTx
8bR2m+Ogh9ZO218GpDMNaQ/WmbYVPDYMbRSOEA7y2gVkd1OeYhJMF6aYffp3aWhN
t9g0uojLY0jfEpWBLBQdlYOTl7hOnLWRRcOAKTlHVg+OuD/O/GmdQ2Rg2H/hAWlI
muuTQPuQTCV1aTc=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBAMuDjHUvOD5YCOQkS23W7nfU
bu76axa9mDTVJnU+w92IAKYr/Ao8yly4lQ52zBE/pcJrWoPxHGbMVQiqL7Lt9KWA
Ega4hzojFWLovfmWiaEPciOpToxtUW7lASVzUoxwScagapnIGTm9ZnUa/3l86QDF
id4X7i6I1Nvs1Eid/673qXqXCxRtqvZwNPjybJCez54dy9MnZ791N4XbjJ+TlX5v
ycdRT8maQ9fOLSX3/jfjGN7N4SHH7jpy/T5eETxnmkwuE9aOOtDWhumxu3NloYOj
TmlYGhSGojW600zQqVY3k8mpbE12JUEN4Yio7LdEO6XxRJCQ6PKoqi2ndTLJefcC
AwEAAQ==
-----END PUBLIC KEY-----

View File

@ -0,0 +1,10 @@
-----BEGIN PUBLIC KEY-----
MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEB
CDALBglghkgBZQMEAgGiAwIBEAOCAQ8AMIIBCgKCAQEA36jTOAvkLQ+94iC6jpKn
1fIWtztfY6V/FLoyJNWe9m227sBM4dPrAcNHQ9DfAoJF53jzhBHequxzZm8gjaka
QyujQ72gjA1oBGg/z5R5EZ7byofNSF0jAuYpo0XznGSBPxp8O4fLWp2m9J++fjg7
yya5hFlBtqu/SJtZljMe0qaHfjaelxGPuyO5wFFFor69+WMeQ3ssWPK8RRqbC7Rr
IuyTUDuGh8ngaY/Lc9BYb9t7j8sBNpxpjx355F1m603Bxm82AYBG3kh/SlJEcHM6
g1zTj4bX2hBxBU5tDKHigpyXtVTo0ssHr++3VdSmX6VYdyodGUUCHvWDlSYY+JTM
dwIDAQAB
-----END PUBLIC KEY-----

View File

@ -0,0 +1,10 @@
-----BEGIN PUBLIC KEY-----
MIIBTTA4BgkqhkiG9w0BAQowK6ANMAsGCWCGSAFlAwQCA6EaMBgGCSqGSIb3DQEB
CDALBglghkgBZQMEAgEDggEPADCCAQoCggEBALzPTYDAg6suXao+VGtnGXHOM1a/
nwhd+G2Z+tzgXmBvEUySjN49+pTv8mprXw3LQ4W/O2+OQkuoqRK/CeeyPVheeUzZ
4LJ+hjaV4MAHtHhV1JyqJ7qpxhAu8fK975AIPfTwSoQLRXOaE2mmQwBSjM3OXSAv
ydGPpMYaowjzSNMnUIGZiga5mIzPXL2zmIEUC9dBq1niflx5NhN93p3WhfuHcI4M
q8yJ7f5ejk5Igi/BefSf4bZNRj67vQU+sOcjkv7x+quhQ+ros1C1c58rXZR44J61
WhyZrKlY62EGvTgGY/Rga03pQVr0PW+vhQR1BRwafB0qfNpVgmU4T63Gf0MCAwEA
AQ==
-----END PUBLIC KEY-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEugIBADALBgkqhkiG9w0BAQoEggSmMIIEogIBAAKCAQEAwMSNbT9SbSHvXmPt
j1x2Ipk1tUM06301UD91xGcA0232zrIQcKjsPM7bE6YXN0zRxfLJUqalewCk80Ct
6V+E5XtMHUFQt1Ne8HW9U930KnfnQEyU8UwRPoWWeZQhs+sa8ZfggtfN7gq4/wiS
KFNNtSJb24NKoLis31P0nILGC4/JewgE0QaFUoOL+Oc3dMhwWg9/H64sSjhI/SGW
9Sv3M6WcSn7vQCe8oM2vslf3Xm8rHNqZMlXujs7zhRtcr5alKz9BwMJIoGouQrk9
9cgupdYsddgNh2bC4TQR9BMKHj8tV5Uf3Pbf4EoZOFffCbyBZxmKtsYsmhh2FDLA
RzNhKwIDAQABAoIBAHBRlj4ziSmBfmG3Q/ImY8chEkQ9lpYn7GqHr2zyv25yQj6J
Tj72jj+YH9pBCoH0Rr5aCqgX5Y/X/kSmSS8TsvGrd9wL9KX88/KUB+7YAq7EEoBK
nvZB5kJRwC2y/DhDIv3mCrDyYVDz+nrPWaoZb8u861zqEQ+4yzGNT5fqMs8Ewm8A
hxg3GA2R6FC2CymZO884XOxlVac6SNURfA+U+xrcMIXbXpok2Z5eh/kMOeIKwmL0
QEO0U6DEnZm4rJjywu8fEkKbX00YfaDQaiGzRZfvmzkTPIQemXPWARdIvFtJU8Fx
OWWeMumJD1KiU9ISW4e76l7F8UOviT6jEg9rxFECgYEA96WCEIB+O4aO7+s56kOv
vQkEXn959lz7e++S9AV3R19PpBCh50l5v9NSjGQlA4FU4AdBB5EmiX/bLZRHFwHI
KLDsMFuq9id3OPHYIzFP4YjVHTGRPZToJHwy4ePIdZEaeJHY39EEz8oHsSSJlLdm
o0417RsFAfApW2VN63c3JFcCgYEAx0Um/ATsT4ELguVQ+XlquLQdS12XS7zjcwWv
PL8UyooSxcjcbLcJB6DRWXM0NOry7KPUCIF4m3KSjIZypV/v2KVFPCfD3vxZcdB7
xgccqXJMUx7MSs9AMZXTtv5hG7RS5z+ig7Yi/6nzBm21jKYKbFDbqfq8MSfiR6cT
KjR+RU0CgYAm/iFnlcPKfZpd/mylDTlLi3Lrqii6+NMEJam+0GmCjGhOzeugLjqE
ULLLtiz5y1Bg4eOEXH9z4PTSzWkQH1Czz3+w8Y4OqhIknjfI+se4HEJqEVbsGlke
/YtJdAMpN8qyN0ytmQyn5wilBLrA9surZPIqvjlgn77zTBUjwSamiwKBgAqIVS8s
83CgWYNpq4YELOfmXUYGhGC0czE5M7H6R5cNBUD/BOeaJRgKIAaiWDgT0xM+9Y4d
icptm+Fhmd2z3HGPCsHLOEco/3FMm74z0ggCypX6IsIxgiscyDv75hYYyej/LA/a
KK9qxDWqxtXQUOy4uWOapSfT+9ndst2gOKxhAoGAVFcfedCLxummgTtZE91n59pL
TWTk4GgYpWyv6XbHjYrFW2y18qmn0hmEpO+440So0NmGGDtNnPYNUKY/MPjHScwC
FoZMFqqnkmshXz0uDx3gMQK2JDmdF+s3VwZq4Rtb3NJ9v4/WMgWftxaUpAm1/aRC
IHc67mAAez4i8fg2wTQ=
-----END PRIVATE KEY-----

View File

@ -8,6 +8,8 @@ const assert = require('assert');
const {
createCipheriv,
createDecipheriv,
createSign,
createVerify,
createSecretKey,
createPublicKey,
createPrivateKey,
@ -178,13 +180,6 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
});
}
{
// This should not cause a crash: https://github.com/nodejs/node/pull/26786
const pem = fixtures.readSync('test_unknown_privkey.pem', 'ascii');
const key = createPrivateKey(pem);
assert.strictEqual(key.asymmetricKeyType, undefined);
}
[
{ private: fixtures.readSync('test_ed25519_privkey.pem', 'ascii'),
public: fixtures.readSync('test_ed25519_pubkey.pem', 'ascii'),
@ -264,6 +259,146 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
assert.strictEqual(privateKey.symmetricKeySize, undefined);
}
{
// Test RSA-PSS.
{
// This key pair does not restrict the message digest algorithm or salt
// length.
const publicPem = fixtures.readKey('rsa_pss_public_2048.pem');
const privatePem = fixtures.readKey('rsa_pss_private_2048.pem');
const publicKey = createPublicKey(publicPem);
const privateKey = createPrivateKey(privatePem);
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
for (const key of [privatePem, privateKey]) {
// Any algorithm should work.
for (const algo of ['sha1', 'sha256']) {
// Any salt length should work.
for (const saltLength of [undefined, 8, 10, 12, 16, 18, 20]) {
const signature = createSign(algo)
.update('foo')
.sign({ key, saltLength });
for (const pkey of [key, publicKey, publicPem]) {
const okay = createVerify(algo)
.update('foo')
.verify({ key: pkey, saltLength }, signature);
assert.ok(okay);
}
}
}
}
// Exporting the key using PKCS#1 should not work since this would discard
// any algorithm restrictions.
common.expectsError(() => {
publicKey.export({ format: 'pem', type: 'pkcs1' });
}, {
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS'
});
}
{
// This key pair enforces sha256 as the message digest and the MGF1
// message digest and a salt length of at least 16 bytes.
const publicPem =
fixtures.readKey('rsa_pss_public_2048_sha256_sha256_16.pem');
const privatePem =
fixtures.readKey('rsa_pss_private_2048_sha256_sha256_16.pem');
const publicKey = createPublicKey(publicPem);
const privateKey = createPrivateKey(privatePem);
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
for (const key of [privatePem, privateKey]) {
// Signing with anything other than sha256 should fail.
assert.throws(() => {
createSign('sha1').sign(key);
}, /digest not allowed/);
// Signing with salt lengths less than 16 bytes should fail.
for (const saltLength of [8, 10, 12]) {
assert.throws(() => {
createSign('sha1').sign({ key, saltLength });
}, /pss saltlen too small/);
}
// Signing with sha256 and appropriate salt lengths should work.
for (const saltLength of [undefined, 16, 18, 20]) {
const signature = createSign('sha256')
.update('foo')
.sign({ key, saltLength });
for (const pkey of [key, publicKey, publicPem]) {
const okay = createVerify('sha256')
.update('foo')
.verify({ key: pkey, saltLength }, signature);
assert.ok(okay);
}
}
}
}
{
// This key enforces sha512 as the message digest and sha256 as the MGF1
// message digest.
const publicPem =
fixtures.readKey('rsa_pss_public_2048_sha512_sha256_20.pem');
const privatePem =
fixtures.readKey('rsa_pss_private_2048_sha512_sha256_20.pem');
const publicKey = createPublicKey(publicPem);
const privateKey = createPrivateKey(privatePem);
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
// Node.js usually uses the same hash function for the message and for MGF1.
// However, when a different MGF1 message digest algorithm has been
// specified as part of the key, it should automatically switch to that.
// This behavior is required by sections 3.1 and 3.3 of RFC4055.
for (const key of [privatePem, privateKey]) {
// sha256 matches the MGF1 hash function and should be used internally,
// but it should not be permitted as the main message digest algorithm.
for (const algo of ['sha1', 'sha256']) {
assert.throws(() => {
createSign(algo).sign(key);
}, /digest not allowed/);
}
// sha512 should produce a valid signature.
const signature = createSign('sha512')
.update('foo')
.sign(key);
for (const pkey of [key, publicKey, publicPem]) {
const okay = createVerify('sha512')
.update('foo')
.verify(pkey, signature);
assert.ok(okay);
}
}
}
}
{

View File

@ -6,12 +6,15 @@ if (!common.hasCrypto)
const assert = require('assert');
const {
constants,
createSign,
createVerify,
generateKeyPair,
generateKeyPairSync,
publicEncrypt,
privateDecrypt
privateDecrypt,
sign,
verify
} = require('crypto');
const { promisify } = require('util');
@ -40,15 +43,26 @@ function testEncryptDecrypt(publicKey, privateKey) {
// Tests that a key pair can be used for signing / verification.
function testSignVerify(publicKey, privateKey) {
const message = 'Hello Node.js world!';
const signature = createSign('SHA256').update(message)
.sign(privateKey, 'hex');
const message = Buffer.from('Hello Node.js world!');
function oldSign(algo, data, key) {
return createSign(algo).update(data).sign(key);
}
function oldVerify(algo, data, key, signature) {
return createVerify(algo).update(data).verify(key, signature);
}
for (const signFn of [sign, oldSign]) {
const signature = signFn('SHA256', message, privateKey);
for (const verifyFn of [verify, oldVerify]) {
for (const key of [publicKey, privateKey]) {
const okay = createVerify('SHA256').update(message)
.verify(key, signature, 'hex');
const okay = verifyFn('SHA256', message, key, signature);
assert(okay);
}
}
}
}
// Constructs a regular expression for a PEM-encoded key with the given label.
function getRegExpForPEM(label, cipher) {
@ -251,6 +265,43 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
}));
}
{
// Test RSA-PSS.
generateKeyPair('rsa-pss', {
modulusLength: 512,
saltLength: 16,
hash: 'sha256',
mgf1Hash: 'sha256'
}, common.mustCall((err, publicKey, privateKey) => {
assert.ifError(err);
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, 'rsa-pss');
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');
// Unlike RSA, RSA-PSS does not allow encryption.
assert.throws(() => {
testEncryptDecrypt(publicKey, privateKey);
}, /operation not supported for this keytype/);
// RSA-PSS also does not permit signing with PKCS1 padding.
assert.throws(() => {
testSignVerify({
key: publicKey,
padding: constants.RSA_PKCS1_PADDING
}, {
key: privateKey,
padding: constants.RSA_PKCS1_PADDING
});
}, /illegal or unsupported padding mode/);
// The padding should correctly default to RSA_PKCS1_PSS_PADDING now.
testSignVerify(publicKey, privateKey);
}));
}
{
const privateKeyEncoding = {
type: 'pkcs8',
@ -440,9 +491,7 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
common.expectsError(() => generateKeyPairSync('rsa2', {}), {
type: TypeError,
code: 'ERR_INVALID_ARG_VALUE',
message: "The argument 'type' must be one of " +
"'rsa', 'dsa', 'ec', 'ed25519', 'ed448'," +
" 'x25519', 'x448'. Received 'rsa2'"
message: "The argument 'type' must be a supported key type. Received 'rsa2'"
});
}