Skip to content
File

Blob: src/workerd/api/node/tests/crypto_keys-test.js

javascript2194 lines
1// Copyright (c) 2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { deepStrictEqual, strictEqual, ok, rejects, throws } from 'node:assert';
5import {
6 KeyObject,
7 SecretKeyObject,
8 PrivateKeyObject,
9 createSecretKey,
10 createPrivateKey,
11 createPublicKey,
12 createHmac,
13 generateKey,
14 generateKeySync,
15 generateKeyPair,
16 generateKeyPairSync,
17 sign,
18 verify,
19} from 'node:crypto';
20import { Buffer } from 'node:buffer';
21import { promisify } from 'node:util';
22 
23export const secret_key_equals_test = {
24 async test() {
25 const secretKeyData1 = Buffer.from('abcdefghijklmnop');
26 const secretKeyData2 = Buffer.from('abcdefghijklmnop'.repeat(2));
27 const aes1 = await crypto.subtle.importKey(
28 'raw',
29 secretKeyData1,
30 { name: 'AES-CBC' },
31 true,
32 ['encrypt', 'decrypt']
33 );
34 const aes2 = await crypto.subtle.importKey(
35 'raw',
36 secretKeyData1,
37 { name: 'AES-CBC' },
38 true,
39 ['encrypt', 'decrypt']
40 );
41 const aes3 = await crypto.subtle.importKey(
42 'raw',
43 secretKeyData2,
44 { name: 'AES-CBC' },
45 true,
46 ['encrypt', 'decrypt']
47 );
48 const hmac = await crypto.subtle.importKey(
49 'raw',
50 secretKeyData1,
51 {
52 name: 'HMAC',
53 hash: 'SHA-256',
54 },
55 true,
56 ['sign', 'verify']
57 );
58 const hmac2 = await crypto.subtle.importKey(
59 'raw',
60 secretKeyData1,
61 {
62 name: 'HMAC',
63 hash: 'SHA-256',
64 },
65 false,
66 ['sign', 'verify']
67 );
68 
69 const aes1_ko = KeyObject.from(aes1);
70 const aes2_ko = KeyObject.from(aes2);
71 const aes3_ko = KeyObject.from(aes3);
72 const hmac_ko = KeyObject.from(hmac);
73 const hmac2_ko = KeyObject.from(hmac2);
74 
75 strictEqual(aes1_ko.type, 'secret');
76 strictEqual(aes2_ko.type, 'secret');
77 strictEqual(aes3_ko.type, 'secret');
78 strictEqual(hmac_ko.type, 'secret');
79 
80 ok(aes1_ko.equals(aes1_ko));
81 ok(aes1_ko.equals(aes2_ko));
82 ok(aes1_ko.equals(hmac_ko));
83 ok(hmac_ko.equals(aes1_ko));
84 ok(hmac_ko.equals(aes2_ko));
85 
86 ok(!aes1_ko.equals(aes3_ko));
87 ok(!hmac_ko.equals(aes3_ko));
88 
89 // Unable to determine equality if either key is not extractable.
90 ok(!hmac_ko.equals(hmac2_ko));
91 ok(!hmac2_ko.equals(hmac_ko));
92 },
93};
94 
95// In case anyone comes across these tests and wonders why there are
96// keys embedded... these are test keys only. They are not sensitive
97// or secret in any way. They are used only in the test here and are
98// pulled directly from MDN examples.
99 
100const jwkEcKey = {
101 crv: 'P-384',
102 d: 'wouCtU7Nw4E8_7n5C1-xBjB4xqSb_liZhYMsy8MGgxUny6Q8NCoH9xSiviwLFfK_',
103 ext: true,
104 key_ops: ['sign'],
105 kty: 'EC',
106 x: 'SzrRXmyI8VWFJg1dPUNbFcc9jZvjZEfH7ulKI1UkXAltd7RGWrcfFxqyGPcwu6AQ',
107 y: 'hHUag3OvDzEr0uUQND4PXHQTXP5IDGdYhJhL-WLKjnGjQAw0rNGy5V29-aV-yseW',
108};
109 
110const pemEncodedKey = `
111MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy3Xo3U13dc+xojwQYWoJLCbOQ5fOVY8LlnqcJm1W1BFtxIhOAJWohiHuIRMctv7d
112zx47TLlmARSKvTRjd0dF92jx/xY20Lz+DXp8YL5yUWAFgA3XkO3LSJ
113gEOex10NB8jfkmgSb7QIudTVvbbUDfd5fwIBmCtaCwWx7NyeWWDb7A
1149cFxj7EjRdrDaK3ux/ToMLHFXVLqSL341TkCf4ZQoz96RFPUGPPLOf
115vN0x66CM1PQCkdhzjE6U5XGE964ZkkYUPPsy6Dcie4obhW4vDjgUmL
116zv0z7UD010RLIneUgDE2FqBfY/C+uWigNPBPkkQ+Bv/UigS6dHqTCV
117eD5wgyBQIDAQAB
118`;
119 
120export const asymmetric_key_equals_test = {
121 async test() {
122 const jwk1_ec = await crypto.subtle.importKey(
123 'jwk',
124 jwkEcKey,
125 {
126 name: 'ECDSA',
127 namedCurve: 'P-384',
128 },
129 true,
130 ['sign']
131 );
132 const jwk2_ec = await crypto.subtle.importKey(
133 'jwk',
134 jwkEcKey,
135 {
136 name: 'ECDSA',
137 namedCurve: 'P-384',
138 },
139 true,
140 ['sign']
141 );
142 strictEqual(jwk1_ec.type, 'private');
143 
144 // fetch the part of the PEM string between header and footer
145 const pemContent = Buffer.from(pemEncodedKey, 'base64');
146 
147 const rsa = await crypto.subtle.importKey(
148 'spki',
149 pemContent,
150 {
151 name: 'RSA-OAEP',
152 hash: 'SHA-256',
153 },
154 true,
155 ['encrypt']
156 );
157 const rsa2 = await crypto.subtle.importKey(
158 'spki',
159 pemContent,
160 {
161 name: 'RSA-OAEP',
162 hash: 'SHA-256',
163 },
164 false,
165 ['encrypt']
166 );
167 strictEqual(rsa.type, 'public');
168 
169 const jwk1_ko = KeyObject.from(jwk1_ec);
170 const jwk2_ko = KeyObject.from(jwk2_ec);
171 const rsa_ko = KeyObject.from(rsa);
172 const rsa2_ko = KeyObject.from(rsa2);
173 
174 strictEqual(jwk1_ko.asymmetricKeyType, 'ec');
175 strictEqual(rsa_ko.asymmetricKeyType, 'rsa');
176 strictEqual(rsa_ko.asymmetricKeyDetails.modulusLength, 2048);
177 strictEqual(rsa_ko.asymmetricKeyDetails.publicExponent, 65537n);
178 strictEqual(jwk1_ko.asymmetricKeyDetails.namedCurve, 'secp384r1');
179 
180 ok(jwk1_ko.equals(jwk1_ko));
181 ok(jwk1_ko.equals(jwk2_ko));
182 ok(!rsa_ko.equals(jwk1_ko));
183 ok(!jwk1_ko.equals(rsa_ko));
184 ok(!rsa_ko.equals(rsa2_ko));
185 
186 jwk1_ko.export({
187 type: 'pkcs8',
188 format: 'der',
189 cipher: 'aes-128-cbc',
190 passphrase: Buffer.alloc(0),
191 });
192 jwk1_ko.export({
193 type: 'pkcs8',
194 format: 'der',
195 cipher: 'aes-128-cbc',
196 passphrase: '',
197 });
198 },
199};
200 
201export const secret_key_test = {
202 test() {
203 const buf = Buffer.from('hello');
204 strictEqual(buf.toString(), 'hello');
205 const key1 = createSecretKey('hello');
206 const key2 = createSecretKey(buf);
207 const key3 = createSecretKey('there');
208 
209 // Creating a zero-length key should also just work.
210 const key4 = createSecretKey('');
211 const key5 = createSecretKey(Buffer.alloc(0));
212 ok(key4.equals(key5));
213 strictEqual(key4.export().toString(), '');
214 
215 // The key should be immutable after creation,
216 // so modifying the buf now should have no impact
217 // on the test.
218 buf.fill(0);
219 strictEqual(buf.toString(), '\0\0\0\0\0');
220 
221 // Now let's make sure the keys we created meet our expectations.
222 strictEqual(key1.export().toString(), 'hello');
223 strictEqual(key2.export().toString(), 'hello');
224 strictEqual(key3.export({ format: 'buffer' }).toString(), 'there');
225 
226 strictEqual(key1.toString(), '[object KeyObject]');
227 strictEqual(key2.toString(), '[object KeyObject]');
228 strictEqual(key3.toString(), '[object KeyObject]');
229 ok(key1 instanceof SecretKeyObject);
230 ok(key2 instanceof SecretKeyObject);
231 ok(key3 instanceof SecretKeyObject);
232 strictEqual(key1.type, 'secret');
233 strictEqual(key2.type, 'secret');
234 strictEqual(key3.type, 'secret');
235 ok(key1.equals(key2));
236 ok(!key1.equals(key3));
237 ok(!key3.equals(key1));
238 
239 strictEqual(key1.symmetricKeySize, 5);
240 
241 const jwk = key3.export({ format: 'jwk' });
242 ok(jwk);
243 strictEqual(typeof jwk, 'object');
244 strictEqual(jwk.kty, 'oct');
245 strictEqual(jwk.ext, true);
246 strictEqual(jwk.k, 'dGhlcmU');
247 },
248};
249 
250export const create_private_key_test_generic = {
251 test(_, env) {
252 // TODO(later): These error messages are inconsistent with one another
253 // despite performing the same basic validation.
254 throws(() => createPrivateKey(1), {
255 message: /The "options.key" property must be of type string/,
256 });
257 throws(() => createPrivateKey(true), {
258 message: /The "options.key" property must be of type string/,
259 });
260 throws(() => createPrivateKey({ key: 1 }), {
261 message: /The "key" argument/,
262 });
263 throws(() => createPrivateKey({ key: true }), {
264 message: /The "key" argument/,
265 });
266 
267 // The "bad" cases here are just the ones that we expect to fail
268 // creating as private keys, generally because boringssl does not
269 // support these variations.
270 [
271 'dh_private.pem',
272 'ec_secp256k1_private.pem',
273 'ed448_private.pem',
274 'rsa_pss_private_2048.pem',
275 'rsa_pss_private_2048_sha1_sha1_20.pem',
276 'rsa_pss_private_2048_sha256_sha256_16.pem',
277 'rsa_pss_private_2048_sha512_sha256_20.pem',
278 'x448_private.pem',
279 ].forEach((i) => {
280 throws(() => createPrivateKey(env[i]), {
281 message: 'Failed to parse private key',
282 });
283 });
284 
285 // These next three are encrypted and require a passphrase.
286 createPrivateKey({
287 key: env['rsa_private_encrypted.pem'],
288 passphrase: 'password',
289 });
290 
291 // Trying to parse an encrypted private key without specifying a
292 // passphrase, or specifying the wrong passphrase, should fail.
293 throws(
294 () => {
295 createPrivateKey({
296 key: env['rsa_private_encrypted.pem'],
297 passphrase: 'password_bad',
298 });
299 },
300 { message: 'Failed to parse private key' }
301 );
302 
303 throws(
304 () => {
305 createPrivateKey({
306 key: env['rsa_private_encrypted.pem'],
307 });
308 },
309 { message: 'Failed to parse private key' }
310 );
311 
312 createPrivateKey({
313 key: env['dsa_private_encrypted.pem'],
314 passphrase: 'password',
315 });
316 
317 createPrivateKey({
318 key: env['dsa_private_encrypted_1025.pem'],
319 passphrase: 'secret',
320 });
321 
322 [
323 'dsa_private.pem',
324 'dsa_private_1025.pem',
325 'dsa_private_pkcs8.pem',
326 'ed25519_private.pem',
327 'ec_p256_private.pem',
328 'ec_p384_private.pem',
329 'ec_p521_private.pem',
330 'rsa_private.pem',
331 'rsa_private_b.pem',
332 'rsa_private_2048.pem',
333 'rsa_private_pkcs8_bad.pem',
334 'rsa_private_4096.pem',
335 'rsa_private_pkcs8.pem',
336 'x25519_private.pem',
337 ].forEach((i) => {
338 try {
339 const key = createPrivateKey(env[i]);
340 
341 // These variations should also just work.
342 const buf = Buffer.from(env[i]);
343 createPrivateKey({ key: env[i] });
344 createPrivateKey(buf);
345 createPrivateKey(buf.buffer);
346 createPrivateKey(new DataView(buf.buffer));
347 createPrivateKey({ key: buf });
348 createPrivateKey({ key: buf.buffer });
349 createPrivateKey({ key: new DataView(buf.buffer) });
350 
351 ok(key instanceof PrivateKeyObject);
352 
353 const details = key.asymmetricKeyDetails;
354 switch (key.asymmetricKeyType) {
355 case 'dsa': {
356 switch (i) {
357 case 'dsa_private.pem': {
358 strictEqual(details.modulusLength, 2048);
359 strictEqual(details.divisorLength, 256);
360 break;
361 }
362 case 'dsa_private_1025.pem': {
363 strictEqual(details.modulusLength, 1088);
364 strictEqual(details.divisorLength, 160);
365 break;
366 }
367 case 'dsa_private_pkcs8.pem': {
368 strictEqual(details.modulusLength, 2048);
369 strictEqual(details.divisorLength, 256);
370 break;
371 }
372 }
373 break;
374 }
375 case 'rsa': {
376 switch (i) {
377 case 'rsa_private.pem': {
378 strictEqual(details.modulusLength, 2048);
379 strictEqual(details.publicExponent, 65537n);
380 break;
381 }
382 case 'rsa_private_b.pem': {
383 strictEqual(details.modulusLength, 2048);
384 strictEqual(details.publicExponent, 65537n);
385 break;
386 }
387 case 'rsa_private_2048.pem': {
388 strictEqual(details.modulusLength, 2048);
389 strictEqual(details.publicExponent, 65537n);
390 break;
391 }
392 case 'rsa_private_pkcs8_bad.pem': {
393 strictEqual(details.modulusLength, 2048);
394 strictEqual(details.publicExponent, 65537n);
395 break;
396 }
397 case 'rsa_private_4096.pem': {
398 strictEqual(details.modulusLength, 4096);
399 strictEqual(details.publicExponent, 65537n);
400 break;
401 }
402 case 'rsa_private_pkcs8.pem': {
403 strictEqual(details.modulusLength, 2048);
404 strictEqual(details.publicExponent, 65537n);
405 break;
406 }
407 }
408 break;
409 }
410 case 'ed25519': {
411 break;
412 }
413 case 'x25519': {
414 break;
415 }
416 case 'ec': {
417 switch (i) {
418 case 'ec_p256_private.pem': {
419 strictEqual(details.namedCurve, 'prime256v1');
420 break;
421 }
422 case 'ec_p384_private.pem': {
423 strictEqual(details.namedCurve, 'secp384r1');
424 break;
425 }
426 case 'ec_p521_private.pem': {
427 strictEqual(details.namedCurve, 'secp521r1');
428 break;
429 }
430 }
431 break;
432 }
433 default:
434 throw new Error('Invalid key type');
435 }
436 
437 // Export/Import roundtrip should work
438 {
439 const exported = key.export({ format: 'pem', type: 'pkcs8' });
440 const key2 = createPrivateKey(exported);
441 const exported2 = key2.export({ format: 'pem', type: 'pkcs8' });
442 strictEqual(exported.toString(), exported2.toString());
443 ok(key.equals(key2));
444 }
445 {
446 const exported = key.export({ format: 'der', type: 'pkcs8' });
447 const key2 = createPrivateKey({ key: exported, format: 'der' });
448 const exported2 = key2.export({ format: 'der', type: 'pkcs8' });
449 strictEqual(exported.toString(), exported2.toString());
450 ok(key.equals(key2));
451 }
452 
453 if (key.asymmetricKeyType == 'ec') {
454 const exported = key.export({ format: 'der', type: 'sec1' });
455 const key2 = createPrivateKey({
456 key: exported,
457 format: 'der',
458 type: 'sec1',
459 });
460 const exported2 = key2.export({ format: 'der', type: 'sec1' });
461 strictEqual(exported.toString(), exported2.toString());
462 } else {
463 throws(() => key.export({ format: 'der', type: 'sec1' }), {
464 message: 'SEC1 can only be used for EC keys',
465 });
466 }
467 } catch (err) {
468 console.log(`${i} FAILED`, err.message);
469 }
470 });
471 },
472};
473 
474export const private_key_import_type = {
475 test() {
476 // This is a DER-encoded PKCS8 ed25519 private key. In this case, just like
477 // with Node.js, the specific "type" argument ends up being ignored.
478 // The actual behavior in Node.js is a bit inconsistent and depends on
479 // the type of key. For instance, for RSA keys, the type argument is
480 // respected and it is not always possible, for instance, to import a
481 // PKCS1 exported key as PKCS8, etc. The edges cases here appear to be
482 // undocumented in Node.js and may not be fully tested in Node.js either.
483 const derData = Buffer.from([
484 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
485 0x04, 0x22, 0x04, 0x20, 0xc1, 0x52, 0xba, 0x33, 0x74, 0x8c, 0x85, 0x08,
486 0x77, 0x34, 0xaf, 0xbb, 0x19, 0x1a, 0xd2, 0x57, 0xe0, 0x55, 0x59, 0x0c,
487 0x75, 0x14, 0xee, 0x69, 0x5b, 0xc8, 0x61, 0x41, 0xeb, 0xbf, 0x35, 0xd0,
488 ]);
489 
490 // All three of these variations should work, depsite the type being different.
491 const k1 = createPrivateKey({ key: derData, format: 'der', type: 'pkcs1' });
492 const k2 = createPrivateKey({ key: derData, format: 'der', type: 'pkcs8' });
493 const k3 = createPrivateKey({ key: derData, format: 'der', type: 'sec1' });
494 
495 const t1 = k1.export({ format: 'pem', type: 'pkcs8' });
496 const t2 = k2.export({ format: 'pem', type: 'pkcs8' });
497 const t3 = k3.export({ format: 'pem', type: 'pkcs8' });
498 strictEqual(t1.toString(), t2.toString());
499 strictEqual(t1.toString(), t3.toString());
500 
501 ok(k1.equals(k2));
502 ok(k2.equals(k3));
503 ok(k3.equals(k1));
504 },
505};
506 
507export const private_key_import_rsa_der = {
508 test() {
509 // A DER-encoded RSA PKCS8 private key
510 const derData = Buffer.from(
511 '30820942020100300d06092a864886f70d01010105000482092c3082092802' +
512 '01000282020100c5e4adc287db8ed66ea25e25c0a9f5c34f00cdc48d2c77c2' +
513 'a8d1476be5bb7abf22b3b2d0b0f963b4f4f8ca7e25b1aa3cde780716f482de' +
514 'd6cc891343a541d8a87db006e524b8e982b589192e2d37d3b1f40b071557a5' +
515 'b93f7b4d12df34209f8faa5471b539277b352d6ce6ac6c88bf6a8ae75561be' +
516 'a6c82a0c6ac5678a13b16b621344af8bbb03a8de44d9d8a3987fd8627967c2' +
517 'ab338331ddb409c0469d5b1b7e04eb8b2a58298ba0609f3dd1edca6fc249dc' +
518 '4e2ceb03575e63af0383650fd3259e6d864c0a651c6eee0f5fc25840d4f888' +
519 'e560cfe2b79735aa9ad9b4a0af3a40963404c58b383742fba24495389417f4' +
520 '111aa16b6af0d2d4f0ca4c746a073435345ebeb42dfaf9f19e6a9fc44612f9' +
521 '53c6809b3c9ccf576ce0cc3fe5984a9fb7a3677d3725bde037b1df314ab381' +
522 '527e28dd2f214b46fd4aeecf06e9078ccf7afded117a822c852c179504cbaf' +
523 '616df0ac2ea723e6d65b4d7130ed8ea1dc21653a060fa43f2f66c5256d1866' +
524 '5800fb6a1b334103f9bea0e8dafce48fc406e53dd967ebdd43683b99b399ee' +
525 '6421b019c2da256822ca8aaf5c07325c3645c87e0e65987caec04b29b9c55c' +
526 '767fce21b4700a3313281910f25aa69c3c2f1fda772ed62ff4898f1a2888a6' +
527 'a973b0677f2f1e48b577f9700ac612d6fcc43a0a127e7bff04c40634d9c6b8' +
528 '3290afab0224f537d5e8533ca561aa1f34058f4bf734bd0203010001028202' +
529 '00536fc7936d94b4f4d450c14149aa5f64a9babd07523e9d80058db77f56ad' +
530 '6563914e12e6cab75bc2c046e599aa6aee4c1bc09fbc9dfb4fd96103aa8baa' +
531 'f1c857c226a5c1976a1f8a6ce0112dd702e2cef50671461e5e516ce29fec85' +
532 '0f8571c1311fc9918f37864b358be4f66e0c7a2881c867c77e8af37a4721fd' +
533 '795a4e534fe35a1c6ba78e824c80eaa6af20cafb9c5068bfc6e44823d8b291' +
534 '664b1b7add1f0a5328bcd46db796975825cbfae737a34757bcfb7914dda3c8' +
535 'b85ee22c544007d6a4a5a92a0677fb350a4a91256ff065db245d12249482b3' +
536 'ce7cd02d5a6b25767a24da69e8a07a63526aa650245a669672e18348ebf17a' +
537 'f869afdc9bbfb9b4af1f3a3140b96f814a9602756c862bc5d5324898748888' +
538 'b22f04a3e9dbd483ea6696213d430bdb0a0b813582aed53b8efada5d7fff31' +
539 '0e018ed72b467b2eb6d93b85971e7b2014ea46b6143783d422fd278cea66f9' +
540 'c442530540539a4846b35c7c8e897371adf49aabfc4fadd6e0f5e45c799974' +
541 'aeefc87e6eb1e888b3478651e6982bcf765a07f1081a36e0908d42201c1ffc' +
542 '13c15e25e3244ea9e1f65784a16c86260b395afbeeefb0f616840c1047d787' +
543 'd3ee2881391a310ae6d7aa65664d1ecd55fd04a2bc2939dde06f7f4f5f15b2' +
544 'd41b8a8c05dbf70a9c62823aaae6a11daf1ce92c41d5095669f0091a94d6cc' +
545 'a6071858d06488a6cf7cbfe954604c4a410282010100fee558136fc778d715' +
546 'fee58023d2eca403571c9137dd314c67c4aec8e62da77318262ce6435b93ec' +
547 'e9b62c46a70e7362cfe7c0421ac4be8d6c0c817dc4e01d7b6aa3207fa20f37' +
548 '0bbc53e00b782442ca458d4df533d38ee71023cc1c5f2f7283886d8cb664d1' +
549 '84db8a257e7a7823aa904a7a58a696380df971c68a607c4dccc4b8f70782bf' +
550 'de593e947dab4275b20cc3ec7b55878cae5c8d35fc10bc174466623b6cb94a' +
551 '3e3e607ab0ed8af78baaaa8d063d4a2cecc860adb7dfdcbdfa12438f826462' +
552 '035015672d6c28ac542aca464379b57e0f8203ff86646724bd8915a2dd6516' +
553 'bc26ad2d9da24f04853d4771525d7befbe8f1ef326c44b80546f2875e49702' +
554 '82010100c6c01fc484238e32a6bb2242cc8bf11bb967bb3a2f772e0e11d4ae' +
555 '24be322b99b7e011c91e7fb21de44872b0e724a5135187df707fce8ce9c94b' +
556 'de0103ac07d6d12ebc6433e8b57a529c00a59d4cff699d9cfd1e0b62f626d4' +
557 '912fc99dc5fcaa5c396858ca43eeb9dc059e8284b119eac5986ba581413011' +
558 '98f55e3f252934d1b6ab29b1377fef44118606e9c3de3eb35f4334b3358ae6' +
559 '253eaac603cb72b308a1bc154ed7c70cd16cfee2011443e47a6d666cb65b9d' +
560 '84260d6e8669555c9326df648405fac3da80e69678c0a9a65daa2e4cab67b8' +
561 'b81ddd84cf51c09a694802c6058ea4f4379a9c015b3a56b0309ae2da993b07' +
562 '4a610a7f83376a60d3e5b7cb028201005e4f0cdf64243199a311c4683cd8f5' +
563 'a5597709a2d1408dd4ef2fde5b868eadbdefd970136228a7faa81e37138d0b' +
564 'd3b563a7238351d4298cb9c586c3b9ec11fc6fe01b4e1deff335ec603c2d02' +
565 '2ea8679e8441abcf991eee6f124f9acfbd06699438b42f67edfd721d12f250' +
566 'edd284710e9d65df7d05106692aa1ad8c82520f648595df60a77821d9d6341' +
567 'd23d29bb7f6227dfe55f2fc41e9b32c01e579d7f24294878e5f751acf0b835' +
568 'ab8d1ba7f1a26c0491453df6858ec0d19b22cf3ba2b39e52f5d0b3f8b74c1f' +
569 '108d7236c2d06c76c3a7f8a4ea45c8bbad4df2b29dc6bc93826deb01783732' +
570 'ae79c5b27e94771d0f960cb377880f77e15781e5feda5fd1028201007f3514' +
571 'a018db10f64654dbd6d94870678841664a157b3854f500a4fd0b66dd1523e5' +
572 '1c3d17722fb4861a009e4d32dd1d023feeb8f874612879183fdd725637263c' +
573 'f8a6c79399cc1da0a60c9bf394069db8ad742c38a97c56da129afd7627f451' +
574 'ad7968d9fb8b834e1e0ed2a742fa7f560e6641efca4cc8d15a8f216555098c' +
575 'aef5359417c327f52221fd208b9a3bb2f1e7750253f95f0f72a32b7655936f' +
576 'b43b40193ba21ce55fc4e2f837faecd78f72f4766bfa43a50ba1b75318606e' +
577 'ac33dadb7c602bdb966351c14469c116544efacf6b6f0191eef5de84549544' +
578 'ab0fdb713b00ef8d9069ce612f550e7fd1812a812bdc8b355d5bc2f65e2ba7' +
579 'c0959f20050282010100baf8445f5361adfb0889993acfaf27c14644a33387' +
580 '5114c0ed8fbee213be44edbbfec4e088f2c40a397640289245900860017228' +
581 '83249118516a9c517e39a900a13a0b66354da877a7fdb60c83171379292f1c' +
582 '137d8d5d5deb257b376c166620a2d23c44dfdee20fb84a453e3a5d479bf98e' +
583 '557e4c38c7e11ac8b44de10c2bf2a535e15d73265a9dd57d86e011f20fd37b' +
584 '91da22212fefe0dc152a6ebcb03c4b1356248d34cfee0434326a65cb5c1d5b' +
585 '43623f39be3523cd8f0f588d0aa1a2ed9bd9dde5581a0513432c3a4383bc82' +
586 '54cf3fd702485557c04f601db2def13a9f4d9096acce9cac65fa05bea1576b' +
587 '6a36c03eec9ebd53d346bb0e4636a5e369f5',
588 'hex'
589 );
590 
591 // Just like with Node.js, it is possible to import a PKCS8 private key (in this case an
592 // rsa key) using any of pkcs1, pkcs8, or sec1 as the type value.
593 const k1 = createPrivateKey({ key: derData, format: 'der', type: 'pkcs1' });
594 const k2 = createPrivateKey({ key: derData, format: 'der', type: 'pkcs8' });
595 const k3 = createPrivateKey({ key: derData, format: 'der', type: 'sec1' });
596 ok(k1.equals(k2));
597 ok(k1.equals(k3));
598 ok(k3.equals(k2));
599 
600 // A DER-encoded RSA PKCS1 private key
601 const derData2 = Buffer.from(
602 '308209280201000282020100c5e4adc287db8ed66ea25e25c0a9f5c34f' +
603 '00cdc48d2c77c2a8d1476be5bb7abf22b3b2d0b0f963b4f4f8ca7e25b1' +
604 'aa3cde780716f482ded6cc891343a541d8a87db006e524b8e982b58919' +
605 '2e2d37d3b1f40b071557a5b93f7b4d12df34209f8faa5471b539277b35' +
606 '2d6ce6ac6c88bf6a8ae75561bea6c82a0c6ac5678a13b16b621344af8b' +
607 'bb03a8de44d9d8a3987fd8627967c2ab338331ddb409c0469d5b1b7e04' +
608 'eb8b2a58298ba0609f3dd1edca6fc249dc4e2ceb03575e63af0383650f' +
609 'd3259e6d864c0a651c6eee0f5fc25840d4f888e560cfe2b79735aa9ad9' +
610 'b4a0af3a40963404c58b383742fba24495389417f4111aa16b6af0d2d4' +
611 'f0ca4c746a073435345ebeb42dfaf9f19e6a9fc44612f953c6809b3c9c' +
612 'cf576ce0cc3fe5984a9fb7a3677d3725bde037b1df314ab381527e28dd' +
613 '2f214b46fd4aeecf06e9078ccf7afded117a822c852c179504cbaf616d' +
614 'f0ac2ea723e6d65b4d7130ed8ea1dc21653a060fa43f2f66c5256d1866' +
615 '5800fb6a1b334103f9bea0e8dafce48fc406e53dd967ebdd43683b99b3' +
616 '99ee6421b019c2da256822ca8aaf5c07325c3645c87e0e65987caec04b' +
617 '29b9c55c767fce21b4700a3313281910f25aa69c3c2f1fda772ed62ff4' +
618 '898f1a2888a6a973b0677f2f1e48b577f9700ac612d6fcc43a0a127e7b' +
619 'ff04c40634d9c6b83290afab0224f537d5e8533ca561aa1f34058f4bf7' +
620 '34bd020301000102820200536fc7936d94b4f4d450c14149aa5f64a9ba' +
621 'bd07523e9d80058db77f56ad6563914e12e6cab75bc2c046e599aa6aee' +
622 '4c1bc09fbc9dfb4fd96103aa8baaf1c857c226a5c1976a1f8a6ce0112d' +
623 'd702e2cef50671461e5e516ce29fec850f8571c1311fc9918f37864b35' +
624 '8be4f66e0c7a2881c867c77e8af37a4721fd795a4e534fe35a1c6ba78e' +
625 '824c80eaa6af20cafb9c5068bfc6e44823d8b291664b1b7add1f0a5328' +
626 'bcd46db796975825cbfae737a34757bcfb7914dda3c8b85ee22c544007' +
627 'd6a4a5a92a0677fb350a4a91256ff065db245d12249482b3ce7cd02d5a' +
628 '6b25767a24da69e8a07a63526aa650245a669672e18348ebf17af869af' +
629 'dc9bbfb9b4af1f3a3140b96f814a9602756c862bc5d5324898748888b2' +
630 '2f04a3e9dbd483ea6696213d430bdb0a0b813582aed53b8efada5d7fff' +
631 '310e018ed72b467b2eb6d93b85971e7b2014ea46b6143783d422fd278c' +
632 'ea66f9c442530540539a4846b35c7c8e897371adf49aabfc4fadd6e0f5' +
633 'e45c799974aeefc87e6eb1e888b3478651e6982bcf765a07f1081a36e0' +
634 '908d42201c1ffc13c15e25e3244ea9e1f65784a16c86260b395afbeeef' +
635 'b0f616840c1047d787d3ee2881391a310ae6d7aa65664d1ecd55fd04a2' +
636 'bc2939dde06f7f4f5f15b2d41b8a8c05dbf70a9c62823aaae6a11daf1c' +
637 'e92c41d5095669f0091a94d6cca6071858d06488a6cf7cbfe954604c4a' +
638 '410282010100fee558136fc778d715fee58023d2eca403571c9137dd31' +
639 '4c67c4aec8e62da77318262ce6435b93ece9b62c46a70e7362cfe7c042' +
640 '1ac4be8d6c0c817dc4e01d7b6aa3207fa20f370bbc53e00b782442ca45' +
641 '8d4df533d38ee71023cc1c5f2f7283886d8cb664d184db8a257e7a7823' +
642 'aa904a7a58a696380df971c68a607c4dccc4b8f70782bfde593e947dab' +
643 '4275b20cc3ec7b55878cae5c8d35fc10bc174466623b6cb94a3e3e607a' +
644 'b0ed8af78baaaa8d063d4a2cecc860adb7dfdcbdfa12438f8264620350' +
645 '15672d6c28ac542aca464379b57e0f8203ff86646724bd8915a2dd6516' +
646 'bc26ad2d9da24f04853d4771525d7befbe8f1ef326c44b80546f2875e4' +
647 '970282010100c6c01fc484238e32a6bb2242cc8bf11bb967bb3a2f772e' +
648 '0e11d4ae24be322b99b7e011c91e7fb21de44872b0e724a5135187df70' +
649 '7fce8ce9c94bde0103ac07d6d12ebc6433e8b57a529c00a59d4cff699d' +
650 '9cfd1e0b62f626d4912fc99dc5fcaa5c396858ca43eeb9dc059e8284b1' +
651 '19eac5986ba58141301198f55e3f252934d1b6ab29b1377fef44118606' +
652 'e9c3de3eb35f4334b3358ae6253eaac603cb72b308a1bc154ed7c70cd1' +
653 '6cfee2011443e47a6d666cb65b9d84260d6e8669555c9326df648405fa' +
654 'c3da80e69678c0a9a65daa2e4cab67b8b81ddd84cf51c09a694802c605' +
655 '8ea4f4379a9c015b3a56b0309ae2da993b074a610a7f83376a60d3e5b7' +
656 'cb028201005e4f0cdf64243199a311c4683cd8f5a5597709a2d1408dd4' +
657 'ef2fde5b868eadbdefd970136228a7faa81e37138d0bd3b563a7238351' +
658 'd4298cb9c586c3b9ec11fc6fe01b4e1deff335ec603c2d022ea8679e84' +
659 '41abcf991eee6f124f9acfbd06699438b42f67edfd721d12f250edd284' +
660 '710e9d65df7d05106692aa1ad8c82520f648595df60a77821d9d6341d2' +
661 '3d29bb7f6227dfe55f2fc41e9b32c01e579d7f24294878e5f751acf0b8' +
662 '35ab8d1ba7f1a26c0491453df6858ec0d19b22cf3ba2b39e52f5d0b3f8' +
663 'b74c1f108d7236c2d06c76c3a7f8a4ea45c8bbad4df2b29dc6bc93826d' +
664 'eb01783732ae79c5b27e94771d0f960cb377880f77e15781e5feda5fd1' +
665 '028201007f3514a018db10f64654dbd6d94870678841664a157b3854f5' +
666 '00a4fd0b66dd1523e51c3d17722fb4861a009e4d32dd1d023feeb8f874' +
667 '612879183fdd725637263cf8a6c79399cc1da0a60c9bf394069db8ad74' +
668 '2c38a97c56da129afd7627f451ad7968d9fb8b834e1e0ed2a742fa7f56' +
669 '0e6641efca4cc8d15a8f216555098caef5359417c327f52221fd208b9a' +
670 '3bb2f1e7750253f95f0f72a32b7655936fb43b40193ba21ce55fc4e2f8' +
671 '37faecd78f72f4766bfa43a50ba1b75318606eac33dadb7c602bdb9663' +
672 '51c14469c116544efacf6b6f0191eef5de84549544ab0fdb713b00ef8d' +
673 '9069ce612f550e7fd1812a812bdc8b355d5bc2f65e2ba7c0959f200502' +
674 '82010100baf8445f5361adfb0889993acfaf27c14644a333875114c0ed' +
675 '8fbee213be44edbbfec4e088f2c40a3976402892459008600172288324' +
676 '9118516a9c517e39a900a13a0b66354da877a7fdb60c83171379292f1c' +
677 '137d8d5d5deb257b376c166620a2d23c44dfdee20fb84a453e3a5d479b' +
678 'f98e557e4c38c7e11ac8b44de10c2bf2a535e15d73265a9dd57d86e011' +
679 'f20fd37b91da22212fefe0dc152a6ebcb03c4b1356248d34cfee043432' +
680 '6a65cb5c1d5b43623f39be3523cd8f0f588d0aa1a2ed9bd9dde5581a05' +
681 '13432c3a4383bc8254cf3fd702485557c04f601db2def13a9f4d9096ac' +
682 'ce9cac65fa05bea1576b6a36c03eec9ebd53d346bb0e4636a5e369f5',
683 'hex'
684 );
685 
686 // However, just like with Node.js, when the der data is exported as pkcs1, trying to read
687 // it as pkcs8 fails... tho oddly sec1 is ok. Silly software.
688 const k4 = createPrivateKey({
689 key: derData2,
690 format: 'der',
691 type: 'pkcs1',
692 });
693 const k6 = createPrivateKey({ key: derData2, format: 'der', type: 'sec1' });
694 
695 ok(k4.equals(k6));
696 
697 throws(
698 () => createPrivateKey({ key: derData2, format: 'der', type: 'pkcs8' }),
699 {
700 message: 'Failed to parse private key',
701 }
702 );
703 },
704};
705 
706export const private_key_jwk_export = {
707 test(_, env) {
708 // These are invalid for JWK export
709 [
710 'dsa_private.pem',
711 'dsa_private_1025.pem',
712 'dsa_private_pkcs8.pem',
713 ].forEach((i) => {
714 throws(() => createPrivateKey(env[i]).export({ format: 'jwk' }), {
715 message: 'Key type is invalid for JWK export',
716 });
717 });
718 
719 deepStrictEqual(
720 createPrivateKey(env['ed25519_private.pem']).export({ format: 'jwk' }),
721 {
722 alg: 'EdDSA',
723 crv: 'Ed25519',
724 d: 'wVK6M3SMhQh3NK-7GRrSV-BVWQx1FO5pW8hhQeu_NdA',
725 kty: 'OKP',
726 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
727 }
728 );
729 
730 deepStrictEqual(
731 createPrivateKey(env['x25519_private.pem']).export({ format: 'jwk' }),
732 {
733 crv: 'X25519',
734 d: 'mL_IWm55RrALUGRfJYzw40gEYWMvtRkesP9mj8o8Omc',
735 kty: 'OKP',
736 x: 'aSb8Q-RndwfNnPeOYGYPDUN3uhAPnMLzXyfi-mqfhig',
737 }
738 );
739 
740 deepStrictEqual(
741 createPrivateKey(env['ec_p256_private.pem']).export({ format: 'jwk' }),
742 {
743 crv: 'P-256',
744 d: 'DxBsPQPIgMuMyQbxzbb9toew6Ev6e9O6ZhpxLNgmAEo',
745 kty: 'EC',
746 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
747 y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI',
748 }
749 );
750 
751 deepStrictEqual(
752 createPrivateKey(env['ec_p384_private.pem']).export({ format: 'jwk' }),
753 {
754 crv: 'P-384',
755 d: 'dwfuHuAtTlMRn7ZBCBm_0grpc1D_4hPeNAgevgelljuC0--k_LDFosDgBlLLmZsi',
756 kty: 'EC',
757 x: 'hON3nzGJgv-08fdHpQxgRJFZzlK-GZDGa5f3KnvM31cvvjJmsj4UeOgIdy3rDAjV',
758 y: 'fidHhtecNCGCfLqmrLjDena1NSzWzWH1u_oUdMKGo5XSabxzD7-8JZxjpc8sR9cl',
759 }
760 );
761 
762 deepStrictEqual(
763 createPrivateKey(env['ec_p521_private.pem']).export({ format: 'jwk' }),
764 {
765 crv: 'P-521',
766 d:
767 'ABIIbmn3Gm_Y11uIDkC3g2ijpRxIrJEBY4i_JJYo5OougzTl3BX2ifRluP' +
768 'JMaaHcNerbQH_WdVkLLX86ShlHrRyJ',
769 kty: 'EC',
770 x:
771 'AaLFgjwZtznM3N7qsfb86awVXe6c6djUYOob1FN-kllekv0KEXV0bwcDjPGQ' +
772 'z5f6MxLCbhMeHRavUS6P10rsTtBn',
773 y:
774 'Ad3flexBeAfXceNzRBH128kFbOWD6W41NjwKRqqIF26vmgW_8COldGKZjFkO' +
775 'SEASxPBcvA2iFJRUyQ3whC00j0Np',
776 }
777 );
778 
779 deepStrictEqual(
780 createPrivateKey(env['rsa_private.pem']).export({ format: 'jwk' }),
781 {
782 d:
783 'ktnq2LvIMqBj4txP82IEOorIRQGVsw1khbm8A-cEpuEkgM71Yi_0WzupKktuc' +
784 'UeevQ5i0Yh8w9e1SJiTLDRAlJz66kdky9uejiWWl6zR4dyNZVMFYRM43ijLC-' +
785 'P8rPne9Fz16IqHFW5VbJqA1xCBhKmuPMsD71RNxZ4Hrsa7Kt_xglQTYsLbdGI' +
786 'wDmcZihId9VGXRzvmCPsDRf2fCkAj7HDeRxpUdEiEDpajADc-PWikra3r3b40' +
787 'tVHKWm8wxJLivOIN7GiYXKQIW6RhZgH-Rk45JIRNKxNagxdeXUqqyhnwhbTo1' +
788 'Hite0iBDexN9tgoZk0XmdYWBn6ElXHRZ7VCDQ',
789 dp:
790 'qS_Mdr5CMRGGMH0bKhPUWEtAixUGZhJaunX5wY71Xoc_Gh4cnO-b7BNJ_-5L' +
791 '8WZog0vr6PgiLhrqBaCYm2wjpyoG2o2wDHm-NAlzN_wp3G2EFhrSxdOux-S1' +
792 'c0kpRcyoiAO2n29rNDa-jOzwBBcU8ACEPdLOCQl0IEFFJO33tl8',
793 dq:
794 'WAziKpxLKL7LnL4dzDcx8JIPIuwnTxh0plCDdCffyLaT8WJ9lXbXHFTjOvt8' +
795 'WfPrlDP_Ylxmfkw5BbGZOP1VLGjZn2DkH9aMiwNmbDXFPdG0G3hzQovx_9fa' +
796 'jiRV4DWghLHeT9wzJfZabRRiI0VQR472300AVEeX4vgbrDBn600',
797 e: 'AQAB',
798 kty: 'RSA',
799 n:
800 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr' +
801 '83Dd5OVe1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4' +
802 'YCytivE24YI0D4XZMPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYS' +
803 'lTIGIhzyaiYBh7wrZBoPczIEu6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0' +
804 'ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsFdi6hHcpZgbopPL630296iByyigQCP' +
805 'JVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
806 p:
807 '8UovlB4nrBm7xH-u7XXBMbqxADQm5vaEZxw9eluc-tP7cIAI4sglMIvL_FMpb' +
808 'd2pEeP_BkR76NTDzzDuPAZvUGRavgEjy0O9j2NAs_WPK4tZF-vFdunhnSh4EH' +
809 'AF4Ij9kbsUi90NOpbGfVqPdOaHqzgHKoR23Cuusk9wFQ2XTV8',
810 q:
811 'wxHdEYT9xrpfrHPqSBQPpO0dWGKJEkrWOb-76rSfuL8wGR4OBNmQdhLuU9zTI' +
812 'h22pog-XPnLPAecC-4yu_wtJ2SPCKiKDbJBre0CKPyRfGqzvA3njXwMxXazU4' +
813 'kGs-2Fg-xu_iKbaIjxXrclBLhkxhBtySrwAFhxxOk6fFcPLSs',
814 qi:
815 'k7czBCT9rHn_PNwCa17hlTy88C4vXkwbz83Oa-aX5L4e5gw5lhcR2ZuZHLb2' +
816 'r6oMt9rlD7EIDItSs-u21LOXWPTAlazdnpYUyw_CzogM_PN-qNwMRXn5uXFF' +
817 'hmlP2mVg2EdELTahXch8kWqHaCSX53yvqCtRKu_j76V31TfQZGM',
818 }
819 );
820 },
821};
822 
823export const rsa_private_key_jwk_import = {
824 test() {
825 const jwk = {
826 e: 'AQAB',
827 n:
828 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' +
829 '1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' +
830 'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' +
831 'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' +
832 'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
833 d:
834 'ktnq2LvIMqBj4txP82IEOorIRQGVsw1khbm8A-cEpuEkgM71Yi_0WzupKktucUeevQ5i0' +
835 'Yh8w9e1SJiTLDRAlJz66kdky9uejiWWl6zR4dyNZVMFYRM43ijLC-P8rPne9Fz16IqHFW' +
836 '5VbJqA1xCBhKmuPMsD71RNxZ4Hrsa7Kt_xglQTYsLbdGIwDmcZihId9VGXRzvmCPsDRf2' +
837 'fCkAj7HDeRxpUdEiEDpajADc-PWikra3r3b40tVHKWm8wxJLivOIN7GiYXKQIW6RhZgH-' +
838 'Rk45JIRNKxNagxdeXUqqyhnwhbTo1Hite0iBDexN9tgoZk0XmdYWBn6ElXHRZ7VCDQ',
839 p:
840 '8UovlB4nrBm7xH-u7XXBMbqxADQm5vaEZxw9eluc-tP7cIAI4sglMIvL_FMpbd2pEeP_B' +
841 'kR76NTDzzDuPAZvUGRavgEjy0O9j2NAs_WPK4tZF-vFdunhnSh4EHAF4Ij9kbsUi90NOp' +
842 'bGfVqPdOaHqzgHKoR23Cuusk9wFQ2XTV8',
843 q:
844 'wxHdEYT9xrpfrHPqSBQPpO0dWGKJEkrWOb-76rSfuL8wGR4OBNmQdhLuU9zTIh22pog-X' +
845 'PnLPAecC-4yu_wtJ2SPCKiKDbJBre0CKPyRfGqzvA3njXwMxXazU4kGs-2Fg-xu_iKbaI' +
846 'jxXrclBLhkxhBtySrwAFhxxOk6fFcPLSs',
847 dp:
848 'qS_Mdr5CMRGGMH0bKhPUWEtAixUGZhJaunX5wY71Xoc_Gh4cnO-b7BNJ_-5L8WZog0vr' +
849 '6PgiLhrqBaCYm2wjpyoG2o2wDHm-NAlzN_wp3G2EFhrSxdOux-S1c0kpRcyoiAO2n29rN' +
850 'Da-jOzwBBcU8ACEPdLOCQl0IEFFJO33tl8',
851 dq:
852 'WAziKpxLKL7LnL4dzDcx8JIPIuwnTxh0plCDdCffyLaT8WJ9lXbXHFTjOvt8WfPrlDP_' +
853 'Ylxmfkw5BbGZOP1VLGjZn2DkH9aMiwNmbDXFPdG0G3hzQovx_9fajiRV4DWghLHeT9wzJ' +
854 'fZabRRiI0VQR472300AVEeX4vgbrDBn600',
855 qi:
856 'k7czBCT9rHn_PNwCa17hlTy88C4vXkwbz83Oa-aX5L4e5gw5lhcR2ZuZHLb2r6oMt9rl' +
857 'D7EIDItSs-u21LOXWPTAlazdnpYUyw_CzogM_PN-qNwMRXn5uXFFhmlP2mVg2EdELTahX' +
858 'ch8kWqHaCSX53yvqCtRKu_j76V31TfQZGM',
859 kty: 'RSA',
860 };
861 
862 const key = createPrivateKey({ key: jwk, format: 'jwk' });
863 strictEqual(key.asymmetricKeyType, 'rsa');
864 deepStrictEqual(key.asymmetricKeyDetails, {
865 modulusLength: 2048,
866 publicExponent: 65537n,
867 });
868 
869 // The fail due to missing information in the JWK
870 throws(
871 () => {
872 createPrivateKey({
873 key: {
874 kty: 'RSA',
875 },
876 format: 'jwk',
877 });
878 },
879 {
880 message: 'RSA JWK missing n parameter',
881 }
882 );
883 
884 throws(
885 () => {
886 createPrivateKey({
887 key: {
888 kty: 'RSA',
889 n:
890 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' +
891 '1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' +
892 'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' +
893 'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' +
894 'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
895 },
896 format: 'jwk',
897 });
898 },
899 {
900 message: 'RSA JWK missing e parameter',
901 }
902 );
903 
904 throws(
905 () => {
906 createPrivateKey({
907 key: {
908 kty: 'RSA',
909 n:
910 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' +
911 '1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' +
912 'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' +
913 'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' +
914 'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
915 e: 'AQAB',
916 },
917 format: 'jwk',
918 });
919 },
920 {
921 message: 'RSA JWK missing d parameter',
922 }
923 );
924 },
925};
926 
927export const ec_private_key_jwk_import = {
928 test() {
929 const jwk = {
930 crv: 'P-256',
931 d: 'DxBsPQPIgMuMyQbxzbb9toew6Ev6e9O6ZhpxLNgmAEo',
932 kty: 'EC',
933 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
934 y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI',
935 };
936 
937 const key = createPrivateKey({ key: jwk, format: 'jwk' });
938 strictEqual(key.asymmetricKeyType, 'ec');
939 deepStrictEqual(key.asymmetricKeyDetails, { namedCurve: 'prime256v1' });
940 
941 // The fail due to missing information in the JWK
942 throws(
943 () => {
944 createPrivateKey({
945 key: {
946 kty: 'EC',
947 },
948 format: 'jwk',
949 });
950 },
951 {
952 message: 'EC JWK missing crv parameter',
953 }
954 );
955 
956 throws(
957 () => {
958 createPrivateKey({
959 key: {
960 kty: 'EC',
961 crv: 'P-256',
962 },
963 format: 'jwk',
964 });
965 },
966 {
967 message: 'EC JWK missing x parameter',
968 }
969 );
970 
971 throws(
972 () => {
973 createPrivateKey({
974 key: {
975 kty: 'EC',
976 crv: 'P-256',
977 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
978 },
979 format: 'jwk',
980 });
981 },
982 {
983 message: 'EC JWK missing y parameter',
984 }
985 );
986 },
987};
988 
989export const ed_private_key_jwk_import = {
990 test() {
991 const jwk = {
992 crv: 'Ed25519',
993 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
994 d: 'wVK6M3SMhQh3NK-7GRrSV-BVWQx1FO5pW8hhQeu_NdA',
995 kty: 'OKP',
996 };
997 
998 const key = createPrivateKey({ key: jwk, format: 'jwk' });
999 strictEqual(key.asymmetricKeyType, 'ed25519');
1000 
1001 const jwk2 = {
1002 crv: 'X25519',
1003 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
1004 d: 'wVK6M3SMhQh3NK-7GRrSV-BVWQx1FO5pW8hhQeu_NdA',
1005 kty: 'OKP',
1006 };
1007 
1008 const key2 = createPrivateKey({ key: jwk2, format: 'jwk' });
1009 strictEqual(key2.asymmetricKeyType, 'x25519');
1010 
1011 // The fail due to missing information in the JWK
1012 throws(
1013 () => {
1014 createPrivateKey({
1015 key: {
1016 kty: 'OKP',
1017 },
1018 format: 'jwk',
1019 });
1020 },
1021 {
1022 message: 'OKP JWK missing crv parameter',
1023 }
1024 );
1025 },
1026};
1027 
1028export const private_key_hmac_fails = {
1029 test(_, env) {
1030 // Verify that creating an hmac with a private key fails.
1031 // TODO(soon): Also try public key, which should also fail.
1032 const key = createPrivateKey(env['dsa_private.pem']);
1033 throws(() => createHmac('sha256', key), {
1034 message: 'Invalid key object type private, expected secret.',
1035 });
1036 },
1037};
1038 
1039export const private_key_tocryptokey = {
1040 async test(_, env) {
1041 const key = createPrivateKey(env['dsa_private.pem']);
1042 // TODO(soon): Getting a CryptoKey from the KeyObject currently does not work.
1043 // Will will implement this conversion as a follow up step.
1044 throws(() => key.toCryptoKey(), {
1045 message: 'The toCryptoKey method is not implemented',
1046 });
1047 
1048 // Getting the private key from a CryptoKey should generally work...
1049 const { privateKey } = await crypto.subtle.generateKey(
1050 {
1051 name: 'RSASSA-PKCS1-v1_5',
1052 modulusLength: 2048,
1053 publicExponent: new Uint8Array([1, 0, 1]),
1054 hash: 'SHA-256',
1055 },
1056 true,
1057 ['sign', 'verify']
1058 );
1059 
1060 const key2 = KeyObject.from(privateKey);
1061 strictEqual(key2.asymmetricKeyDetails.modulusLength, 2048);
1062 strictEqual(key2.asymmetricKeyDetails.publicExponent, 65537n);
1063 strictEqual(key2.asymmetricKeyType, 'rsa');
1064 strictEqual(key2.type, 'private');
1065 },
1066};
1067 
1068export const create_public_key = {
1069 test(_, env) {
1070 // TODO(later): These error messages are inconsistent with one another
1071 // despite performing the same basic validation.
1072 throws(() => createPublicKey(1), {
1073 message: /The "options.key" property must be of type string/,
1074 });
1075 throws(() => createPublicKey(true), {
1076 message: /The "options.key" property must be of type string/,
1077 });
1078 throws(() => createPublicKey({ key: 1 }), {
1079 message: /The "key" argument/,
1080 });
1081 throws(() => createPublicKey({ key: true }), {
1082 message: /The "key" argument/,
1083 });
1084 
1085 [
1086 // These are the public key types that are not supported by boringssl
1087 'dh_public.pem',
1088 'ec_secp256k1_public.pem',
1089 'ed448_public.pem',
1090 'rsa_pss_public_2048.pem',
1091 'rsa_pss_public_2048_sha1_sha1_20.pem',
1092 'rsa_pss_public_2048_sha256_sha256_16.pem',
1093 'rsa_pss_public_2048_sha512_sha256_20.pem',
1094 'x448_public.pem',
1095 ].forEach((i) => {
1096 throws(() => createPublicKey(env[i]), {
1097 message: 'Failed to parse public key',
1098 });
1099 });
1100 
1101 [
1102 'dsa_public_1025.pem',
1103 'dsa_public.pem',
1104 'ec_p256_public.pem',
1105 'ec_p384_public.pem',
1106 'ec_p521_public.pem',
1107 'ed25519_public.pem',
1108 'rsa_public_2048.pem',
1109 'rsa_public_4096.pem',
1110 'rsa_public_b.pem',
1111 'rsa_public.pem',
1112 'x25519_public.pem',
1113 
1114 // It is also possible to use an X509 cert as the source of a public key
1115 'agent1-cert.pem',
1116 
1117 // It is possible to create a public key from a private key
1118 'dsa_private.pem',
1119 'dsa_private_1025.pem',
1120 'dsa_private_pkcs8.pem',
1121 'ed25519_private.pem',
1122 'ec_p256_private.pem',
1123 'ec_p384_private.pem',
1124 'ec_p521_private.pem',
1125 'rsa_private.pem',
1126 'rsa_private_b.pem',
1127 'rsa_private_2048.pem',
1128 'rsa_private_pkcs8_bad.pem',
1129 'rsa_private_4096.pem',
1130 'rsa_private_pkcs8.pem',
1131 'x25519_private.pem',
1132 ].forEach((i) => {
1133 const key = createPublicKey(env[i]);
1134 strictEqual(key.type, 'public');
1135 
1136 switch (i) {
1137 case 'dsa_public_1025.pem': {
1138 strictEqual(key.asymmetricKeyType, 'dsa');
1139 strictEqual(key.asymmetricKeyDetails.modulusLength, 1088);
1140 strictEqual(key.asymmetricKeyDetails.divisorLength, 160);
1141 break;
1142 }
1143 case 'dsa_public.pem': {
1144 strictEqual(key.asymmetricKeyType, 'dsa');
1145 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1146 strictEqual(key.asymmetricKeyDetails.divisorLength, 256);
1147 break;
1148 }
1149 case 'ec_p256_public.pem': {
1150 strictEqual(key.asymmetricKeyType, 'ec');
1151 strictEqual(key.asymmetricKeyDetails.namedCurve, 'prime256v1');
1152 break;
1153 }
1154 case 'ec_p384_public.pem': {
1155 strictEqual(key.asymmetricKeyType, 'ec');
1156 strictEqual(key.asymmetricKeyDetails.namedCurve, 'secp384r1');
1157 break;
1158 }
1159 case 'ec_p521_public.pem': {
1160 strictEqual(key.asymmetricKeyType, 'ec');
1161 strictEqual(key.asymmetricKeyDetails.namedCurve, 'secp521r1');
1162 break;
1163 }
1164 case 'ed25519_public.pem': {
1165 strictEqual(key.asymmetricKeyType, 'ed25519');
1166 break;
1167 }
1168 case 'rsa_public_2048.pem': {
1169 strictEqual(key.asymmetricKeyType, 'rsa');
1170 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1171 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1172 break;
1173 }
1174 case 'rsa_public_4096.pem': {
1175 strictEqual(key.asymmetricKeyType, 'rsa');
1176 strictEqual(key.asymmetricKeyDetails.modulusLength, 4096);
1177 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1178 break;
1179 }
1180 case 'rsa_public_b.pem': {
1181 strictEqual(key.asymmetricKeyType, 'rsa');
1182 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1183 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1184 break;
1185 }
1186 case 'rsa_public.pem': {
1187 strictEqual(key.asymmetricKeyType, 'rsa');
1188 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1189 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1190 break;
1191 }
1192 case 'x25519_public.pem': {
1193 strictEqual(key.asymmetricKeyType, 'x25519');
1194 break;
1195 }
1196 case 'dsa_private.pem': {
1197 strictEqual(key.asymmetricKeyType, 'dsa');
1198 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1199 strictEqual(key.asymmetricKeyDetails.divisorLength, 256);
1200 break;
1201 }
1202 case 'dsa_private_1025.pem': {
1203 strictEqual(key.asymmetricKeyType, 'dsa');
1204 strictEqual(key.asymmetricKeyDetails.modulusLength, 1088);
1205 strictEqual(key.asymmetricKeyDetails.divisorLength, 160);
1206 break;
1207 }
1208 case 'dsa_private_pkcs8.pem': {
1209 strictEqual(key.asymmetricKeyType, 'dsa');
1210 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1211 strictEqual(key.asymmetricKeyDetails.divisorLength, 256);
1212 break;
1213 }
1214 case 'ed25519_private.pem': {
1215 strictEqual(key.asymmetricKeyType, 'ed25519');
1216 break;
1217 }
1218 case 'ec_p256_private.pem': {
1219 strictEqual(key.asymmetricKeyType, 'ec');
1220 strictEqual(key.asymmetricKeyDetails.namedCurve, 'prime256v1');
1221 break;
1222 }
1223 case 'ec_p384_private.pem': {
1224 strictEqual(key.asymmetricKeyType, 'ec');
1225 strictEqual(key.asymmetricKeyDetails.namedCurve, 'secp384r1');
1226 break;
1227 }
1228 case 'ec_p521_private.pem': {
1229 strictEqual(key.asymmetricKeyType, 'ec');
1230 strictEqual(key.asymmetricKeyDetails.namedCurve, 'secp521r1');
1231 break;
1232 }
1233 case 'rsa_private.pem': {
1234 strictEqual(key.asymmetricKeyType, 'rsa');
1235 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1236 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1237 break;
1238 }
1239 case 'rsa_private_b.pem': {
1240 strictEqual(key.asymmetricKeyType, 'rsa');
1241 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1242 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1243 break;
1244 }
1245 case 'rsa_private_2048.pem': {
1246 strictEqual(key.asymmetricKeyType, 'rsa');
1247 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1248 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1249 break;
1250 }
1251 case 'rsa_private_pkcs8_bad.pem': {
1252 strictEqual(key.asymmetricKeyType, 'rsa');
1253 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1254 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1255 break;
1256 }
1257 case 'rsa_private_4096.pem': {
1258 strictEqual(key.asymmetricKeyType, 'rsa');
1259 strictEqual(key.asymmetricKeyDetails.modulusLength, 4096);
1260 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1261 break;
1262 }
1263 case 'rsa_private_pkcs8.pem': {
1264 strictEqual(key.asymmetricKeyType, 'rsa');
1265 strictEqual(key.asymmetricKeyDetails.modulusLength, 2048);
1266 strictEqual(key.asymmetricKeyDetails.publicExponent, 65537n);
1267 break;
1268 }
1269 case 'x25519_private.pem': {
1270 strictEqual(key.asymmetricKeyType, 'x25519');
1271 break;
1272 }
1273 }
1274 
1275 if (key.asymmetricKeyType === 'rsa') {
1276 const exp = key.export({ format: 'pem', type: 'pkcs1' });
1277 const key2 = createPublicKey(exp);
1278 ok(key.equals(key2));
1279 
1280 const exp2 = key.export({ format: 'der', type: 'pkcs1' });
1281 const key3 = createPublicKey({
1282 key: exp2,
1283 format: 'der',
1284 type: 'pkcs1',
1285 });
1286 ok(key.equals(key3));
1287 }
1288 {
1289 const exp = key.export({ format: 'pem', type: 'spki' });
1290 const key2 = createPublicKey(exp);
1291 ok(key.equals(key2));
1292 }
1293 {
1294 const exp = key.export({ format: 'der', type: 'spki' });
1295 const key2 = createPublicKey({ key: exp, format: 'der', type: 'spki' });
1296 ok(key.equals(key2));
1297 }
1298 });
1299 
1300 const privateKey = createPrivateKey(env['rsa_private.pem']);
1301 const publicKey = createPublicKey(privateKey);
1302 strictEqual(publicKey.type, 'public');
1303 strictEqual(publicKey.asymmetricKeyType, 'rsa');
1304 strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 2048);
1305 strictEqual(publicKey.asymmetricKeyDetails.publicExponent, 65537n);
1306 },
1307};
1308 
1309export const public_key_tocryptokey = {
1310 async test(_, env) {
1311 const key = createPublicKey(env['dsa_public_1025.pem']);
1312 // TODO(soon): Getting a CryptoKey from the KeyObject currently does not work.
1313 // Will will implement this conversion as a follow up step.
1314 throws(() => key.toCryptoKey(), {
1315 message: 'The toCryptoKey method is not implemented',
1316 });
1317 
1318 // Getting the private key from a CryptoKey should generally work...
1319 const { publicKey } = await crypto.subtle.generateKey(
1320 {
1321 name: 'RSASSA-PKCS1-v1_5',
1322 modulusLength: 2048,
1323 publicExponent: new Uint8Array([1, 0, 1]),
1324 hash: 'SHA-256',
1325 },
1326 true,
1327 ['sign', 'verify']
1328 );
1329 
1330 const key2 = KeyObject.from(publicKey);
1331 strictEqual(key2.asymmetricKeyDetails.modulusLength, 2048);
1332 strictEqual(key2.asymmetricKeyDetails.publicExponent, 65537n);
1333 strictEqual(key2.asymmetricKeyType, 'rsa');
1334 strictEqual(key2.type, 'public');
1335 },
1336};
1337 
1338export const export_public_key_jwk = {
1339 test(_, env) {
1340 // These are invalid for JWK export
1341 [
1342 'dsa_private.pem',
1343 'dsa_private_1025.pem',
1344 'dsa_private_pkcs8.pem',
1345 ].forEach((i) => {
1346 throws(() => createPrivateKey(env[i]).export({ format: 'jwk' }), {
1347 message: 'Key type is invalid for JWK export',
1348 });
1349 });
1350 
1351 deepStrictEqual(
1352 createPublicKey(env['ec_p256_public.pem']).export({ format: 'jwk' }),
1353 {
1354 crv: 'P-256',
1355 kty: 'EC',
1356 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
1357 y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI',
1358 }
1359 );
1360 
1361 deepStrictEqual(
1362 createPublicKey(env['ec_p384_public.pem']).export({ format: 'jwk' }),
1363 {
1364 crv: 'P-384',
1365 kty: 'EC',
1366 x: 'hON3nzGJgv-08fdHpQxgRJFZzlK-GZDGa5f3KnvM31cvvjJmsj4UeOgIdy3rDAjV',
1367 y: 'fidHhtecNCGCfLqmrLjDena1NSzWzWH1u_oUdMKGo5XSabxzD7-8JZxjpc8sR9cl',
1368 }
1369 );
1370 
1371 deepStrictEqual(
1372 createPublicKey(env['ec_p521_public.pem']).export({ format: 'jwk' }),
1373 {
1374 crv: 'P-521',
1375 kty: 'EC',
1376 x:
1377 'AaLFgjwZtznM3N7qsfb86awVXe6c6djUYOob1FN-kllekv0KEXV0bwcDjPGQz5f' +
1378 '6MxLCbhMeHRavUS6P10rsTtBn',
1379 y:
1380 'Ad3flexBeAfXceNzRBH128kFbOWD6W41NjwKRqqIF26vmgW_8COldGKZjFkOSEA' +
1381 'SxPBcvA2iFJRUyQ3whC00j0Np',
1382 }
1383 );
1384 
1385 deepStrictEqual(
1386 createPublicKey(env['ed25519_public.pem']).export({ format: 'jwk' }),
1387 {
1388 alg: 'EdDSA',
1389 crv: 'Ed25519',
1390 kty: 'OKP',
1391 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
1392 }
1393 );
1394 
1395 deepStrictEqual(
1396 createPublicKey(env['x25519_public.pem']).export({ format: 'jwk' }),
1397 {
1398 crv: 'X25519',
1399 kty: 'OKP',
1400 x: 'aSb8Q-RndwfNnPeOYGYPDUN3uhAPnMLzXyfi-mqfhig',
1401 }
1402 );
1403 
1404 deepStrictEqual(
1405 createPublicKey(env['rsa_public_2048.pem']).export({ format: 'jwk' }),
1406 {
1407 e: 'AQAB',
1408 kty: 'RSA',
1409 n:
1410 'rk4OqxBqU5_k0FoUDU7CpZpjz6YJEXUpyqeJmFRVZPMUv_Rc7U4seLY-Qp6k26' +
1411 'T_wlQ2WJWuyY-VJcbQNWLvjJWks5HWknwDuVs6sjuTM8CfHWn1960JkK5Ec2Tj' +
1412 'RhCQ1KJy-uc3GJLtWb4rWVgTbbaaC5fiR1_GeuJ8JH1Q50lB3mDsNGIk1U5jhN' +
1413 'aYY82hYvlbErf6Ft5njHK0BOM5OTvQ6BBv7c363WNG7tYlNw1J40dup9OQPo5J' +
1414 'mXN_h-sRbdgG8iUxrkRibuGv7loh52QQgq2snznuRMdKidRfUZjCDGgwbgK23Q' +
1415 '7n8VZ9Y10j8PIvPTLJ83PX4lOEA37Jlw',
1416 }
1417 );
1418 
1419 // TODO(now): JWK import
1420 throws(() => createPublicKey({ key: {}, format: 'jwk' }), {
1421 message: 'JWK public key import is not implemented for this key type',
1422 });
1423 },
1424};
1425 
1426export const rsa_public_key_jwk_import = {
1427 test() {
1428 const jwk = {
1429 e: 'AQAB',
1430 n:
1431 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' +
1432 '1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' +
1433 'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' +
1434 'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' +
1435 'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
1436 kty: 'RSA',
1437 };
1438 
1439 const key = createPublicKey({ key: jwk, format: 'jwk' });
1440 strictEqual(key.asymmetricKeyType, 'rsa');
1441 deepStrictEqual(key.asymmetricKeyDetails, {
1442 modulusLength: 2048,
1443 publicExponent: 65537n,
1444 });
1445 
1446 // The fail due to missing information in the JWK
1447 throws(
1448 () => {
1449 createPublicKey({
1450 key: {
1451 kty: 'RSA',
1452 },
1453 format: 'jwk',
1454 });
1455 },
1456 {
1457 message: 'RSA JWK missing n parameter',
1458 }
1459 );
1460 
1461 throws(
1462 () => {
1463 createPublicKey({
1464 key: {
1465 kty: 'RSA',
1466 n:
1467 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' +
1468 '1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' +
1469 'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' +
1470 'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' +
1471 'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
1472 },
1473 format: 'jwk',
1474 });
1475 },
1476 {
1477 message: 'RSA JWK missing e parameter',
1478 }
1479 );
1480 },
1481};
1482 
1483export const ec_public_key_jwk_import = {
1484 test() {
1485 const jwk = {
1486 crv: 'P-256',
1487 kty: 'EC',
1488 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
1489 y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI',
1490 };
1491 
1492 const key = createPublicKey({ key: jwk, format: 'jwk' });
1493 strictEqual(key.asymmetricKeyType, 'ec');
1494 deepStrictEqual(key.asymmetricKeyDetails, { namedCurve: 'prime256v1' });
1495 
1496 // The fail due to missing information in the JWK
1497 throws(
1498 () => {
1499 createPublicKey({
1500 key: {
1501 kty: 'EC',
1502 },
1503 format: 'jwk',
1504 });
1505 },
1506 {
1507 message: 'EC JWK missing crv parameter',
1508 }
1509 );
1510 
1511 throws(
1512 () => {
1513 createPublicKey({
1514 key: {
1515 kty: 'EC',
1516 crv: 'P-256',
1517 },
1518 format: 'jwk',
1519 });
1520 },
1521 {
1522 message: 'EC JWK missing x parameter',
1523 }
1524 );
1525 
1526 throws(
1527 () => {
1528 createPublicKey({
1529 key: {
1530 kty: 'EC',
1531 crv: 'P-256',
1532 x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
1533 },
1534 format: 'jwk',
1535 });
1536 },
1537 {
1538 message: 'EC JWK missing y parameter',
1539 }
1540 );
1541 },
1542};
1543 
1544export const ed_public_key_jwk_import = {
1545 test() {
1546 const jwk = {
1547 crv: 'Ed25519',
1548 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
1549 kty: 'OKP',
1550 };
1551 
1552 const key = createPublicKey({ key: jwk, format: 'jwk' });
1553 strictEqual(key.asymmetricKeyType, 'ed25519');
1554 
1555 const { publicKey, privateKey } = generateKeyPairSync('ed25519');
1556 const publicJwk = publicKey.export({ format: 'jwk' });
1557 const reimported = createPublicKey({ key: publicJwk, format: 'jwk' });
1558 const data = Buffer.from('workerd okp jwk');
1559 const sig = sign(null, data, privateKey);
1560 ok(verify(null, data, reimported, sig));
1561 ok(
1562 publicKey
1563 .export({ format: 'der', type: 'spki' })
1564 .equals(reimported.export({ format: 'der', type: 'spki' }))
1565 );
1566 
1567 const jwk2 = {
1568 crv: 'X25519',
1569 x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
1570 kty: 'OKP',
1571 };
1572 
1573 const key2 = createPublicKey({ key: jwk2, format: 'jwk' });
1574 strictEqual(key2.asymmetricKeyType, 'x25519');
1575 
1576 const { publicKey: x25519PublicKey } = generateKeyPairSync('x25519');
1577 const x25519Jwk = x25519PublicKey.export({ format: 'jwk' });
1578 const x25519Reimported = createPublicKey({ key: x25519Jwk, format: 'jwk' });
1579 ok(
1580 x25519PublicKey
1581 .export({ format: 'der', type: 'spki' })
1582 .equals(x25519Reimported.export({ format: 'der', type: 'spki' }))
1583 );
1584 
1585 // The fail due to missing information in the JWK
1586 throws(
1587 () => {
1588 createPublicKey({
1589 key: {
1590 kty: 'OKP',
1591 },
1592 format: 'jwk',
1593 });
1594 },
1595 {
1596 message: 'OKP JWK missing crv parameter',
1597 }
1598 );
1599 },
1600};
1601 
1602export const generate_hmac_secret_key = {
1603 async test() {
1604 // Length is intentionally not a multiple of 8
1605 const key = generateKeySync('hmac', { length: 33 });
1606 strictEqual(key.type, 'secret');
1607 strictEqual(key.symmetricKeySize, 4);
1608 
1609 const { promise, resolve, reject } = Promise.withResolvers();
1610 generateKey('hmac', { length: 33 }, (err, key) => {
1611 if (err) {
1612 reject(err);
1613 return;
1614 }
1615 strictEqual(key.type, 'secret');
1616 strictEqual(key.symmetricKeySize, 4);
1617 resolve();
1618 });
1619 await promise;
1620 
1621 throws(() => generateKeySync('hmac', { length: 0 }), {
1622 message:
1623 'The value of "options.length" is out of range. It must ' +
1624 'be >= 8 && <= 65536. Received 0',
1625 });
1626 throws(() => generateKeySync('hmac', { length: 65537 }), {
1627 message:
1628 'The value of "options.length" is out of range. It must ' +
1629 'be >= 8 && <= 65536. Received 65537',
1630 });
1631 
1632 const h = createHmac('sha256', key);
1633 ok(h.update('test').digest());
1634 },
1635};
1636 
1637export const generate_aes_secret_key = {
1638 async test() {
1639 const key = generateKeySync('aes', { length: 128 });
1640 strictEqual(key.type, 'secret');
1641 strictEqual(key.symmetricKeySize, 16);
1642 
1643 const { promise, resolve, reject } = Promise.withResolvers();
1644 generateKey('aes', { length: 128 }, (err, key) => {
1645 if (err) {
1646 reject(err);
1647 return;
1648 }
1649 strictEqual(key.type, 'secret');
1650 strictEqual(key.symmetricKeySize, 16);
1651 resolve();
1652 });
1653 await promise;
1654 
1655 throws(() => generateKeySync('aes', { length: 0 }), {
1656 message:
1657 "The property 'options.length' must be one of: 128, 192, " +
1658 '256. Received 0',
1659 });
1660 },
1661};
1662 
1663export const generate_key_pair_arg_validation = {
1664 async test() {
1665 throws(() => generateKeyPairSync('invalid type'), {
1666 message:
1667 "The argument 'type' must be one of: 'rsa', " +
1668 "'ec', 'ed25519', 'x25519', 'dh'. Received " +
1669 "'invalid type'",
1670 });
1671 
1672 throws(() => generateKeyPairSync('rsa', { modulusLength: -1 }), {
1673 message:
1674 'The value of "options.modulusLength" is out of range. ' +
1675 'It must be >= 0 && < 4294967296. Received -1',
1676 });
1677 
1678 throws(() => generateKeyPairSync('rsa', { modulusLength: 'foo' }), {
1679 message:
1680 'The "options.modulusLength" property must be of type number. ' +
1681 "Received type string ('foo')",
1682 });
1683 
1684 throws(
1685 () =>
1686 generateKeyPairSync('rsa', {
1687 modulusLength: 512,
1688 publicExponent: 'foo',
1689 }),
1690 {
1691 message:
1692 'The "options.publicExponent" property must be of type number. ' +
1693 "Received type string ('foo')",
1694 }
1695 );
1696 
1697 // TODO(later): BoringSSL does not currently support rsa-pss key generation
1698 // in the way Node.js does. Uncomment these later when it does.
1699 // throws(
1700 // () =>
1701 // generateKeyPairSync('rsa-pss', {
1702 // modulusLength: 512,
1703 // hashAlgorithm: false,
1704 // }),
1705 // {
1706 // message:
1707 // 'The "options.hashAlgorithm" property must be of type string. ' +
1708 // 'Received type boolean (false)',
1709 // }
1710 // );
1711 
1712 // throws(
1713 // () =>
1714 // generateKeyPairSync('rsa-pss', {
1715 // modulusLength: 512,
1716 // mgf1HashAlgorithm: false,
1717 // }),
1718 // {
1719 // message:
1720 // 'The "options.mgf1HashAlgorithm" property must be of type ' +
1721 // 'string. Received type boolean (false)',
1722 // }
1723 // );
1724 
1725 // throws(
1726 // () =>
1727 // generateKeyPairSync('rsa-pss', {
1728 // modulusLength: 512,
1729 // saltLength: 'foo',
1730 // }),
1731 // {
1732 // message:
1733 // 'The "options.saltLength" property must be of type number. ' +
1734 // "Received type string ('foo')",
1735 // }
1736 // );
1737 
1738 // TODO(later): BoringSSL currently does not support dsa key generation
1739 // in the same way Node.js does. Uncomment this when it does.
1740 // throws(
1741 // () =>
1742 // generateKeyPairSync('dsa', {
1743 // modulusLength: 512,
1744 // divisorLength: 'foo',
1745 // }),
1746 // {
1747 // message:
1748 // 'The "options.divisorLength" property must be of type number. ' +
1749 // "Received type string ('foo')",
1750 // }
1751 // );
1752 
1753 throws(() => generateKeyPairSync('dh', { prime: 'foo' }), {
1754 message:
1755 'The "options.prime" property must be an instance of Buffer, ' +
1756 "TypedArray, or ArrayBuffer. Received type string ('foo')",
1757 });
1758 
1759 throws(() => generateKeyPairSync('dh', { primeLength: 'foo' }), {
1760 message:
1761 'The "options.primeLength" property must be of type number. ' +
1762 "Received type string ('foo')",
1763 });
1764 
1765 throws(() => generateKeyPairSync('dh', { primeLength: -1 }), {
1766 message:
1767 'The value of "options.primeLength" is out of range. It ' +
1768 'must be >= 0 && <= 2147483647. Received -1',
1769 });
1770 
1771 throws(
1772 () => generateKeyPairSync('dh', { primeLength: 10, generator: -1 }),
1773 {
1774 message:
1775 'The value of "options.generator" is out of range. It must ' +
1776 'be >= 0 && <= 2147483647. Received -1',
1777 }
1778 );
1779 
1780 throws(() => generateKeyPairSync('dh', { groupName: 123 }), {
1781 message:
1782 'The "options.group" property must be of type string. ' +
1783 'Received type number (123)',
1784 });
1785 
1786 throws(
1787 () =>
1788 generateKeyPairSync('ec', { namedCurve: 'foo', paramEncoding: 'foo' }),
1789 {
1790 message:
1791 "The property 'options.paramEncoding' must be one of: " +
1792 "'named', 'explicit'. Received 'foo'",
1793 }
1794 );
1795 
1796 throws(() => generateKeyPairSync('ed25519', { publicKeyEncoding: 'foo' }), {
1797 message:
1798 'The "options.publicKeyEncoding" property must be of type ' +
1799 "object. Received type string ('foo')",
1800 });
1801 
1802 throws(() => generateKeyPairSync('x25519', { privateKeyEncoding: 'foo' }), {
1803 message:
1804 'The "options.privateKeyEncoding" property must be of type ' +
1805 "object. Received type string ('foo')",
1806 });
1807 
1808 // ====
1809 async function wrapped(...args) {
1810 const { promise, resolve, reject } = Promise.withResolvers();
1811 generateKeyPair(...args, (err) => {
1812 if (err) {
1813 reject(err);
1814 return;
1815 }
1816 resolve();
1817 });
1818 await promise;
1819 }
1820 
1821 await rejects(wrapped('invalid type', {}), {
1822 message:
1823 "The argument 'type' must be one of: 'rsa', " +
1824 "'ec', 'ed25519', 'x25519', 'dh'. Received " +
1825 "'invalid type'",
1826 });
1827 
1828 await rejects(wrapped('rsa', { modulusLength: -1 }), {
1829 message:
1830 'The value of "options.modulusLength" is out of range. ' +
1831 'It must be >= 0 && < 4294967296. Received -1',
1832 });
1833 
1834 await rejects(wrapped('rsa', { modulusLength: 'foo' }), {
1835 message:
1836 'The "options.modulusLength" property must be of type number. ' +
1837 "Received type string ('foo')",
1838 });
1839 
1840 await rejects(
1841 wrapped('rsa', { modulusLength: 512, publicExponent: 'foo' }),
1842 {
1843 message:
1844 'The "options.publicExponent" property must be of type number. ' +
1845 "Received type string ('foo')",
1846 }
1847 );
1848 
1849 // TODO(later): BoringSSL currently does not support rsa-pss key generation
1850 // in the same way Node.js does. Uncomment these later when it does.
1851 // await rejects(
1852 // wrapped('rsa-pss', { modulusLength: 512, hashAlgorithm: false }),
1853 // {
1854 // message:
1855 // 'The "options.hashAlgorithm" property must be of type string. ' +
1856 // 'Received type boolean (false)',
1857 // }
1858 // );
1859 
1860 // await rejects(
1861 // wrapped('rsa-pss', { modulusLength: 512, mgf1HashAlgorithm: false }),
1862 // {
1863 // message:
1864 // 'The "options.mgf1HashAlgorithm" property must be of type ' +
1865 // 'string. Received type boolean (false)',
1866 // }
1867 // );
1868 
1869 // await rejects(
1870 // wrapped('rsa-pss', { modulusLength: 512, saltLength: 'foo' }),
1871 // {
1872 // message:
1873 // 'The "options.saltLength" property must be of type number. ' +
1874 // "Received type string ('foo')",
1875 // }
1876 // );
1877 
1878 // TODO(later): BoringSSL currently does not support dsa key generation
1879 // in the same way Node.js does. Uncomment this later when it does.
1880 // await rejects(
1881 // wrapped('dsa', { modulusLength: 512, divisorLength: 'foo' }),
1882 // {
1883 // message:
1884 // 'The "options.divisorLength" property must be of type number. ' +
1885 // "Received type string ('foo')",
1886 // }
1887 // );
1888 
1889 await rejects(wrapped('dh', { prime: 'foo' }), {
1890 message:
1891 'The "options.prime" property must be an instance of Buffer, ' +
1892 "TypedArray, or ArrayBuffer. Received type string ('foo')",
1893 });
1894 
1895 await rejects(wrapped('dh', { primeLength: 'foo' }), {
1896 message:
1897 'The "options.primeLength" property must be of type number. ' +
1898 "Received type string ('foo')",
1899 });
1900 
1901 await rejects(wrapped('dh', { primeLength: -1 }), {
1902 message:
1903 'The value of "options.primeLength" is out of range. It ' +
1904 'must be >= 0 && <= 2147483647. Received -1',
1905 });
1906 
1907 await rejects(wrapped('dh', { primeLength: 10, generator: -1 }), {
1908 message:
1909 'The value of "options.generator" is out of range. It must ' +
1910 'be >= 0 && <= 2147483647. Received -1',
1911 });
1912 
1913 await rejects(wrapped('dh', { groupName: 123 }), {
1914 message:
1915 'The "options.group" property must be of type string. ' +
1916 'Received type number (123)',
1917 });
1918 
1919 await rejects(wrapped('ec', { namedCurve: 'foo', paramEncoding: 'foo' }), {
1920 message:
1921 "The property 'options.paramEncoding' must be one of: " +
1922 "'named', 'explicit'. Received 'foo'",
1923 });
1924 
1925 await rejects(wrapped('ed25519', { publicKeyEncoding: 'foo' }), {
1926 message:
1927 'The "options.publicKeyEncoding" property must be of type ' +
1928 "object. Received type string ('foo')",
1929 });
1930 
1931 await rejects(wrapped('x25519', { privateKeyEncoding: 'foo' }), {
1932 message:
1933 'The "options.privateKeyEncoding" property must be of type ' +
1934 "object. Received type string ('foo')",
1935 });
1936 },
1937};
1938 
1939export const generate_rsa_key_pair = {
1940 test() {
1941 const { publicKey, privateKey } = generateKeyPairSync('rsa', {
1942 modulusLength: 2048,
1943 });
1944 strictEqual(publicKey.type, 'public');
1945 strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 2048);
1946 strictEqual(publicKey.asymmetricKeyDetails.publicExponent, 65537n);
1947 
1948 strictEqual(privateKey.type, 'private');
1949 strictEqual(privateKey.asymmetricKeyDetails.modulusLength, 2048);
1950 strictEqual(privateKey.asymmetricKeyDetails.publicExponent, 65537n);
1951 },
1952};
1953 
1954export const generate_rsa_key_pair_enc = {
1955 test() {
1956 const { publicKey, privateKey } = generateKeyPairSync('rsa', {
1957 modulusLength: 2048,
1958 publicKeyEncoding: {
1959 format: 'der',
1960 type: 'pkcs1',
1961 },
1962 privateKeyEncoding: {
1963 format: 'pem',
1964 type: 'pkcs8',
1965 },
1966 });
1967 
1968 const pubImported = createPublicKey({
1969 key: publicKey,
1970 format: 'der',
1971 type: 'pkcs1',
1972 });
1973 strictEqual(pubImported.type, 'public');
1974 strictEqual(pubImported.asymmetricKeyDetails.modulusLength, 2048);
1975 strictEqual(pubImported.asymmetricKeyDetails.publicExponent, 65537n);
1976 
1977 const imported = createPrivateKey(privateKey);
1978 strictEqual(imported.type, 'private');
1979 strictEqual(imported.asymmetricKeyDetails.modulusLength, 2048);
1980 strictEqual(imported.asymmetricKeyDetails.publicExponent, 65537n);
1981 },
1982};
1983 
1984export const generate_ec_key_pair = {
1985 test() {
1986 const { privateKey, publicKey } = generateKeyPairSync('ec', {
1987 namedCurve: 'P-256',
1988 });
1989 strictEqual(publicKey.type, 'public');
1990 strictEqual(publicKey.asymmetricKeyDetails.namedCurve, 'prime256v1');
1991 strictEqual(privateKey.type, 'private');
1992 strictEqual(privateKey.asymmetricKeyDetails.namedCurve, 'prime256v1');
1993 },
1994};
1995 
1996export const generate_ed25519_key_pair = {
1997 test() {
1998 const { privateKey, publicKey } = generateKeyPairSync('ed25519', {});
1999 strictEqual(publicKey.type, 'public');
2000 strictEqual(privateKey.type, 'private');
2001 strictEqual(publicKey.asymmetricKeyType, 'ed25519');
2002 strictEqual(privateKey.asymmetricKeyType, 'ed25519');
2003 },
2004};
2005 
2006export const generate_x25519_key_pair = {
2007 test() {
2008 const { privateKey, publicKey } = generateKeyPairSync('x25519', {});
2009 strictEqual(publicKey.type, 'public');
2010 strictEqual(privateKey.type, 'private');
2011 strictEqual(publicKey.asymmetricKeyType, 'x25519');
2012 strictEqual(privateKey.asymmetricKeyType, 'x25519');
2013 },
2014};
2015 
2016// TODO(later): BoringSSL with fips does not support generating DH keys
2017// this way. Uncomment this later when it does.
2018// export const generate_dh_key_pair = {
2019// test() {
2020// const { privateKey, publicKey } = generateKeyPairSync('dh', {
2021// group: 'modp14',
2022// });
2023// strictEqual(publicKey.type, 'public');
2024// strictEqual(privateKey.type, 'private');
2025// strictEqual(publicKey.asymmetricKeyType, 'dh');
2026// strictEqual(privateKey.asymmetricKeyType, 'dh');
2027 
2028// const res = diffieHellman({ privateKey, publicKey });
2029// ok(res instanceof Buffer);
2030// strictEqual(res.byteLength, 256);
2031// },
2032// };
2033 
2034// TODO(later): BoringSSL with fips does not support generating DH keys
2035// this way. Uncomment this later when it does.
2036// export const generate_dh_from_fixed_prime = {
2037// test() {
2038// const prime = generatePrimeSync(1024);
2039 
2040// const { privateKey: privateKey1, publicKey: publicKey1 } =
2041// generateKeyPairSync('dh', {
2042// prime,
2043// });
2044// strictEqual(publicKey1.type, 'public');
2045// strictEqual(privateKey1.type, 'private');
2046// strictEqual(publicKey1.asymmetricKeyType, 'dh');
2047// strictEqual(privateKey1.asymmetricKeyType, 'dh');
2048 
2049// const { privateKey: privateKey2, publicKey: publicKey2 } =
2050// generateKeyPairSync('dh', {
2051// prime,
2052// });
2053// strictEqual(publicKey2.type, 'public');
2054// strictEqual(privateKey2.type, 'private');
2055// strictEqual(publicKey2.asymmetricKeyType, 'dh');
2056// strictEqual(privateKey2.asymmetricKeyType, 'dh');
2057 
2058// ok(!publicKey1.equals(publicKey2));
2059// ok(!privateKey1.equals(privateKey2));
2060 
2061// // Once we generate the keys, let's make sure they are usable.
2062 
2063// const res1 = diffieHellman({
2064// privateKey: privateKey2,
2065// publicKey: publicKey1,
2066// });
2067// ok(res1 instanceof Buffer);
2068// strictEqual(res1.byteLength, 128);
2069 
2070// const res2 = diffieHellman({
2071// privateKey: privateKey2,
2072// publicKey: publicKey1,
2073// });
2074// ok(res2 instanceof Buffer);
2075// strictEqual(res2.byteLength, 128);
2076 
2077// deepStrictEqual(res1, res2);
2078// // It's actual data and not just zeroes right?
2079// notDeepStrictEqual(res1, Buffer.alloc(128, 0));
2080 
2081// // Keys generated from different prime groups aren't compatible and should throw.
2082// const prime2 = generatePrimeSync(1024);
2083// const { privateKey: privateKey3, publicKey: publicKey3 } =
2084// generateKeyPairSync('dh', {
2085// prime: prime2,
2086// });
2087// strictEqual(publicKey3.type, 'public');
2088// strictEqual(privateKey3.type, 'private');
2089// strictEqual(publicKey3.asymmetricKeyType, 'dh');
2090// strictEqual(privateKey3.asymmetricKeyType, 'dh');
2091 
2092// throws(
2093// () => diffieHellman({ publicKey: publicKey1, privateKey: privateKey3 }),
2094// {
2095// message: 'Failed to derive shared diffie-hellman secret',
2096// }
2097// );
2098// },
2099// };
2100 
2101export const generate_dh_key_pair_by_length = {
2102 test() {
2103 throws(
2104 () =>
2105 generateKeyPairSync('dh', {
2106 primeLength: 1048,
2107 }),
2108 {
2109 message:
2110 'Generating DH keys from a prime length is not yet implemented',
2111 }
2112 );
2113 },
2114};
2115 
2116export const generate_ed_keypair_promisified = {
2117 async test() {
2118 const promisifiedGenKeyPair = promisify(generateKeyPair);
2119 const { publicKey, privateKey } = await promisifiedGenKeyPair(
2120 'ed25519',
2121 {}
2122 );
2123 strictEqual(publicKey.asymmetricKeyType, 'ed25519');
2124 strictEqual(privateKey.asymmetricKeyType, 'ed25519');
2125 },
2126};
2127 
2128// Regression test: privateKey.export() with cipher and passphrase must
2129// produce an encrypted PEM (BEGIN ENCRYPTED PRIVATE KEY), not plaintext.
2130export const export_encrypted_private_key = {
2131 test() {
2132 const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
2133 
2134 const plainPem = privateKey.export({ format: 'pem', type: 'pkcs8' });
2135 const encryptedPem = privateKey.export({
2136 format: 'pem',
2137 type: 'pkcs8',
2138 cipher: 'aes-256-cbc',
2139 passphrase: 'test-passphrase',
2140 });
2141 
2142 // Encrypted PEM must differ from unencrypted PEM.
2143 ok(plainPem !== encryptedPem);
2144 
2145 // Encrypted PEM must have the ENCRYPTED header.
2146 ok(
2147 encryptedPem.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----'),
2148 'Expected encrypted PEM header'
2149 );
2150 
2151 // Re-import the encrypted PEM using the passphrase.
2152 const reimported = createPrivateKey({
2153 key: encryptedPem,
2154 format: 'pem',
2155 passphrase: 'test-passphrase',
2156 });
2157 
2158 // The re-imported key must produce the same unencrypted export.
2159 strictEqual(reimported.export({ format: 'pem', type: 'pkcs8' }), plainPem);
2160 },
2161};
2162 
2163// Verify encrypted export also works for EC keys.
2164export const export_encrypted_ec_private_key = {
2165 test() {
2166 const { privateKey } = generateKeyPairSync('ec', {
2167 namedCurve: 'P-256',
2168 });
2169 
2170 const encryptedPem = privateKey.export({
2171 format: 'pem',
2172 type: 'pkcs8',
2173 cipher: 'aes-128-cbc',
2174 passphrase: 'ec-passphrase',
2175 });
2176 
2177 ok(
2178 encryptedPem.startsWith('-----BEGIN ENCRYPTED PRIVATE KEY-----'),
2179 'Expected encrypted PEM header for EC key'
2180 );
2181 
2182 const reimported = createPrivateKey({
2183 key: encryptedPem,
2184 format: 'pem',
2185 passphrase: 'ec-passphrase',
2186 });
2187 
2188 strictEqual(
2189 reimported.export({ format: 'pem', type: 'pkcs8' }),
2190 privateKey.export({ format: 'pem', type: 'pkcs8' })
2191 );
2192 },
2193};