@avieldr/react-native-rsa 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +453 -0
  3. package/Rsa.podspec +23 -0
  4. package/android/build.gradle +69 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/rsa/RsaModule.kt +129 -0
  7. package/android/src/main/java/com/rsa/RsaPackage.kt +33 -0
  8. package/android/src/main/java/com/rsa/core/ASN1Utils.kt +201 -0
  9. package/android/src/main/java/com/rsa/core/Algorithms.kt +126 -0
  10. package/android/src/main/java/com/rsa/core/KeyUtils.kt +83 -0
  11. package/android/src/main/java/com/rsa/core/RSACipher.kt +71 -0
  12. package/android/src/main/java/com/rsa/core/RSAKeyGenerator.kt +125 -0
  13. package/android/src/main/java/com/rsa/core/RSASigner.kt +70 -0
  14. package/ios/ASN1Utils.swift +225 -0
  15. package/ios/Algorithms.swift +89 -0
  16. package/ios/KeyUtils.swift +125 -0
  17. package/ios/RSACipher.swift +77 -0
  18. package/ios/RSAKeyGenerator.swift +164 -0
  19. package/ios/RSASigner.swift +101 -0
  20. package/ios/Rsa.h +61 -0
  21. package/ios/Rsa.mm +216 -0
  22. package/lib/module/NativeRsa.js +16 -0
  23. package/lib/module/NativeRsa.js.map +1 -0
  24. package/lib/module/constants.js +24 -0
  25. package/lib/module/constants.js.map +1 -0
  26. package/lib/module/encoding.js +116 -0
  27. package/lib/module/encoding.js.map +1 -0
  28. package/lib/module/errors.js +135 -0
  29. package/lib/module/errors.js.map +1 -0
  30. package/lib/module/index.js +232 -0
  31. package/lib/module/index.js.map +1 -0
  32. package/lib/module/keyInfo.js +286 -0
  33. package/lib/module/keyInfo.js.map +1 -0
  34. package/lib/module/package.json +1 -0
  35. package/lib/module/types.js +2 -0
  36. package/lib/module/types.js.map +1 -0
  37. package/lib/typescript/package.json +1 -0
  38. package/lib/typescript/src/NativeRsa.d.ts +32 -0
  39. package/lib/typescript/src/NativeRsa.d.ts.map +1 -0
  40. package/lib/typescript/src/constants.d.ts +21 -0
  41. package/lib/typescript/src/constants.d.ts.map +1 -0
  42. package/lib/typescript/src/encoding.d.ts +30 -0
  43. package/lib/typescript/src/encoding.d.ts.map +1 -0
  44. package/lib/typescript/src/errors.d.ts +47 -0
  45. package/lib/typescript/src/errors.d.ts.map +1 -0
  46. package/lib/typescript/src/index.d.ts +122 -0
  47. package/lib/typescript/src/index.d.ts.map +1 -0
  48. package/lib/typescript/src/keyInfo.d.ts +7 -0
  49. package/lib/typescript/src/keyInfo.d.ts.map +1 -0
  50. package/lib/typescript/src/types.d.ts +63 -0
  51. package/lib/typescript/src/types.d.ts.map +1 -0
  52. package/package.json +133 -0
  53. package/src/NativeRsa.ts +59 -0
  54. package/src/constants.ts +25 -0
  55. package/src/encoding.ts +139 -0
  56. package/src/errors.ts +206 -0
  57. package/src/index.ts +305 -0
  58. package/src/keyInfo.ts +334 -0
  59. package/src/types.ts +85 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aviel Drori
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,453 @@
1
+ # @avieldr/react-native-rsa
2
+
3
+ High-performance native RSA cryptography for React Native. Uses platform-native crypto libraries (Android `KeyPairGenerator`, iOS `Security` framework) for fast, secure operations.
4
+
5
+ ## Features
6
+
7
+ - **Native RSA key generation** on both iOS and Android
8
+ - **Encrypt/Decrypt** with OAEP or PKCS#1 padding
9
+ - **Sign/Verify** with PSS or PKCS#1 padding
10
+ - **Key sizes**: 1024, 2048, 4096 bit
11
+ - **Hash algorithms**: SHA-1, SHA-256, SHA-384, SHA-512
12
+ - **Output formats**: PKCS#1 and PKCS#8 (private key), SPKI/X.509 (public key)
13
+ - **Key format conversion** between PKCS#1 and PKCS#8
14
+ - **Public key extraction** from private key
15
+ - **Key validation** (JS-only, no bridge call)
16
+ - **Turbo Module** (New Architecture required)
17
+
18
+ ## Why This Package?
19
+
20
+ ### Performance
21
+
22
+ Uses platform-native crypto APIs (`KeyPairGenerator` on Android, `Security.framework` on iOS) instead of JavaScript implementations or slow bridge calls:
23
+
24
+ | Operation | Native (this package) |
25
+ | --------------- | --------------------- |
26
+ | 2048-bit keygen | ~200ms |
27
+ | Encrypt/Decrypt | < 10ms |
28
+ | Sign/Verify | < 10ms |
29
+
30
+ > Note: Pure JS implementations are significantly less efficient for RSA operations.
31
+
32
+ ### Security
33
+
34
+ - Uses **OS-hardened cryptographic APIs** — battle-tested implementations maintained by Apple and Google
35
+ - No bundled crypto libraries that could become outdated or vulnerable
36
+ - Supports modern padding schemes (OAEP, PSS) recommended by security standards
37
+
38
+ ### Lightweight
39
+
40
+ Zero runtime dependencies — only peer dependencies on React and React Native.
41
+
42
+ ### Flexibility
43
+
44
+ - Multiple **padding modes**: OAEP/PKCS#1 for encryption, PSS/PKCS#1 for signatures
45
+ - Multiple **hash algorithms**: SHA-1, SHA-256, SHA-384, SHA-512
46
+ - Multiple **key formats**: PKCS#1 and PKCS#8, with conversion between them
47
+ - Full **TypeScript support** with typed errors and options
48
+
49
+ ### Compatibility
50
+
51
+ - Requires **New Architecture** (Turbo Modules) — React Native **0.71+**
52
+ - Android **API 24+** (Android 7.0)
53
+ - iOS **13.4+**
54
+
55
+ ## Installation
56
+
57
+ ```sh
58
+ npm install @avieldr/react-native-rsa
59
+ # or
60
+ yarn add @avieldr/react-native-rsa
61
+ ```
62
+
63
+ For iOS:
64
+
65
+ ```sh
66
+ cd ios && pod install
67
+ ```
68
+
69
+ > **💡 Tip:** Check out the [`example/`](./example) app in this repository for complete working demonstrations of all features, including key generation, encryption, signing, and error handling.
70
+
71
+ ## Quick Start
72
+
73
+ ```typescript
74
+ import RSA, { base64ToUtf8 } from '@avieldr/react-native-rsa';
75
+
76
+ // Generate a key pair
77
+ const { publicKey, privateKey } = await RSA.generateKeyPair(2048);
78
+
79
+ // Encrypt and decrypt
80
+ const encrypted = await RSA.encrypt('Hello, World!', publicKey);
81
+ const decryptedBase64 = await RSA.decrypt(encrypted, privateKey);
82
+ const decrypted = base64ToUtf8(decryptedBase64); // "Hello, World!"
83
+
84
+ // Sign and verify
85
+ const signature = await RSA.sign('Message to sign', privateKey);
86
+ const isValid = await RSA.verify('Message to sign', signature, publicKey); // true
87
+ ```
88
+
89
+ ## API
90
+
91
+ ### `generateKeyPair(keySize?, options?)`
92
+
93
+ Generate an RSA key pair using native platform crypto.
94
+
95
+ ```typescript
96
+ import RSA from '@avieldr/react-native-rsa';
97
+
98
+ const { publicKey, privateKey } = await RSA.generateKeyPair(2048);
99
+
100
+ // With PKCS#8 format
101
+ const { publicKey, privateKey } = await RSA.generateKeyPair(2048, {
102
+ format: 'pkcs8',
103
+ });
104
+ ```
105
+
106
+ | Parameter | Type | Default | Description |
107
+ | ---------------- | -------------------- | --------- | --------------------------------------- |
108
+ | `keySize` | `number` | `2048` | RSA key size in bits (1024, 2048, 4096) |
109
+ | `options.format` | `'pkcs1' \| 'pkcs8'` | `'pkcs1'` | Private key output format |
110
+
111
+ **Returns:** `Promise<RSAKeyPair>`
112
+
113
+ ```typescript
114
+ interface RSAKeyPair {
115
+ publicKey: string; // PEM (SPKI/X.509): -----BEGIN PUBLIC KEY-----
116
+ privateKey: string; // PEM (PKCS#1): -----BEGIN RSA PRIVATE KEY-----
117
+ // or (PKCS#8): -----BEGIN PRIVATE KEY-----
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ### `encrypt(data, publicKeyPEM, options?)`
124
+
125
+ Encrypt data with an RSA public key.
126
+
127
+ ```typescript
128
+ // Basic encryption (UTF-8 text)
129
+ const encrypted = await RSA.encrypt('Hello, World!', publicKey);
130
+
131
+ // With options
132
+ const encrypted = await RSA.encrypt('Hello, World!', publicKey, {
133
+ padding: 'oaep', // or 'pkcs1'
134
+ hash: 'sha256', // or 'sha1', 'sha384', 'sha512'
135
+ });
136
+
137
+ // Binary data (already base64-encoded)
138
+ const encrypted = await RSA.encrypt(binaryDataBase64, publicKey, {
139
+ encoding: 'base64',
140
+ });
141
+ ```
142
+
143
+ | Parameter | Type | Default | Description |
144
+ | ------------------ | -------------------- | ---------- | ---------------------------------------- |
145
+ | `data` | `string` | — | Data to encrypt (UTF-8 string or base64) |
146
+ | `publicKeyPEM` | `string` | — | Public key in SPKI PEM format |
147
+ | `options.padding` | `'oaep' \| 'pkcs1'` | `'oaep'` | Padding mode (OAEP recommended) |
148
+ | `options.hash` | `HashAlgorithm` | `'sha256'` | Hash algorithm (used with OAEP) |
149
+ | `options.encoding` | `'utf8' \| 'base64'` | `'utf8'` | How to interpret the input string |
150
+
151
+ **Returns:** `Promise<string>` — Base64-encoded ciphertext
152
+
153
+ ---
154
+
155
+ ### `decrypt(encrypted, privateKeyPEM, options?)`
156
+
157
+ Decrypt ciphertext with an RSA private key.
158
+
159
+ ```typescript
160
+ import RSA, { base64ToUtf8 } from '@avieldr/react-native-rsa';
161
+
162
+ const decryptedBase64 = await RSA.decrypt(encrypted, privateKey);
163
+ const plaintext = base64ToUtf8(decryptedBase64); // Convert back to UTF-8
164
+
165
+ // With options (must match encryption options)
166
+ const decryptedBase64 = await RSA.decrypt(encrypted, privateKey, {
167
+ padding: 'oaep',
168
+ hash: 'sha256',
169
+ });
170
+ ```
171
+
172
+ | Parameter | Type | Default | Description |
173
+ | ----------------- | ------------------- | ---------- | ----------------------------------------- |
174
+ | `encrypted` | `string` | — | Base64-encoded ciphertext |
175
+ | `privateKeyPEM` | `string` | — | Private key in PEM format (PKCS#1/PKCS#8) |
176
+ | `options.padding` | `'oaep' \| 'pkcs1'` | `'oaep'` | Padding mode (must match encryption) |
177
+ | `options.hash` | `HashAlgorithm` | `'sha256'` | Hash algorithm (must match encryption) |
178
+
179
+ **Returns:** `Promise<string>` — Base64-encoded plaintext (use `base64ToUtf8()` to convert)
180
+
181
+ ---
182
+
183
+ ### `sign(data, privateKeyPEM, options?)`
184
+
185
+ Sign data with an RSA private key.
186
+
187
+ ```typescript
188
+ const signature = await RSA.sign('Message to sign', privateKey);
189
+
190
+ // With options
191
+ const signature = await RSA.sign('Message to sign', privateKey, {
192
+ padding: 'pss', // or 'pkcs1'
193
+ hash: 'sha256',
194
+ });
195
+ ```
196
+
197
+ | Parameter | Type | Default | Description |
198
+ | ------------------ | -------------------- | ---------- | ----------------------------------------- |
199
+ | `data` | `string` | — | Data to sign (UTF-8 string or base64) |
200
+ | `privateKeyPEM` | `string` | — | Private key in PEM format (PKCS#1/PKCS#8) |
201
+ | `options.padding` | `'pss' \| 'pkcs1'` | `'pss'` | Padding mode (PSS recommended) |
202
+ | `options.hash` | `HashAlgorithm` | `'sha256'` | Hash algorithm |
203
+ | `options.encoding` | `'utf8' \| 'base64'` | `'utf8'` | How to interpret the input string |
204
+
205
+ **Returns:** `Promise<string>` — Base64-encoded signature
206
+
207
+ ---
208
+
209
+ ### `verify(data, signature, publicKeyPEM, options?)`
210
+
211
+ Verify a signature against data using an RSA public key.
212
+
213
+ ```typescript
214
+ const isValid = await RSA.verify('Message to sign', signature, publicKey);
215
+ // true if signature is valid, false otherwise
216
+
217
+ // With options (must match signing options)
218
+ const isValid = await RSA.verify('Message to sign', signature, publicKey, {
219
+ padding: 'pss',
220
+ hash: 'sha256',
221
+ });
222
+ ```
223
+
224
+ | Parameter | Type | Default | Description |
225
+ | ------------------ | -------------------- | ---------- | ----------------------------------- |
226
+ | `data` | `string` | — | Original data that was signed |
227
+ | `signature` | `string` | — | Base64-encoded signature |
228
+ | `publicKeyPEM` | `string` | — | Public key in SPKI PEM format |
229
+ | `options.padding` | `'pss' \| 'pkcs1'` | `'pss'` | Padding mode (must match signing) |
230
+ | `options.hash` | `HashAlgorithm` | `'sha256'` | Hash algorithm (must match signing) |
231
+ | `options.encoding` | `'utf8' \| 'base64'` | `'utf8'` | How to interpret the input string |
232
+
233
+ **Returns:** `Promise<boolean>` — `true` if valid, `false` otherwise
234
+
235
+ ---
236
+
237
+ ### `getPublicKeyFromPrivate(privateKeyPEM)`
238
+
239
+ Extract the public key from an RSA private key.
240
+
241
+ ```typescript
242
+ const publicKey = await RSA.getPublicKeyFromPrivate(privateKey);
243
+ ```
244
+
245
+ Accepts both PKCS#1 and PKCS#8 private key formats.
246
+
247
+ **Returns:** `Promise<string>` — Public key in SPKI PEM format
248
+
249
+ ---
250
+
251
+ ### `convertPrivateKey(pem, targetFormat)`
252
+
253
+ Convert a private key between PKCS#1 and PKCS#8 formats.
254
+
255
+ ```typescript
256
+ // Convert PKCS#1 to PKCS#8
257
+ const pkcs8Key = await RSA.convertPrivateKey(pkcs1Key, 'pkcs8');
258
+
259
+ // Convert PKCS#8 to PKCS#1
260
+ const pkcs1Key = await RSA.convertPrivateKey(pkcs8Key, 'pkcs1');
261
+ ```
262
+
263
+ | Parameter | Type | Description |
264
+ | -------------- | -------------------- | ---------------------------- |
265
+ | `pem` | `string` | Private key in PEM format |
266
+ | `targetFormat` | `'pkcs1' \| 'pkcs8'` | Target format for conversion |
267
+
268
+ **Returns:** `Promise<string>` — Private key in the target format
269
+
270
+ ---
271
+
272
+ ### `getKeyInfo(keyString)`
273
+
274
+ Analyze a PEM key string and return metadata. Runs entirely in JS — no native bridge call.
275
+
276
+ ```typescript
277
+ import { getKeyInfo } from '@avieldr/react-native-rsa';
278
+
279
+ const info = getKeyInfo(privateKey);
280
+ // {
281
+ // isValid: true,
282
+ // format: 'pkcs1',
283
+ // keyType: 'private',
284
+ // pemLineCount: 13,
285
+ // derByteLength: 609,
286
+ // errors: []
287
+ // }
288
+ ```
289
+
290
+ **Returns:** `RSAKeyInfo`
291
+
292
+ ```typescript
293
+ interface RSAKeyInfo {
294
+ isValid: boolean;
295
+ format: 'pkcs1' | 'pkcs8' | 'public' | 'unknown';
296
+ keyType: 'private' | 'public' | 'unknown';
297
+ pemLineCount: number;
298
+ derByteLength: number;
299
+ errors: string[];
300
+ }
301
+ ```
302
+
303
+ ---
304
+
305
+ ## Encoding Utilities
306
+
307
+ The library provides pure-JS encoding utilities that work in all React Native JS engines.
308
+
309
+ ```typescript
310
+ import { utf8ToBase64, base64ToUtf8 } from '@avieldr/react-native-rsa';
311
+
312
+ // Encode UTF-8 text to base64
313
+ const encoded = utf8ToBase64('Hello, 世界! 🎉');
314
+
315
+ // Decode base64 back to UTF-8
316
+ const decoded = base64ToUtf8(encoded);
317
+ ```
318
+
319
+ These are useful for:
320
+
321
+ - Converting decrypted data back to readable text
322
+ - Preparing binary data for encryption
323
+ - Handling Unicode and emoji correctly
324
+
325
+ ---
326
+
327
+ ## Error Handling
328
+
329
+ The library throws `RsaError` for invalid inputs and native failures with specific error codes:
330
+
331
+ ```typescript
332
+ import RSA, { RsaError } from '@avieldr/react-native-rsa';
333
+
334
+ try {
335
+ await RSA.encrypt('data', 'invalid-key');
336
+ } catch (error) {
337
+ if (error instanceof RsaError) {
338
+ console.log(error.code); // 'INVALID_KEY'
339
+ console.log(error.message); // Detailed error message
340
+ console.log(error.cause); // Original error (if from native layer)
341
+ }
342
+ }
343
+ ```
344
+
345
+ ### Validation Errors
346
+
347
+ These are thrown **before** calling native code when inputs are invalid:
348
+
349
+ | Error Code | Description |
350
+ | ------------------ | ----------------------------------------- |
351
+ | `INVALID_INPUT` | Required parameter is missing or empty |
352
+ | `INVALID_KEY` | Key format is wrong or key type mismatch |
353
+ | `INVALID_KEY_SIZE` | Unsupported key size (not 1024/2048/4096) |
354
+ | `INVALID_PADDING` | Unknown padding mode |
355
+ | `INVALID_HASH` | Unknown hash algorithm |
356
+ | `INVALID_FORMAT` | Unknown key format |
357
+ | `INVALID_ENCODING` | Unknown encoding type |
358
+
359
+ ### Native Operation Errors
360
+
361
+ These are thrown when the platform crypto operation fails:
362
+
363
+ | Error Code | Description |
364
+ | ----------------------- | ---------------------------------------------- |
365
+ | `KEY_GENERATION_FAILED` | Native key generation failed |
366
+ | `KEY_EXTRACTION_FAILED` | Failed to extract public key from private key |
367
+ | `KEY_CONVERSION_FAILED` | Failed to convert key between formats |
368
+ | `ENCRYPTION_FAILED` | Encryption failed (e.g., data too large) |
369
+ | `DECRYPTION_FAILED` | Decryption failed (e.g., wrong key or padding) |
370
+ | `SIGNING_FAILED` | Signing operation failed |
371
+ | `VERIFICATION_FAILED` | Signature verification failed |
372
+
373
+ ### TypeScript Support
374
+
375
+ The error code type is exported for TypeScript users:
376
+
377
+ ```typescript
378
+ import type { RsaErrorCode } from '@avieldr/react-native-rsa';
379
+
380
+ function handleError(code: RsaErrorCode) {
381
+ switch (code) {
382
+ case 'INVALID_KEY':
383
+ // Handle invalid key
384
+ break;
385
+ case 'DECRYPTION_FAILED':
386
+ // Handle decryption failure
387
+ break;
388
+ // ...
389
+ }
390
+ }
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Types
396
+
397
+ All types are exported for TypeScript users:
398
+
399
+ ```typescript
400
+ import type {
401
+ RSAKeyPair,
402
+ RSAKeyInfo,
403
+ GenerateKeyPairOptions,
404
+ KeyFormat,
405
+ EncryptOptions,
406
+ DecryptOptions,
407
+ SignOptions,
408
+ VerifyOptions,
409
+ EncryptionPadding,
410
+ SignaturePadding,
411
+ HashAlgorithm,
412
+ InputEncoding,
413
+ RsaErrorCode,
414
+ } from '@avieldr/react-native-rsa';
415
+ ```
416
+
417
+ | Type | Values |
418
+ | ------------------- | -------------------------------------------- |
419
+ | `KeyFormat` | `'pkcs1' \| 'pkcs8'` |
420
+ | `RsaErrorCode` | See [Error Handling](#error-handling) |
421
+ | `EncryptionPadding` | `'oaep' \| 'pkcs1'` |
422
+ | `SignaturePadding` | `'pss' \| 'pkcs1'` |
423
+ | `HashAlgorithm` | `'sha1' \| 'sha256' \| 'sha384' \| 'sha512'` |
424
+ | `InputEncoding` | `'utf8' \| 'base64'` |
425
+
426
+ ---
427
+
428
+ ## Benchmarks
429
+
430
+ Typical key generation times on modern devices:
431
+
432
+ | Key Size | Time |
433
+ | -------- | -------- |
434
+ | 1024-bit | ~50ms |
435
+ | 2048-bit | ~200ms |
436
+ | 4096-bit | ~2,000ms |
437
+
438
+ _Times vary by device. Measured on mid-range Android and iPhone devices._
439
+
440
+ ---
441
+
442
+ ## Security Recommendations
443
+
444
+ - **Use OAEP padding** for encryption (default) — PKCS#1 v1.5 is vulnerable to padding oracle attacks
445
+ - **Use PSS padding** for signatures (default) — more secure than PKCS#1 v1.5
446
+ - **Use SHA-256 or higher** — SHA-1 is deprecated for new applications
447
+ - **Use 2048-bit keys minimum** — 1024-bit is considered weak
448
+
449
+ ---
450
+
451
+ ## License
452
+
453
+ MIT
package/Rsa.podspec ADDED
@@ -0,0 +1,23 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "Rsa"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/avieldr/react-native-rsa-turbo.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ s.swift_version = "5.0"
20
+ s.frameworks = "Security"
21
+
22
+ install_modules_dependencies(s)
23
+ end
@@ -0,0 +1,69 @@
1
+ buildscript {
2
+ ext.Rsa = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+
14
+ return Rsa[prop]
15
+ }
16
+
17
+ repositories {
18
+ google()
19
+ mavenCentral()
20
+ }
21
+
22
+ dependencies {
23
+ classpath "com.android.tools.build:gradle:8.7.2"
24
+ // noinspection DifferentKotlinGradleVersion
25
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
26
+ }
27
+ }
28
+
29
+
30
+ apply plugin: "com.android.library"
31
+ apply plugin: "kotlin-android"
32
+
33
+ apply plugin: "com.facebook.react"
34
+
35
+ android {
36
+ namespace "com.rsa"
37
+
38
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
39
+
40
+ defaultConfig {
41
+ minSdkVersion getExtOrDefault("minSdkVersion")
42
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
43
+ }
44
+
45
+ buildFeatures {
46
+ buildConfig true
47
+ }
48
+
49
+ buildTypes {
50
+ release {
51
+ minifyEnabled false
52
+ }
53
+ }
54
+
55
+ lint {
56
+ disable "GradleCompatible"
57
+ }
58
+
59
+ compileOptions {
60
+ sourceCompatibility JavaVersion.VERSION_1_8
61
+ targetCompatibility JavaVersion.VERSION_1_8
62
+ }
63
+ }
64
+
65
+ dependencies {
66
+ implementation "com.facebook.react:react-android"
67
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
68
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
69
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,129 @@
1
+ package com.rsa
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.rsa.core.RSACipher
7
+ import com.rsa.core.RSAKeyGenerator
8
+ import com.rsa.core.RSASigner
9
+ import kotlinx.coroutines.CoroutineScope
10
+ import kotlinx.coroutines.Dispatchers
11
+ import kotlinx.coroutines.SupervisorJob
12
+ import kotlinx.coroutines.cancel
13
+ import kotlinx.coroutines.launch
14
+
15
+ /**
16
+ * React Native TurboModule bridging RSA operations to JavaScript.
17
+ *
18
+ * Each method launches a coroutine on Dispatchers.Default (background thread pool)
19
+ * to keep crypto operations off the main thread, and resolves/rejects the JS promise.
20
+ *
21
+ * Dispatches to:
22
+ * - [RSAKeyGenerator] for key generation, public key extraction, and format conversion
23
+ * - [RSACipher] for encrypt/decrypt
24
+ * - [RSASigner] for sign/verify
25
+ */
26
+ class RsaModule(reactContext: ReactApplicationContext) :
27
+ NativeRsaSpec(reactContext) {
28
+
29
+ private val supervisorJob = SupervisorJob()
30
+ private val moduleScope = CoroutineScope(Dispatchers.Default + supervisorJob)
31
+ private val keyGenerator = RSAKeyGenerator.getInstance()
32
+
33
+ override fun invalidate() {
34
+ supervisorJob.cancel("RsaModule invalidated")
35
+ super.invalidate()
36
+ }
37
+
38
+ // --- Key Generation ---
39
+
40
+ override fun generateKeyPair(keySize: Double, format: String, promise: Promise) {
41
+ moduleScope.launch {
42
+ try {
43
+ val result = keyGenerator.generateKeyPair(keySize.toInt(), format)
44
+ val map = Arguments.createMap()
45
+ map.putString("publicKey", result.publicKey)
46
+ map.putString("privateKey", result.privateKey)
47
+ promise.resolve(map)
48
+ } catch (e: Exception) {
49
+ promise.reject("RSAKeyGenerationError", e.message, e)
50
+ }
51
+ }
52
+ }
53
+
54
+ override fun getPublicKeyFromPrivate(privateKeyPEM: String, promise: Promise) {
55
+ moduleScope.launch {
56
+ try {
57
+ val publicKeyPEM = keyGenerator.getPublicKeyFromPrivate(privateKeyPEM)
58
+ promise.resolve(publicKeyPEM)
59
+ } catch (e: Exception) {
60
+ promise.reject("RSAKeyExtractionError", e.message, e)
61
+ }
62
+ }
63
+ }
64
+
65
+ // --- Encrypt / Decrypt ---
66
+
67
+ override fun encrypt(dataBase64: String, publicKeyPEM: String, padding: String, hash: String, promise: Promise) {
68
+ moduleScope.launch {
69
+ try {
70
+ val result = RSACipher.encrypt(dataBase64, publicKeyPEM, padding, hash)
71
+ promise.resolve(result)
72
+ } catch (e: Exception) {
73
+ promise.reject("RSAEncryptError", e.message, e)
74
+ }
75
+ }
76
+ }
77
+
78
+ override fun decrypt(dataBase64: String, privateKeyPEM: String, padding: String, hash: String, promise: Promise) {
79
+ moduleScope.launch {
80
+ try {
81
+ val result = RSACipher.decrypt(dataBase64, privateKeyPEM, padding, hash)
82
+ promise.resolve(result)
83
+ } catch (e: Exception) {
84
+ promise.reject("RSADecryptError", e.message, e)
85
+ }
86
+ }
87
+ }
88
+
89
+ // --- Sign / Verify ---
90
+
91
+ override fun sign(dataBase64: String, privateKeyPEM: String, padding: String, hash: String, promise: Promise) {
92
+ moduleScope.launch {
93
+ try {
94
+ val result = RSASigner.sign(dataBase64, privateKeyPEM, padding, hash)
95
+ promise.resolve(result)
96
+ } catch (e: Exception) {
97
+ promise.reject("RSASignError", e.message, e)
98
+ }
99
+ }
100
+ }
101
+
102
+ override fun verify(dataBase64: String, signatureBase64: String, publicKeyPEM: String, padding: String, hash: String, promise: Promise) {
103
+ moduleScope.launch {
104
+ try {
105
+ val result = RSASigner.verify(dataBase64, signatureBase64, publicKeyPEM, padding, hash)
106
+ promise.resolve(result)
107
+ } catch (e: Exception) {
108
+ promise.reject("RSAVerifyError", e.message, e)
109
+ }
110
+ }
111
+ }
112
+
113
+ // --- Key Format Conversion ---
114
+
115
+ override fun convertPrivateKey(pem: String, targetFormat: String, promise: Promise) {
116
+ moduleScope.launch {
117
+ try {
118
+ val result = keyGenerator.convertPrivateKey(pem, targetFormat)
119
+ promise.resolve(result)
120
+ } catch (e: Exception) {
121
+ promise.reject("RSAConvertKeyError", e.message, e)
122
+ }
123
+ }
124
+ }
125
+
126
+ companion object {
127
+ const val NAME = NativeRsaSpec.NAME
128
+ }
129
+ }