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:
parent
d834275a48
commit
969bd1eb7b
@ -1129,6 +1129,9 @@ passing keys as strings or `Buffer`s due to improved security features.
|
|||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v11.6.0
|
added: v11.6.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/26960
|
||||||
|
description: Added support for `'rsa-pss'`
|
||||||
- version: REPLACEME
|
- version: REPLACEME
|
||||||
pr-url: https://github.com/nodejs/node/pull/26786
|
pr-url: https://github.com/nodejs/node/pull/26786
|
||||||
description: This property now returns `undefined` for KeyObject
|
description: This property now returns `undefined` for KeyObject
|
||||||
@ -1142,8 +1145,18 @@ changes:
|
|||||||
-->
|
-->
|
||||||
* {string}
|
* {string}
|
||||||
|
|
||||||
For asymmetric keys, this property represents the type of the embedded key
|
For asymmetric keys, this property represents the type of the key. Supported key
|
||||||
(`'rsa'`, `'dsa'`, `'ec'`, `'ed25519'`, `'ed448'`, `'x25519'` or `'x448'`).
|
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
|
This property is `undefined` for unrecognized `KeyObject` types and symmetric
|
||||||
keys.
|
keys.
|
||||||
|
|
||||||
@ -1271,6 +1284,9 @@ console.log(verify.verify(publicKey, signature));
|
|||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.92
|
added: v0.1.92
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/26960
|
||||||
|
description: This function now supports RSA-PSS keys.
|
||||||
- version: v11.6.0
|
- version: v11.6.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/24234
|
pr-url: https://github.com/nodejs/node/pull/24234
|
||||||
description: This function now supports key objects.
|
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`
|
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
|
||||||
|
|
||||||
Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function
|
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
|
* `saltLength`: {integer} - salt length for when padding is
|
||||||
`RSA_PKCS1_PSS_PADDING`. The special value
|
`RSA_PKCS1_PSS_PADDING`. The special value
|
||||||
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
|
`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
|
<!-- YAML
|
||||||
added: v0.1.92
|
added: v0.1.92
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/26960
|
||||||
|
description: This function now supports RSA-PSS keys.
|
||||||
- version: v11.7.0
|
- version: v11.7.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/25217
|
pr-url: https://github.com/nodejs/node/pull/25217
|
||||||
description: The key can now be a private key.
|
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`
|
* `crypto.constants.RSA_PKCS1_PSS_PADDING`
|
||||||
|
|
||||||
Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function
|
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
|
* `saltLength`: {integer} - salt length for when padding is
|
||||||
`RSA_PKCS1_PSS_PADDING`. The special value
|
`RSA_PKCS1_PSS_PADDING`. The special value
|
||||||
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
|
`crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
const { AsyncWrap, Providers } = internalBinding('async_wrap');
|
const { AsyncWrap, Providers } = internalBinding('async_wrap');
|
||||||
const {
|
const {
|
||||||
generateKeyPairRSA,
|
generateKeyPairRSA,
|
||||||
|
generateKeyPairRSAPSS,
|
||||||
generateKeyPairDSA,
|
generateKeyPairDSA,
|
||||||
generateKeyPairEC,
|
generateKeyPairEC,
|
||||||
generateKeyPairNid,
|
generateKeyPairNid,
|
||||||
@ -141,6 +142,7 @@ function check(type, options, callback) {
|
|||||||
let impl;
|
let impl;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'rsa':
|
case 'rsa':
|
||||||
|
case 'rsa-pss':
|
||||||
{
|
{
|
||||||
const { modulusLength } = needOptions();
|
const { modulusLength } = needOptions();
|
||||||
if (!isUint32(modulusLength))
|
if (!isUint32(modulusLength))
|
||||||
@ -153,10 +155,27 @@ function check(type, options, callback) {
|
|||||||
throw new ERR_INVALID_OPT_VALUE('publicExponent', publicExponent);
|
throw new ERR_INVALID_OPT_VALUE('publicExponent', publicExponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl = (wrap) => generateKeyPairRSA(modulusLength, publicExponent,
|
if (type === 'rsa') {
|
||||||
publicFormat, publicType,
|
impl = (wrap) => generateKeyPairRSA(modulusLength, publicExponent,
|
||||||
privateFormat, privateType,
|
publicFormat, publicType,
|
||||||
cipher, passphrase, wrap);
|
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;
|
break;
|
||||||
case 'dsa':
|
case 'dsa':
|
||||||
@ -225,8 +244,7 @@ function check(type, options, callback) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ERR_INVALID_ARG_VALUE('type', type,
|
throw new ERR_INVALID_ARG_VALUE('type', type,
|
||||||
"must be one of 'rsa', 'dsa', 'ec', " +
|
'must be a supported key type');
|
||||||
"'ed25519', 'ed448', 'x25519', 'x448'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
|
@ -12,10 +12,6 @@ const {
|
|||||||
signOneShot: _signOneShot,
|
signOneShot: _signOneShot,
|
||||||
verifyOneShot: _verifyOneShot
|
verifyOneShot: _verifyOneShot
|
||||||
} = internalBinding('crypto');
|
} = internalBinding('crypto');
|
||||||
const {
|
|
||||||
RSA_PSS_SALTLEN_AUTO,
|
|
||||||
RSA_PKCS1_PADDING
|
|
||||||
} = internalBinding('constants').crypto;
|
|
||||||
const {
|
const {
|
||||||
getDefaultEncoding,
|
getDefaultEncoding,
|
||||||
kHandle,
|
kHandle,
|
||||||
@ -56,14 +52,14 @@ Sign.prototype.update = function update(data, encoding) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function getPadding(options) {
|
function getPadding(options) {
|
||||||
return getIntOption('padding', RSA_PKCS1_PADDING, options);
|
return getIntOption('padding', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSaltLength(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];
|
const value = options[name];
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
if (value === value >> 0) {
|
if (value === value >> 0) {
|
||||||
@ -72,7 +68,7 @@ function getIntOption(name, defaultValue, options) {
|
|||||||
throw new ERR_INVALID_OPT_VALUE(name, value);
|
throw new ERR_INVALID_OPT_VALUE(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultValue;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sign.prototype.sign = function sign(options, encoding) {
|
Sign.prototype.sign = function sign(options, encoding) {
|
||||||
|
@ -161,6 +161,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
|||||||
V(crypto_x25519_string, "x25519") \
|
V(crypto_x25519_string, "x25519") \
|
||||||
V(crypto_x448_string, "x448") \
|
V(crypto_x448_string, "x448") \
|
||||||
V(crypto_rsa_string, "rsa") \
|
V(crypto_rsa_string, "rsa") \
|
||||||
|
V(crypto_rsa_pss_string, "rsa-pss") \
|
||||||
V(cwd_string, "cwd") \
|
V(cwd_string, "cwd") \
|
||||||
V(data_string, "data") \
|
V(data_string, "data") \
|
||||||
V(dest_string, "dest") \
|
V(dest_string, "dest") \
|
||||||
|
@ -3690,6 +3690,8 @@ Local<Value> KeyObject::GetAsymmetricKeyType() const {
|
|||||||
switch (EVP_PKEY_id(this->asymmetric_key_.get())) {
|
switch (EVP_PKEY_id(this->asymmetric_key_.get())) {
|
||||||
case EVP_PKEY_RSA:
|
case EVP_PKEY_RSA:
|
||||||
return env()->crypto_rsa_string();
|
return env()->crypto_rsa_string();
|
||||||
|
case EVP_PKEY_RSA_PSS:
|
||||||
|
return env()->crypto_rsa_pss_string();
|
||||||
case EVP_PKEY_DSA:
|
case EVP_PKEY_DSA:
|
||||||
return env()->crypto_dsa_string();
|
return env()->crypto_dsa_string();
|
||||||
case EVP_PKEY_EC:
|
case EVP_PKEY_EC:
|
||||||
@ -4684,13 +4686,14 @@ void SignBase::CheckThrow(SignBase::Error error) {
|
|||||||
static bool ApplyRSAOptions(const ManagedEVPPKey& pkey,
|
static bool ApplyRSAOptions(const ManagedEVPPKey& pkey,
|
||||||
EVP_PKEY_CTX* pkctx,
|
EVP_PKEY_CTX* pkctx,
|
||||||
int padding,
|
int padding,
|
||||||
int salt_len) {
|
const Maybe<int>& salt_len) {
|
||||||
if (EVP_PKEY_id(pkey.get()) == EVP_PKEY_RSA ||
|
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)
|
if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0)
|
||||||
return false;
|
return false;
|
||||||
if (padding == RSA_PKCS1_PSS_PADDING) {
|
if (padding == RSA_PKCS1_PSS_PADDING && salt_len.IsJust()) {
|
||||||
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len) <= 0)
|
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len.FromJust()) <= 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4741,11 +4744,16 @@ void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
|
|||||||
sign->CheckThrow(err);
|
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,
|
static AllocatedBuffer Node_SignFinal(Environment* env,
|
||||||
EVPMDPointer&& mdctx,
|
EVPMDPointer&& mdctx,
|
||||||
const ManagedEVPPKey& pkey,
|
const ManagedEVPPKey& pkey,
|
||||||
int padding,
|
int padding,
|
||||||
int pss_salt_len) {
|
Maybe<int> pss_salt_len) {
|
||||||
unsigned char m[EVP_MAX_MD_SIZE];
|
unsigned char m[EVP_MAX_MD_SIZE];
|
||||||
unsigned int m_len;
|
unsigned int m_len;
|
||||||
|
|
||||||
@ -4778,7 +4786,7 @@ static AllocatedBuffer Node_SignFinal(Environment* env,
|
|||||||
Sign::SignResult Sign::SignFinal(
|
Sign::SignResult Sign::SignFinal(
|
||||||
const ManagedEVPPKey& pkey,
|
const ManagedEVPPKey& pkey,
|
||||||
int padding,
|
int padding,
|
||||||
int salt_len) {
|
const Maybe<int>& salt_len) {
|
||||||
if (!mdctx_)
|
if (!mdctx_)
|
||||||
return SignResult(kSignNotInitialised);
|
return SignResult(kSignNotInitialised);
|
||||||
|
|
||||||
@ -4829,11 +4837,17 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
|
|||||||
if (!key)
|
if (!key)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CHECK(args[offset]->IsInt32());
|
int padding = GetDefaultSignPadding(key);
|
||||||
int padding = args[offset].As<Int32>()->Value();
|
if (!args[offset]->IsUndefined()) {
|
||||||
|
CHECK(args[offset]->IsInt32());
|
||||||
|
padding = args[offset].As<Int32>()->Value();
|
||||||
|
}
|
||||||
|
|
||||||
CHECK(args[offset + 1]->IsInt32());
|
Maybe<int> salt_len = Nothing<int>();
|
||||||
int salt_len = args[offset + 1].As<Int32>()->Value();
|
if (!args[offset + 1]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 1]->IsInt32());
|
||||||
|
salt_len = Just<int>(args[offset + 1].As<Int32>()->Value());
|
||||||
|
}
|
||||||
|
|
||||||
SignResult ret = sign->SignFinal(
|
SignResult ret = sign->SignFinal(
|
||||||
key,
|
key,
|
||||||
@ -4894,11 +4908,17 @@ void SignOneShot(const FunctionCallbackInfo<Value>& args) {
|
|||||||
return CheckThrow(env, SignBase::Error::kSignUnknownDigest);
|
return CheckThrow(env, SignBase::Error::kSignUnknownDigest);
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK(args[offset + 2]->IsInt32());
|
int rsa_padding = GetDefaultSignPadding(key);
|
||||||
int rsa_padding = args[offset + 2].As<Int32>()->Value();
|
if (!args[offset + 2]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 2]->IsInt32());
|
||||||
|
rsa_padding = args[offset + 2].As<Int32>()->Value();
|
||||||
|
}
|
||||||
|
|
||||||
CHECK(args[offset + 3]->IsInt32());
|
Maybe<int> rsa_salt_len = Nothing<int>();
|
||||||
int rsa_salt_len = args[offset + 3].As<Int32>()->Value();
|
if (!args[offset + 3]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 3]->IsInt32());
|
||||||
|
rsa_salt_len = Just<int>(args[offset + 3].As<Int32>()->Value());
|
||||||
|
}
|
||||||
|
|
||||||
EVP_PKEY_CTX* pkctx = nullptr;
|
EVP_PKEY_CTX* pkctx = nullptr;
|
||||||
EVPMDPointer mdctx(EVP_MD_CTX_new());
|
EVPMDPointer mdctx(EVP_MD_CTX_new());
|
||||||
@ -4976,7 +4996,7 @@ SignBase::Error Verify::VerifyFinal(const ManagedEVPPKey& pkey,
|
|||||||
const char* sig,
|
const char* sig,
|
||||||
int siglen,
|
int siglen,
|
||||||
int padding,
|
int padding,
|
||||||
int saltlen,
|
const Maybe<int>& saltlen,
|
||||||
bool* verify_result) {
|
bool* verify_result) {
|
||||||
if (!mdctx_)
|
if (!mdctx_)
|
||||||
return kSignNotInitialised;
|
return kSignNotInitialised;
|
||||||
@ -5020,11 +5040,17 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
|
|||||||
|
|
||||||
ArrayBufferViewContents<char> hbuf(args[offset]);
|
ArrayBufferViewContents<char> hbuf(args[offset]);
|
||||||
|
|
||||||
CHECK(args[offset + 1]->IsInt32());
|
int padding = GetDefaultSignPadding(pkey);
|
||||||
int padding = args[offset + 1].As<Int32>()->Value();
|
if (!args[offset + 1]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 1]->IsInt32());
|
||||||
|
padding = args[offset + 1].As<Int32>()->Value();
|
||||||
|
}
|
||||||
|
|
||||||
CHECK(args[offset + 2]->IsInt32());
|
Maybe<int> salt_len = Nothing<int>();
|
||||||
int salt_len = args[offset + 2].As<Int32>()->Value();
|
if (!args[offset + 2]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 2]->IsInt32());
|
||||||
|
salt_len = Just<int>(args[offset + 2].As<Int32>()->Value());
|
||||||
|
}
|
||||||
|
|
||||||
bool verify_result;
|
bool verify_result;
|
||||||
Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding,
|
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);
|
return CheckThrow(env, SignBase::Error::kSignUnknownDigest);
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK(args[offset + 3]->IsInt32());
|
int rsa_padding = GetDefaultSignPadding(key);
|
||||||
int rsa_padding = args[offset + 3].As<Int32>()->Value();
|
if (!args[offset + 3]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 3]->IsInt32());
|
||||||
|
rsa_padding = args[offset + 3].As<Int32>()->Value();
|
||||||
|
}
|
||||||
|
|
||||||
CHECK(args[offset + 4]->IsInt32());
|
Maybe<int> rsa_salt_len = Nothing<int>();
|
||||||
int rsa_salt_len = args[offset + 4].As<Int32>()->Value();
|
if (!args[offset + 4]->IsUndefined()) {
|
||||||
|
CHECK(args[offset + 4]->IsInt32());
|
||||||
|
rsa_salt_len = Just<int>(args[offset + 4].As<Int32>()->Value());
|
||||||
|
}
|
||||||
|
|
||||||
EVP_PKEY_CTX* pkctx = nullptr;
|
EVP_PKEY_CTX* pkctx = nullptr;
|
||||||
EVPMDPointer mdctx(EVP_MD_CTX_new());
|
EVPMDPointer mdctx(EVP_MD_CTX_new());
|
||||||
@ -6064,6 +6096,48 @@ class RSAKeyPairGenerationConfig : public KeyPairGenerationConfig {
|
|||||||
const unsigned int exponent_;
|
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 {
|
class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig {
|
||||||
public:
|
public:
|
||||||
DSAKeyPairGenerationConfig(unsigned int modulus_bits, int divisor_bits)
|
DSAKeyPairGenerationConfig(unsigned int modulus_bits, int divisor_bits)
|
||||||
@ -6293,6 +6367,44 @@ void GenerateKeyPairRSA(const FunctionCallbackInfo<Value>& args) {
|
|||||||
GenerateKeyPair(args, 2, std::move(config));
|
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) {
|
void GenerateKeyPairDSA(const FunctionCallbackInfo<Value>& args) {
|
||||||
CHECK(args[0]->IsUint32());
|
CHECK(args[0]->IsUint32());
|
||||||
const uint32_t modulus_bits = args[0].As<Uint32>()->Value();
|
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, "pbkdf2", PBKDF2);
|
||||||
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
|
env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
|
||||||
|
env->SetMethod(target, "generateKeyPairRSAPSS", GenerateKeyPairRSAPSS);
|
||||||
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
|
env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
|
||||||
env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
|
env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
|
||||||
env->SetMethod(target, "generateKeyPairNid", GenerateKeyPairNid);
|
env->SetMethod(target, "generateKeyPairNid", GenerateKeyPairNid);
|
||||||
|
@ -670,7 +670,7 @@ class Sign : public SignBase {
|
|||||||
SignResult SignFinal(
|
SignResult SignFinal(
|
||||||
const ManagedEVPPKey& pkey,
|
const ManagedEVPPKey& pkey,
|
||||||
int padding,
|
int padding,
|
||||||
int saltlen);
|
const v8::Maybe<int>& saltlen);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
@ -691,7 +691,7 @@ class Verify : public SignBase {
|
|||||||
const char* sig,
|
const char* sig,
|
||||||
int siglen,
|
int siglen,
|
||||||
int padding,
|
int padding,
|
||||||
int saltlen,
|
const v8::Maybe<int>& saltlen,
|
||||||
bool* verify_result);
|
bool* verify_result);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
24
test/fixtures/keys/Makefile
vendored
24
test/fixtures/keys/Makefile
vendored
@ -37,6 +37,12 @@ all: \
|
|||||||
rsa_public_1024.pem \
|
rsa_public_1024.pem \
|
||||||
rsa_public_2048.pem \
|
rsa_public_2048.pem \
|
||||||
rsa_public_4096.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
|
# Create Certificate Authority: ca1
|
||||||
@ -574,6 +580,24 @@ rsa_public_2048.pem: rsa_private_2048.pem
|
|||||||
rsa_public_4096.pem: rsa_private_4096.pem
|
rsa_public_4096.pem: rsa_private_4096.pem
|
||||||
openssl rsa -in rsa_private_4096.pem -pubout -out rsa_public_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:
|
clean:
|
||||||
rm -f *.pfx *.pem *.srl ca2-database.txt ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem
|
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
|
@> fake-startcom-root-database.txt
|
||||||
|
28
test/fixtures/keys/rsa_pss_private_2048.pem
vendored
Normal file
28
test/fixtures/keys/rsa_pss_private_2048.pem
vendored
Normal 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-----
|
29
test/fixtures/keys/rsa_pss_private_2048_sha256_sha256_16.pem
vendored
Normal file
29
test/fixtures/keys/rsa_pss_private_2048_sha256_sha256_16.pem
vendored
Normal 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-----
|
29
test/fixtures/keys/rsa_pss_private_2048_sha512_sha256_20.pem
vendored
Normal file
29
test/fixtures/keys/rsa_pss_private_2048_sha512_sha256_20.pem
vendored
Normal 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-----
|
9
test/fixtures/keys/rsa_pss_public_2048.pem
vendored
Normal file
9
test/fixtures/keys/rsa_pss_public_2048.pem
vendored
Normal 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-----
|
10
test/fixtures/keys/rsa_pss_public_2048_sha256_sha256_16.pem
vendored
Normal file
10
test/fixtures/keys/rsa_pss_public_2048_sha256_sha256_16.pem
vendored
Normal 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-----
|
10
test/fixtures/keys/rsa_pss_public_2048_sha512_sha256_20.pem
vendored
Normal file
10
test/fixtures/keys/rsa_pss_public_2048_sha512_sha256_20.pem
vendored
Normal 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-----
|
28
test/fixtures/test_unknown_privkey.pem
vendored
28
test/fixtures/test_unknown_privkey.pem
vendored
@ -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-----
|
|
@ -8,6 +8,8 @@ const assert = require('assert');
|
|||||||
const {
|
const {
|
||||||
createCipheriv,
|
createCipheriv,
|
||||||
createDecipheriv,
|
createDecipheriv,
|
||||||
|
createSign,
|
||||||
|
createVerify,
|
||||||
createSecretKey,
|
createSecretKey,
|
||||||
createPublicKey,
|
createPublicKey,
|
||||||
createPrivateKey,
|
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'),
|
{ private: fixtures.readSync('test_ed25519_privkey.pem', 'ascii'),
|
||||||
public: fixtures.readSync('test_ed25519_pubkey.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.type, 'private');
|
||||||
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
|
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
|
||||||
assert.strictEqual(privateKey.symmetricKeySize, undefined);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -6,12 +6,15 @@ if (!common.hasCrypto)
|
|||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const {
|
const {
|
||||||
|
constants,
|
||||||
createSign,
|
createSign,
|
||||||
createVerify,
|
createVerify,
|
||||||
generateKeyPair,
|
generateKeyPair,
|
||||||
generateKeyPairSync,
|
generateKeyPairSync,
|
||||||
publicEncrypt,
|
publicEncrypt,
|
||||||
privateDecrypt
|
privateDecrypt,
|
||||||
|
sign,
|
||||||
|
verify
|
||||||
} = require('crypto');
|
} = require('crypto');
|
||||||
const { promisify } = require('util');
|
const { promisify } = require('util');
|
||||||
|
|
||||||
@ -40,13 +43,24 @@ function testEncryptDecrypt(publicKey, privateKey) {
|
|||||||
|
|
||||||
// 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 = Buffer.from('Hello Node.js world!');
|
||||||
const signature = createSign('SHA256').update(message)
|
|
||||||
.sign(privateKey, 'hex');
|
function oldSign(algo, data, key) {
|
||||||
for (const key of [publicKey, privateKey]) {
|
return createSign(algo).update(data).sign(key);
|
||||||
const okay = createVerify('SHA256').update(message)
|
}
|
||||||
.verify(key, signature, 'hex');
|
|
||||||
assert(okay);
|
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 = verifyFn('SHA256', message, key, signature);
|
||||||
|
assert(okay);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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 = {
|
const privateKeyEncoding = {
|
||||||
type: 'pkcs8',
|
type: 'pkcs8',
|
||||||
@ -440,9 +491,7 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher);
|
|||||||
common.expectsError(() => generateKeyPairSync('rsa2', {}), {
|
common.expectsError(() => generateKeyPairSync('rsa2', {}), {
|
||||||
type: TypeError,
|
type: TypeError,
|
||||||
code: 'ERR_INVALID_ARG_VALUE',
|
code: 'ERR_INVALID_ARG_VALUE',
|
||||||
message: "The argument 'type' must be one of " +
|
message: "The argument 'type' must be a supported key type. Received 'rsa2'"
|
||||||
"'rsa', 'dsa', 'ec', 'ed25519', 'ed448'," +
|
|
||||||
" 'x25519', 'x448'. Received 'rsa2'"
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user