Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(461)

Unified Diff: content/child/webcrypto/jwk.cc

Issue 287133004: [webcrypto] Add JWK import/export of RSA private keys (NSS). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add JWK private key import as well. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/child/webcrypto/jwk.cc
diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc
index 53dd787d7a36cc5ebe15fc9aedc7370a1d10aff4..2e67ddd6e94c5df7337fe14026a424c3d0520c8d 100644
--- a/content/child/webcrypto/jwk.cc
+++ b/content/child/webcrypto/jwk.cc
@@ -130,11 +130,8 @@
//
// kty-specific parameters
// The value of kty determines the type and content of the keying material
-// carried in the JWK to be imported. Currently only two possibilities are
-// supported: a raw key or an RSA public key. RSA private keys are not
-// supported because typical applications seldom need to import a private key,
-// and the large number of JWK parameters required to describe one.
-// - kty == "oct" (symmetric or other raw key)
+// carried in the JWK to be imported.
+// // - kty == "oct" (symmetric or other raw key)
// +-------+--------------------------------------------------------------+
// | "k" | Contains the value of the symmetric (or other single-valued) |
// | | key. It is represented as the base64url encoding of the |
@@ -150,6 +147,39 @@
// | | represented as the base64url encoding of the value's |
// | | unsigned big endian representation as an octet sequence. |
// +-------+--------------------------------------------------------------+
+// - If key == "RSA" and the "d" parameter is present then it is a private key.
+// All the parameters above for public keys apply, as well as the following.
+// (Note that except for "d", all of these are optional):
+// +-------+--------------------------------------------------------------+
+// | "d" | Contains the private exponent value for the RSA private key. |
+// | | It is represented as the base64url encoding of the value's |
+// | | unsigned big endian representation as an octet sequence. |
+// +-------+--------------------------------------------------------------+
+// | "p" | Contains the first prime factor value for the RSA private |
+// | | key. It is represented as the base64url encoding of the |
+// | | value's |
+// | | unsigned big endian representation as an octet sequence. |
+// +-------+--------------------------------------------------------------+
+// | "q" | Contains the second prime factor value for the RSA private |
+// | | key. It is represented as the base64url encoding of the |
+// | | value's unsigned big endian representation as an octet |
+// | | sequence. |
+// +-------+--------------------------------------------------------------+
+// | "dp" | Contains the first factor CRT exponent value for the RSA |
+// | | private key. It is represented as the base64url encoding of |
+// | | the value's unsigned big endian representation as an octet |
+// | | sequence. |
+// +-------+--------------------------------------------------------------+
+// | "dq" | Contains the second factor CRT exponent value for the RSA |
+// | | private key. It is represented as the base64url encoding of |
+// | | the value's unsigned big endian representation as an octet |
+// | | sequence. |
+// +-------+--------------------------------------------------------------+
+// | "dq" | Contains the first CRT coefficient value for the RSA private |
+// | | key. It is represented as the base64url encoding of the |
+// | | value's unsigned big endian representation as an octet |
+// | | sequence. |
+// +-------+--------------------------------------------------------------+
//
// Consistency and conflict resolution
// The 'algorithm', 'extractable', and 'usage_mask' input parameters
@@ -430,6 +460,25 @@ Status GetJwkBytes(base::DictionaryValue* dict,
return Status::Success();
}
+// Extracts the optional string property with key |path| from |dict| and saves
+// the base64url-decoded bytes to |*result|. If the property exist and is not a
+// string, or could not be base64url-decoded, returns an error.
+Status GetOptionalJwkBytes(base::DictionaryValue* dict,
+ const std::string& path,
+ std::string* result,
+ bool* property_exists) {
+ std::string base64_string;
+ Status status =
+ GetOptionalJwkString(dict, path, &base64_string, property_exists);
+ if (!*property_exists || status.IsError())
Ryan Sleevi 2014/05/19 00:10:34 nit: Check status.IsError() before doing *property
eroman 2014/05/19 18:51:09 Good point! Done. Reading the boolean first could
+ return status;
+
+ if (!Base64DecodeUrlSafe(base64_string, result))
+ return Status::ErrorJwkBase64Decode(path);
+
+ return Status::Success();
+}
+
// Extracts the optional boolean property with key |path| from |dict| and saves
// the result to |*result| if it was found. If the property exists and is not a
// boolean, returns an error. Otherwise returns success, and sets
@@ -480,6 +529,53 @@ void WriteRsaPublicKey(const std::vector<uint8>& modulus,
jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent));
}
+// Writes an RSA private key to a JWK dictionary
+Status ExportRsaPrivateKeyJwk(const blink::WebCryptoKey& key,
+ base::DictionaryValue* jwk_dict) {
+ platform::PrivateKey* private_key;
+ Status status = ToPlatformPrivateKey(key, &private_key);
+ if (status.IsError())
+ return status;
+
+ // TODO(eroman): Copying the key properties to temporary vectors is
+ // inefficient. Once there aren't two implementations of platform_crypto this
+ // and other code will be easier to streamline.
+ std::vector<uint8> modulus;
+ std::vector<uint8> public_exponent;
+ std::vector<uint8> private_exponent;
+ std::vector<uint8> prime1;
+ std::vector<uint8> prime2;
+ std::vector<uint8> exponent1;
+ std::vector<uint8> exponent2;
+ std::vector<uint8> coefficient;
+
+ status = platform::ExportRsaPrivateKey(private_key,
+ &modulus,
+ &public_exponent,
+ &private_exponent,
+ &prime1,
+ &prime2,
+ &exponent1,
+ &exponent2,
+ &coefficient);
+ if (status.IsError())
+ return status;
+
+ jwk_dict->SetString("kty", "RSA");
+ jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus));
+ jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent));
+ jwk_dict->SetString("d", Base64EncodeUrlSafe(private_exponent));
+ // Although these are "optional", the JWA spec says that producers
+ // SHOULD include them.
Ryan Sleevi 2014/05/19 00:10:34 Doesn't matter what JWA says. The WebCrypto spec i
eroman 2014/05/19 18:51:09 Done, updated the comment.
+ jwk_dict->SetString("p", Base64EncodeUrlSafe(prime1));
+ jwk_dict->SetString("q", Base64EncodeUrlSafe(prime2));
+ jwk_dict->SetString("dp", Base64EncodeUrlSafe(exponent1));
+ jwk_dict->SetString("dq", Base64EncodeUrlSafe(exponent2));
+ jwk_dict->SetString("qi", Base64EncodeUrlSafe(coefficient));
+
+ return Status::Success();
+}
+
// Writes a Web Crypto usage mask to a JWK dictionary.
void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages,
base::DictionaryValue* jwk_dict) {
@@ -592,26 +688,16 @@ Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm,
return Status::Success();
}
-bool IsRsaPublicKey(const blink::WebCryptoKey& key) {
- if (key.type() != blink::WebCryptoKeyTypePublic)
- return false;
+bool IsRsaKey(const blink::WebCryptoKey& key) {
const blink::WebCryptoAlgorithmId algorithm_id = key.algorithm().id();
return algorithm_id == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
algorithm_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
algorithm_id == blink::WebCryptoAlgorithmIdRsaOaep;
}
-// TODO(padolph): This function is duplicated in shared_crypto.cc
-Status ToPlatformPublicKey(const blink::WebCryptoKey& key,
- platform::PublicKey** out) {
- *out = static_cast<platform::Key*>(key.handle())->AsPublicKey();
- if (!*out)
- return Status::ErrorUnexpectedKeyType();
- return Status::Success();
-}
-
} // namespace
+// TODO(eroman): Split this up into smaller functions.
Ryan Sleevi 2014/05/19 00:10:34 I would rather see RSA Private key import split ou
eroman 2014/05/19 18:51:09 Done.
Status ImportKeyJwk(const CryptoData& key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
@@ -753,13 +839,6 @@ Status ImportKeyJwk(const CryptoData& key_data,
// (private exponent) entry.
// See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
// section 6.3.
-
- // RSA private key import is not currently supported, so fail here if a "d"
- // entry is found.
- // TODO(padolph): Support RSA private key import.
- if (dict_value->HasKey("d"))
- return Status::ErrorJwkRsaPrivateKeyUnsupported();
-
std::string jwk_n_value;
status = GetJwkBytes(dict_value, "n", &jwk_n_value);
if (status.IsError())
@@ -769,13 +848,68 @@ Status ImportKeyJwk(const CryptoData& key_data,
if (status.IsError())
return status;
- return platform::ImportRsaPublicKey(algorithm,
- extractable,
- usage_mask,
- CryptoData(jwk_n_value),
- CryptoData(jwk_e_value),
- key);
+ if (!dict_value->HasKey("d")) {
+ return platform::ImportRsaPublicKey(algorithm,
+ extractable,
+ usage_mask,
+ CryptoData(jwk_n_value),
+ CryptoData(jwk_e_value),
+ key);
+ }
+
+ std::string jwk_d_value;
+ status = GetJwkBytes(dict_value, "d", &jwk_d_value);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_p_value;
+ bool has_p;
+ status = GetOptionalJwkBytes(dict_value, "p", &jwk_p_value, &has_p);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_q_value;
+ bool has_q;
+ status = GetOptionalJwkBytes(dict_value, "q", &jwk_q_value, &has_q);
+ if (status.IsError())
+ return status;
+ std::string jwk_dp_value;
+ bool has_dp;
+ status = GetOptionalJwkBytes(dict_value, "dp", &jwk_dp_value, &has_dp);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_dq_value;
+ bool has_dq;
+ status = GetOptionalJwkBytes(dict_value, "dq", &jwk_dq_value, &has_dq);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_qi_value;
+ bool has_qi;
+ status = GetOptionalJwkBytes(dict_value, "qi", &jwk_qi_value, &has_qi);
+ if (status.IsError())
+ return status;
+
+ return platform::ImportRsaPrivateKey(
+ algorithm,
+ extractable,
+ usage_mask,
+ CryptoData(jwk_n_value), // modulus
+ CryptoData(jwk_e_value), // public_exponent
+ CryptoData(jwk_d_value), // private_exponent
+ has_p,
+ CryptoData(jwk_p_value), // prime1
+ has_q,
+ CryptoData(jwk_q_value), // prime2
+ has_dp,
+ CryptoData(jwk_dp_value), // exponent1
+ has_dq,
+ CryptoData(jwk_dq_value), // exponent2
+ has_qi,
+ CryptoData(jwk_qi_value), // coefficient
+ key);
}
return Status::ErrorJwkUnrecognizedKty();
@@ -797,8 +931,8 @@ Status ExportKeyJwk(const blink::WebCryptoKey& key,
break;
}
case blink::WebCryptoKeyTypePublic: {
- // Currently only RSA public key export is supported.
- if (!IsRsaPublicKey(key))
+ // TODO(eroman): Update when there are asymmetric keys other than RSA.
+ if (!IsRsaKey(key))
return Status::ErrorUnsupported();
platform::PublicKey* public_key;
status = ToPlatformPublicKey(key, &public_key);
@@ -813,7 +947,17 @@ Status ExportKeyJwk(const blink::WebCryptoKey& key,
WriteRsaPublicKey(modulus, public_exponent, &jwk_dict);
break;
}
- case blink::WebCryptoKeyTypePrivate: // TODO(padolph)
+ case blink::WebCryptoKeyTypePrivate: {
+ // TODO(eroman): Update when there are asymmetric keys other than RSA.
+ if (!IsRsaKey(key))
+ return Status::ErrorUnsupported();
+
+ status = ExportRsaPrivateKeyJwk(key, &jwk_dict);
+ if (status.IsError())
+ return status;
+ break;
+ }
+
default:
return Status::ErrorUnsupported();
}
« no previous file with comments | « no previous file | content/child/webcrypto/platform_crypto.h » ('j') | content/child/webcrypto/platform_crypto_nss.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698