Skip to content

Commit 6ef1915

Browse files
committed
feat: implement JWK export for KeyObject (createPrivateKey/createPublicKey)
Wire up PublicKeyObject.export({ format: 'jwk' }) and PrivateKeyObject.export({ format: 'jwk' }) to the existing native exportJwk() method instead of throwing 'not implemented'. The native C++ implementation was already complete — this change bridges the TypeScript layer to call it.
1 parent 9b9bf9f commit 6ef1915

3 files changed

Lines changed: 49 additions & 12 deletions

File tree

packages/react-native-quick-crypto/src/hkdf.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export interface HkdfAlgorithm {
1717

1818
export interface CryptoKeyInternal {
1919
keyObject: {
20-
export: () => Buffer;
20+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
21+
export: (...args: any[]) => any;
2122
};
2223
}
2324

packages/react-native-quick-crypto/src/keys/classes.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { NitroModules } from 'react-native-nitro-modules';
33
import type {
44
AsymmetricKeyType,
55
EncodingOptions,
6+
JWK,
67
KeyDetail,
78
KeyObjectHandle,
89
KeyUsage,
@@ -81,10 +82,10 @@ export class KeyObject {
8182

8283
export(options: { format: 'pem' } & EncodingOptions): string | Buffer;
8384
export(options?: { format: 'der' } & EncodingOptions): Buffer;
84-
export(options?: { format: 'jwk' } & EncodingOptions): never;
85-
export(options?: EncodingOptions): string | Buffer;
85+
export(options?: { format: 'jwk' } & EncodingOptions): JWK;
86+
export(options?: EncodingOptions): string | Buffer | JWK;
8687
// eslint-disable-next-line @typescript-eslint/no-unused-vars
87-
export(_options?: EncodingOptions): string | Buffer {
88+
export(_options?: EncodingOptions): string | Buffer | JWK {
8889
// This is a placeholder and should be overridden by subclasses.
8990
throw new Error('export() must be implemented by subclasses');
9091
}
@@ -284,10 +285,10 @@ export class PublicKeyObject extends AsymmetricKeyObject {
284285

285286
export(options: { format: 'pem' } & EncodingOptions): string;
286287
export(options: { format: 'der' } & EncodingOptions): Buffer;
287-
export(options: { format: 'jwk' } & EncodingOptions): never;
288-
export(options: EncodingOptions): string | Buffer {
288+
export(options: { format: 'jwk' } & EncodingOptions): JWK;
289+
export(options: EncodingOptions): string | Buffer | JWK {
289290
if (options?.format === 'jwk') {
290-
throw new Error('PublicKey export for jwk is not implemented');
291+
return this.handle.exportJwk({}, false);
291292
}
292293
const { format, type } = parsePublicKeyEncoding(
293294
options,
@@ -309,13 +310,13 @@ export class PrivateKeyObject extends AsymmetricKeyObject {
309310

310311
export(options: { format: 'pem' } & EncodingOptions): string;
311312
export(options: { format: 'der' } & EncodingOptions): Buffer;
312-
export(options: { format: 'jwk' } & EncodingOptions): never;
313-
export(options: EncodingOptions): string | Buffer {
313+
export(options: { format: 'jwk' } & EncodingOptions): JWK;
314+
export(options: EncodingOptions): string | Buffer | JWK {
314315
if (options?.format === 'jwk') {
315316
if (options.passphrase !== undefined) {
316317
throw new Error('jwk does not support encryption');
317318
}
318-
throw new Error('PrivateKey export for jwk is not implemented');
319+
return this.handle.exportJwk({}, false);
319320
}
320321
const { format, type, cipher, passphrase } = parsePrivateKeyEncoding(
321322
options,

packages/react-native-quick-crypto/src/keys/index.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,19 @@ import {
2727
parsePrivateKeyEncoding,
2828
parsePublicKeyEncoding,
2929
} from './utils';
30-
import type { BinaryLike } from '../utils';
30+
import { NitroModules } from 'react-native-nitro-modules';
31+
import type { BinaryLike, JWK, KeyObjectHandle } from '../utils';
3132
import {
3233
binaryLikeToArrayBuffer as toAB,
3334
isStringOrBuffer,
3435
KFormatType,
3536
KeyEncoding,
37+
KeyType,
3638
} from '../utils';
3739
import { randomBytes } from '../random';
3840

3941
interface KeyInputObject {
40-
key: BinaryLike | KeyObject | CryptoKey;
42+
key: BinaryLike | KeyObject | CryptoKey | JWK;
4143
format?: 'pem' | 'der' | 'jwk';
4244
type?: 'pkcs1' | 'pkcs8' | 'spki' | 'sec1';
4345
passphrase?: BinaryLike;
@@ -123,6 +125,29 @@ function prepareAsymmetricKey(
123125
}
124126

125127
function createPublicKey(key: KeyInput): PublicKeyObject {
128+
if (typeof key === 'object' && 'key' in key && key.format === 'jwk') {
129+
const handle =
130+
NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
131+
const keyType = handle.initJwk(key.key as JWK);
132+
if (keyType === undefined) {
133+
throw new Error('Failed to import JWK');
134+
}
135+
if (keyType === KeyType.PRIVATE) {
136+
// Extract public from private
137+
const exported = handle.exportKey(KFormatType.DER, KeyEncoding.SPKI);
138+
const pubHandle =
139+
NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
140+
pubHandle.init(
141+
KeyType.PUBLIC,
142+
exported,
143+
KFormatType.DER,
144+
KeyEncoding.SPKI,
145+
);
146+
return new PublicKeyObject(pubHandle);
147+
}
148+
return new PublicKeyObject(handle);
149+
}
150+
126151
const { data, format, type } = prepareAsymmetricKey(key, true);
127152

128153
// Map format string to KFormatType enum
@@ -144,6 +169,16 @@ function createPublicKey(key: KeyInput): PublicKeyObject {
144169
}
145170

146171
function createPrivateKey(key: KeyInput): PrivateKeyObject {
172+
if (typeof key === 'object' && 'key' in key && key.format === 'jwk') {
173+
const handle =
174+
NitroModules.createHybridObject<KeyObjectHandle>('KeyObjectHandle');
175+
const keyType = handle.initJwk(key.key as JWK);
176+
if (keyType === undefined || keyType !== KeyType.PRIVATE) {
177+
throw new Error('Failed to import private key from JWK');
178+
}
179+
return new PrivateKeyObject(handle);
180+
}
181+
147182
const { data, format, type } = prepareAsymmetricKey(key, false);
148183

149184
// Map format string to KFormatType enum

0 commit comments

Comments
 (0)