如何在NodeJS/Typescript中将JWK转换为PEM

huangapple go评论79阅读模式
英文:

How to convert JWK to PEM in NodeJS/Typescript

问题

我想从jwk端点下载公钥并将其转换为jwtsing函数所需的pem格式。

export type Secret =
    | string
    | Buffer
    | { key: string | Buffer; passphrase: string };

jwk格式可以使用crypto中的subtle导入为JsonWebKey类型的webKey,并将其返回为CryptoKey

const pubKey = await subtle.importKey(
    "jwk",
    webKey,
    { hash: 'SHA-256', name: 'RSA-OAEP' },
    true,
    []
  );

CryptoKey可以使用subtle.exportKey导出,但结果是ByteArray,将其转换为pem需要两步:1. 将字节转换为字符,然后2. 转换为Base64,然后添加到-----BEGIN PUBLIC KEY-----中,并在每64个字符后添加新行,这将产生无效的密钥

/**
 * Exports the given key into the specified format, if supported.
 *
 * If the `<CryptoKey>` is not extractable, the returned promise will reject.
 *
 * When `format` is either `'pkcs8'` or `'spki'` and the export is successful,
 * the returned promise will be resolved with an `<ArrayBuffer>` containing the exported key data.
 *
 * When `format` is `'jwk'` and the export is successful, the returned promise will be resolved with a
 * JavaScript object conforming to the {@link https://tools.ietf.org/html/rfc7517 JSON Web Key} specification.
 * @param format Must be one of `'raw'`, `'pkcs8'`, `'spki'`, or `'jwk'`.
 * @returns `<Promise>` containing `<ArrayBuffer>`.
 * @since v15.0.0
 */
exportKey(format: 'jwk', key: CryptoKey): Promise<JsonWebKey>;
exportKey(format: Exclude<KeyFormat, 'jwk'>, key: CryptoKey): Promise<ArrayBuffer>;

此外,公钥只能导出为spkijwk,不支持raw

问题是如何尽可能简单地将jwk转换为pem

英文:

I'd like to download public key from jwk endpoint and convert it into pem format that is required in sing function for jwt.

export type Secret =
    | string
    | Buffer
    | { key: string | Buffer; passphrase: string };

The jwk format can be imported with subtle from crypto as the webKey of JsonWebKey type and returned as CryptoKey

const pubKey = await subtle.importKey(
    &quot;jwk&quot;,
    webKey,
    { hash: &#39;SHA-256&#39;, name: &#39;RSA-OAEP&#39; },
    true,
    []
  );

The CryptoKey can be exported with subtle.exportKey but the result is in ByteArray and converting it to pem with 1. converting bytes to chars then 2. to base64 and adding it to -----BEGIN PUBLIC KEY----- with envelope with adding new line every 64 characters produces an invalid key.

/**
 * Exports the given key into the specified format, if supported.
 *
 * If the `&lt;CryptoKey&gt;` is not extractable, the returned promise will reject.
 *
 * When `format` is either `&#39;pkcs8&#39;` or `&#39;spki&#39;` and the export is successful,
 * the returned promise will be resolved with an `&lt;ArrayBuffer&gt;` containing the exported key data.
 *
 * When `format` is `&#39;jwk&#39;` and the export is successful, the returned promise will be resolved with a
 * JavaScript object conforming to the {@link https://tools.ietf.org/html/rfc7517 JSON Web Key} specification.
 * @param format Must be one of `&#39;raw&#39;`, `&#39;pkcs8&#39;`, `&#39;spki&#39;`, or `&#39;jwk&#39;`.
 * @returns `&lt;Promise&gt;` containing `&lt;ArrayBuffer&gt;`.
 * @since v15.0.0
 */
exportKey(format: &#39;jwk&#39;, key: CryptoKey): Promise&lt;JsonWebKey&gt;;
exportKey(format: Exclude&lt;KeyFormat, &#39;jwk&#39;&gt;, key: CryptoKey): Promise&lt;ArrayBuffer&gt;;

Also, the pub key can be exported only to spki and jwk the raw is not supported.

The question in how to convert jwk to pem as simply as possible?!

答案1

得分: 1

以下是翻译好的内容:

"要诀是使用 static from(key: webcrypto.CryptoKey): KeyObject;CryptoKey 转换为 KeyObject,然后使用 KeyObject 的成员 export(options: KeyExportOptions&lt;&#39;pem&#39;&gt;): string | Buffer;

函数如下:

export async function jwkToPem(webKey: JsonWebKey): Promise&lt;string&gt; {
  const cryptoKey = await subtle.importKey(
    &quot;jwk&quot;,
    webKey,
    { hash: &#39;SHA-256&#39;, name: &#39;RSA-OAEP&#39; },
    true,
    []
  );

  return KeyObject.from(cryptoKey).export({ format: &quot;pem&quot;, type: &quot;pkcs1&quot;}).toString();
}

Topaco's suggestion

使用 createPublicKey 可能更简单,它接受 JsonWebKeyInput 并生成 KeyObject,可以轻松导出。

/**
 * 创建并返回包含公钥的新密钥对象。如果 `key` 是字符串或 `Buffer`,则假定 `format` 为 `&#39;pem&#39;`;如果 `key` 是带有类型为 `&#39;private&#39;` 的 `KeyObject`,则从给定的私钥派生公钥;
 * 否则,`key` 必须是具有上述属性的对象。
 *
 * 如果格式为 `&#39;pem&#39;`,则 `&#39;key&#39;` 也可以是 X.509 证书。
 *
 * 由于可以从私钥派生公钥,因此可以传递私钥而不是公钥。在这种情况下,此函数的行为就像已调用 {@link createPrivateKey} 一样,只是返回的 `KeyObject` 的类型将为 `&#39;public&#39;`,并且无法从返回的 `KeyObject` 中提取私钥。类似地,如果给定带有类型`&#39;private&#39;` 的 `KeyObject`,则将返回一个新的具有类型 `&#39;public&#39;` 的 `KeyObject`,并且无法从返回的对象中提取私钥。
 * @since v11.6.0
 */
function createPublicKey(key: PublicKeyInput | string | Buffer | KeyObject | JsonWebKeyInput): KeyObject;

然后

export async function jwkToPem(webKey: JsonWebKey): Promise&lt;string&gt; {
  const pubKey: KeyObject = createPublicKey({
    key: webKey,
    format: &#39;jwk&#39;
  });

  return pubKey.export({ format: &quot;pem&quot;, type: &quot;pkcs1&quot;}).toString();
}
英文:

The trick is converting CryptoKey to KeyObject with static from(key: webcrypto.CryptoKey): KeyObject; and then use export(options: KeyExportOptions&lt;&#39;pem&#39;&gt;): string | Buffer; that is member of KeyObject.

The function would be

export async function jwkToPem(webKey: JsonWebKey): Promise&lt;string&gt; {
  const cryptoKey = await subtle.importKey(
    &quot;jwk&quot;,
    webKey,
    { hash: &#39;SHA-256&#39;, name: &#39;RSA-OAEP&#39; },
    true,
    []
  );

  return KeyObject.from(cryptoKey).export({ format: &quot;pem&quot;, type: &quot;pkcs1&quot;}).toString();
}

Topaco's suggestion

It could be even easier using createPublicKey that accepts JsonWebKeyInput and produces KeyObject that could be easily exported.

    /**
     * Creates and returns a new key object containing a public key. If `key` is a
     * string or `Buffer`, `format` is assumed to be `&#39;pem&#39;`; if `key` is a `KeyObject`with type `&#39;private&#39;`, the public key is derived from the given private key;
     * otherwise, `key` must be an object with the properties described above.
     *
     * If the format is `&#39;pem&#39;`, the `&#39;key&#39;` may also be an X.509 certificate.
     *
     * Because public keys can be derived from private keys, a private key may be
     * passed instead of a public key. In that case, this function behaves as if {@link createPrivateKey} had been called, except that the type of the
     * returned `KeyObject` will be `&#39;public&#39;` and that the private key cannot be
     * extracted from the returned `KeyObject`. Similarly, if a `KeyObject` with type`&#39;private&#39;` is given, a new `KeyObject` with type `&#39;public&#39;` will be returned
     * and it will be impossible to extract the private key from the returned object.
     * @since v11.6.0
     */
    function createPublicKey(key: PublicKeyInput | string | Buffer | KeyObject | JsonWebKeyInput): KeyObject;

then

export async function jwkToPem(webKey: JsonWebKey): Promise&lt;string&gt; {
  const pubKey: KeyObject = createPublicKey({
    key: webKey,
    format: &#39;jwk&#39;
  });

  return pubKey.export({ format: &quot;pem&quot;, type: &quot;pkcs1&quot;}).toString();
}

huangapple
  • 本文由 发表于 2023年1月11日 02:51:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75074565.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定