@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.
- package/LICENSE +20 -0
- package/README.md +453 -0
- package/Rsa.podspec +23 -0
- package/android/build.gradle +69 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/rsa/RsaModule.kt +129 -0
- package/android/src/main/java/com/rsa/RsaPackage.kt +33 -0
- package/android/src/main/java/com/rsa/core/ASN1Utils.kt +201 -0
- package/android/src/main/java/com/rsa/core/Algorithms.kt +126 -0
- package/android/src/main/java/com/rsa/core/KeyUtils.kt +83 -0
- package/android/src/main/java/com/rsa/core/RSACipher.kt +71 -0
- package/android/src/main/java/com/rsa/core/RSAKeyGenerator.kt +125 -0
- package/android/src/main/java/com/rsa/core/RSASigner.kt +70 -0
- package/ios/ASN1Utils.swift +225 -0
- package/ios/Algorithms.swift +89 -0
- package/ios/KeyUtils.swift +125 -0
- package/ios/RSACipher.swift +77 -0
- package/ios/RSAKeyGenerator.swift +164 -0
- package/ios/RSASigner.swift +101 -0
- package/ios/Rsa.h +61 -0
- package/ios/Rsa.mm +216 -0
- package/lib/module/NativeRsa.js +16 -0
- package/lib/module/NativeRsa.js.map +1 -0
- package/lib/module/constants.js +24 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/encoding.js +116 -0
- package/lib/module/encoding.js.map +1 -0
- package/lib/module/errors.js +135 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/index.js +232 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/keyInfo.js +286 -0
- package/lib/module/keyInfo.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeRsa.d.ts +32 -0
- package/lib/typescript/src/NativeRsa.d.ts.map +1 -0
- package/lib/typescript/src/constants.d.ts +21 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/encoding.d.ts +30 -0
- package/lib/typescript/src/encoding.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +47 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +122 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/keyInfo.d.ts +7 -0
- package/lib/typescript/src/keyInfo.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +63 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +133 -0
- package/src/NativeRsa.ts +59 -0
- package/src/constants.ts +25 -0
- package/src/encoding.ts +139 -0
- package/src/errors.ts +206 -0
- package/src/index.ts +305 -0
- package/src/keyInfo.ts +334 -0
- package/src/types.ts +85 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Security
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Result of RSA key pair generation, containing both keys as PEM strings.
|
|
6
|
+
* Exposed to Objective-C for bridge compatibility.
|
|
7
|
+
*/
|
|
8
|
+
@objc public class RSAKeyPairResult: NSObject {
|
|
9
|
+
@objc public let publicKey: String
|
|
10
|
+
@objc public let privateKey: String
|
|
11
|
+
|
|
12
|
+
init(publicKey: String, privateKey: String) {
|
|
13
|
+
self.publicKey = publicKey
|
|
14
|
+
self.privateKey = privateKey
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* RSA key generation and conversion operations.
|
|
20
|
+
*
|
|
21
|
+
* Thread-safe singleton that provides:
|
|
22
|
+
* - Key pair generation (PKCS#1 or PKCS#8 private key format)
|
|
23
|
+
* - Public key extraction from a private key
|
|
24
|
+
* - Private key format conversion between PKCS#1 and PKCS#8
|
|
25
|
+
*
|
|
26
|
+
* Uses the iOS Security framework (`SecKey` APIs) for key generation and import.
|
|
27
|
+
* Delegates ASN.1 encoding to `ASN1Utils` and PEM/key-loading to `KeyUtils`.
|
|
28
|
+
*/
|
|
29
|
+
@objc public final class RSAKeyGenerator: NSObject {
|
|
30
|
+
|
|
31
|
+
@objc public static let shared = RSAKeyGenerator()
|
|
32
|
+
private override init() { super.init() }
|
|
33
|
+
|
|
34
|
+
// MARK: - Key Pair Generation
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate an RSA key pair using the iOS Security framework.
|
|
38
|
+
*
|
|
39
|
+
* - Parameters:
|
|
40
|
+
* - keySize: RSA key size in bits (e.g. 1024, 2048, 4096)
|
|
41
|
+
* - format: "pkcs1" or "pkcs8" — controls the private key output format.
|
|
42
|
+
* Public key is always SPKI ("BEGIN PUBLIC KEY").
|
|
43
|
+
* - Throws: If key generation or export fails
|
|
44
|
+
* - Returns: RSAKeyPairResult with public and private keys in PEM format
|
|
45
|
+
*/
|
|
46
|
+
@objc public func generateKeyPair(keySize: Int, format: String) throws -> RSAKeyPairResult {
|
|
47
|
+
let attributes: [String: Any] = [
|
|
48
|
+
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
|
|
49
|
+
kSecAttrKeySizeInBits as String: keySize,
|
|
50
|
+
kSecPrivateKeyAttrs as String: [
|
|
51
|
+
kSecAttrIsPermanent as String: false
|
|
52
|
+
]
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
var error: Unmanaged<CFError>?
|
|
56
|
+
guard let privateSecKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
|
|
57
|
+
let errorMessage = error?.takeRetainedValue().localizedDescription ?? "Unknown error"
|
|
58
|
+
throw NSError(domain: "RSAKeyGenerator", code: 1,
|
|
59
|
+
userInfo: [NSLocalizedDescriptionKey: "Failed to generate RSA key: \(errorMessage)"])
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// SecKeyCopyExternalRepresentation returns PKCS#1 DER for RSA private keys
|
|
63
|
+
guard let privateKeyData = SecKeyCopyExternalRepresentation(privateSecKey, &error) as Data? else {
|
|
64
|
+
let errorMessage = error?.takeRetainedValue().localizedDescription ?? "Unknown error"
|
|
65
|
+
throw NSError(domain: "RSAKeyGenerator", code: 2,
|
|
66
|
+
userInfo: [NSLocalizedDescriptionKey: "Failed to export RSA key: \(errorMessage)"])
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Encode private key in the requested format
|
|
70
|
+
let privateKeyPEM: String
|
|
71
|
+
if format == "pkcs8" {
|
|
72
|
+
let pkcs8Data = ASN1Utils.wrapPkcs1InPkcs8(pkcs1Data: privateKeyData)
|
|
73
|
+
privateKeyPEM = KeyUtils.toPEM(data: pkcs8Data, header: "PRIVATE KEY")
|
|
74
|
+
} else {
|
|
75
|
+
privateKeyPEM = KeyUtils.toPEM(data: privateKeyData, header: "RSA PRIVATE KEY")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Extract and export public key
|
|
79
|
+
guard let publicSecKey = SecKeyCopyPublicKey(privateSecKey) else {
|
|
80
|
+
throw NSError(domain: "RSAKeyGenerator", code: 3,
|
|
81
|
+
userInfo: [NSLocalizedDescriptionKey: "Could not extract public key"])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
guard let publicKeyRawData = SecKeyCopyExternalRepresentation(publicSecKey, &error) as Data? else {
|
|
85
|
+
let errorMessage = error?.takeRetainedValue().localizedDescription ?? "Unknown error"
|
|
86
|
+
throw NSError(domain: "RSAKeyGenerator", code: 4,
|
|
87
|
+
userInfo: [NSLocalizedDescriptionKey: "Failed to export public key: \(errorMessage)"])
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Wrap raw PKCS#1 public key in SPKI for standard PEM format
|
|
91
|
+
let spkiData = ASN1Utils.wrapPublicKeyInSPKI(rawKeyData: publicKeyRawData)
|
|
92
|
+
let publicKeyPEM = KeyUtils.toPEM(data: spkiData, header: "PUBLIC KEY")
|
|
93
|
+
|
|
94
|
+
return RSAKeyPairResult(publicKey: publicKeyPEM, privateKey: privateKeyPEM)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// MARK: - Public Key Extraction
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Extract the public key from an RSA private key PEM string.
|
|
101
|
+
*
|
|
102
|
+
* Works with both PKCS#1 and PKCS#8 input formats.
|
|
103
|
+
* The returned public key is always in SPKI PEM format ("BEGIN PUBLIC KEY").
|
|
104
|
+
*/
|
|
105
|
+
@objc public func getPublicKeyFromPrivate(privateKeyPEM: String) throws -> String {
|
|
106
|
+
// Load private key (handles both PKCS#1 and PKCS#8 automatically)
|
|
107
|
+
let privateSecKey = try KeyUtils.loadPrivateKey(pem: privateKeyPEM)
|
|
108
|
+
|
|
109
|
+
guard let publicSecKey = SecKeyCopyPublicKey(privateSecKey) else {
|
|
110
|
+
throw NSError(domain: "RSAKeyGenerator", code: 8,
|
|
111
|
+
userInfo: [NSLocalizedDescriptionKey: "Could not extract public key"])
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var error: Unmanaged<CFError>?
|
|
115
|
+
guard let publicKeyRawData = SecKeyCopyExternalRepresentation(publicSecKey, &error) as Data? else {
|
|
116
|
+
let errorMessage = error?.takeRetainedValue().localizedDescription ?? "Unknown error"
|
|
117
|
+
throw NSError(domain: "RSAKeyGenerator", code: 9,
|
|
118
|
+
userInfo: [NSLocalizedDescriptionKey: "Failed to export public key: \(errorMessage)"])
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let spkiData = ASN1Utils.wrapPublicKeyInSPKI(rawKeyData: publicKeyRawData)
|
|
122
|
+
return KeyUtils.toPEM(data: spkiData, header: "PUBLIC KEY")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// MARK: - Key Format Conversion
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Convert a private key PEM between PKCS#1 and PKCS#8 formats.
|
|
129
|
+
*
|
|
130
|
+
* - Parameters:
|
|
131
|
+
* - pem: The private key in PEM format
|
|
132
|
+
* - targetFormat: "pkcs1" or "pkcs8"
|
|
133
|
+
* - Throws: If the PEM format is unrecognized or base64 decoding fails
|
|
134
|
+
* - Returns: The private key re-encoded in the target format
|
|
135
|
+
*/
|
|
136
|
+
@objc public func convertPrivateKey(pem: String, targetFormat: String) throws -> String {
|
|
137
|
+
if pem.contains("BEGIN RSA PRIVATE KEY") && targetFormat == "pkcs8" {
|
|
138
|
+
// PKCS#1 → PKCS#8: wrap the raw bytes in a PKCS#8 structure
|
|
139
|
+
let base64 = KeyUtils.extractBase64(from: pem)
|
|
140
|
+
guard let derData = Data(base64Encoded: base64) else {
|
|
141
|
+
throw NSError(domain: "RSAKeyGenerator", code: 10,
|
|
142
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid base64 encoding"])
|
|
143
|
+
}
|
|
144
|
+
let pkcs8Data = ASN1Utils.wrapPkcs1InPkcs8(pkcs1Data: derData)
|
|
145
|
+
return KeyUtils.toPEM(data: pkcs8Data, header: "PRIVATE KEY")
|
|
146
|
+
} else if pem.contains("BEGIN PRIVATE KEY") && targetFormat == "pkcs1" {
|
|
147
|
+
// PKCS#8 → PKCS#1: extract the inner PKCS#1 bytes
|
|
148
|
+
let base64 = KeyUtils.extractBase64(from: pem)
|
|
149
|
+
guard let derData = Data(base64Encoded: base64) else {
|
|
150
|
+
throw NSError(domain: "RSAKeyGenerator", code: 11,
|
|
151
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid base64 encoding"])
|
|
152
|
+
}
|
|
153
|
+
let pkcs1Data = ASN1Utils.extractPkcs1FromPkcs8(pkcs8Data: derData)
|
|
154
|
+
return KeyUtils.toPEM(data: pkcs1Data, header: "RSA PRIVATE KEY")
|
|
155
|
+
} else if (pem.contains("BEGIN RSA PRIVATE KEY") && targetFormat == "pkcs1") ||
|
|
156
|
+
(pem.contains("BEGIN PRIVATE KEY") && targetFormat == "pkcs8") {
|
|
157
|
+
// Already in the target format — return unchanged
|
|
158
|
+
return pem
|
|
159
|
+
} else {
|
|
160
|
+
throw NSError(domain: "RSAKeyGenerator", code: 12,
|
|
161
|
+
userInfo: [NSLocalizedDescriptionKey: "Unrecognized PEM format or unsupported target format"])
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Security
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* RSA signing and verification operations for iOS.
|
|
6
|
+
*
|
|
7
|
+
* Uses the Security framework's `SecKeyCreateSignature` / `SecKeyVerifySignature`.
|
|
8
|
+
* All data crosses the bridge as base64 strings to avoid binary encoding issues.
|
|
9
|
+
*
|
|
10
|
+
* Supported padding modes:
|
|
11
|
+
* - "pss" — PSS (Probabilistic Signature Scheme) with configurable hash
|
|
12
|
+
* - "pkcs1" — PKCS#1 v1.5 deterministic signatures
|
|
13
|
+
*
|
|
14
|
+
* The "Message" algorithm variants are used (e.g. `rsaSignatureMessagePSSSHA256`),
|
|
15
|
+
* meaning the Security framework hashes the input internally — callers pass the raw message.
|
|
16
|
+
*
|
|
17
|
+
* Exposed to Objective-C via `@objc` for the React Native bridge.
|
|
18
|
+
*/
|
|
19
|
+
@objc public class RSASigner: NSObject {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sign data with an RSA private key.
|
|
23
|
+
*
|
|
24
|
+
* - Parameters:
|
|
25
|
+
* - dataBase64: The data to sign, encoded as base64
|
|
26
|
+
* - privateKeyPEM: The private key in PEM format (PKCS#1 or PKCS#8)
|
|
27
|
+
* - padding: "pss" or "pkcs1"
|
|
28
|
+
* - hash: "sha256", "sha1", "sha384", or "sha512"
|
|
29
|
+
* - Throws: If the input is invalid, key loading fails, or signing fails
|
|
30
|
+
* - Returns: The signature encoded as base64
|
|
31
|
+
*/
|
|
32
|
+
@objc public static func sign(dataBase64: String, privateKeyPEM: String, padding: String, hash: String) throws -> String {
|
|
33
|
+
guard let data = Data(base64Encoded: dataBase64) else {
|
|
34
|
+
throw NSError(domain: "RSASigner", code: 1,
|
|
35
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid base64 input data"])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let privateKey = try KeyUtils.loadPrivateKey(pem: privateKeyPEM)
|
|
39
|
+
let algorithm = try Algorithms.signatureAlgorithm(padding: padding, hash: hash)
|
|
40
|
+
|
|
41
|
+
var error: Unmanaged<CFError>?
|
|
42
|
+
guard let signatureData = SecKeyCreateSignature(privateKey, algorithm, data as CFData, &error) as Data? else {
|
|
43
|
+
let errorMessage = error?.takeRetainedValue().localizedDescription ?? "Unknown error"
|
|
44
|
+
throw NSError(domain: "RSASigner", code: 2,
|
|
45
|
+
userInfo: [NSLocalizedDescriptionKey: "Signing failed: \(errorMessage)"])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return signatureData.base64EncodedString()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Verify a signature against data using an RSA public key.
|
|
53
|
+
*
|
|
54
|
+
* - Parameters:
|
|
55
|
+
* - dataBase64: The original data that was signed, encoded as base64
|
|
56
|
+
* - signatureBase64: The signature to verify, encoded as base64
|
|
57
|
+
* - publicKeyPEM: The public key in SPKI PEM format ("BEGIN PUBLIC KEY")
|
|
58
|
+
* - padding: "pss" or "pkcs1" — must match the padding used during signing
|
|
59
|
+
* - hash: "sha256", "sha1", "sha384", or "sha512" — must match signing hash
|
|
60
|
+
* - Throws: If the input is invalid or key loading fails
|
|
61
|
+
* - Returns: true if the signature is valid, false otherwise
|
|
62
|
+
*
|
|
63
|
+
* Note on Swift→Obj-C bridging: A `throws -> Bool` method maps to Obj-C as
|
|
64
|
+
* `(BOOL)...error:(NSError **)`. When the method returns `false` normally
|
|
65
|
+
* (invalid signature), `*error` is nil. When it throws (actual error), `*error`
|
|
66
|
+
* is set. The bridge checks `error != nil` to distinguish these cases.
|
|
67
|
+
*/
|
|
68
|
+
@objc public static func verify(dataBase64: String, signatureBase64: String, publicKeyPEM: String, padding: String, hash: String) throws -> Bool {
|
|
69
|
+
guard let data = Data(base64Encoded: dataBase64) else {
|
|
70
|
+
throw NSError(domain: "RSASigner", code: 3,
|
|
71
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid base64 input data"])
|
|
72
|
+
}
|
|
73
|
+
guard let signatureData = Data(base64Encoded: signatureBase64) else {
|
|
74
|
+
throw NSError(domain: "RSASigner", code: 4,
|
|
75
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid base64 signature"])
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let publicKey = try KeyUtils.loadPublicKey(pem: publicKeyPEM)
|
|
79
|
+
let algorithm = try Algorithms.signatureAlgorithm(padding: padding, hash: hash)
|
|
80
|
+
|
|
81
|
+
// SecKeyVerifySignature returns true for valid signatures, false otherwise.
|
|
82
|
+
// IMPORTANT: On iOS, it also sets *error for invalid signatures (errSecVerifyFailed = -67808).
|
|
83
|
+
// We must distinguish between "invalid signature" (return false) and "actual error"
|
|
84
|
+
// (bad key type, unsupported algorithm, etc.) by checking the error code.
|
|
85
|
+
var error: Unmanaged<CFError>?
|
|
86
|
+
let result = SecKeyVerifySignature(publicKey, algorithm, data as CFData, signatureData as CFData, &error)
|
|
87
|
+
|
|
88
|
+
if !result, let cfError = error?.takeRetainedValue() {
|
|
89
|
+
let nsError = cfError as Error as NSError
|
|
90
|
+
// errSecVerifyFailed (-67808) means "signature doesn't match" — not an actual error.
|
|
91
|
+
// Return false for this case; only throw for unexpected failures.
|
|
92
|
+
if nsError.domain == NSOSStatusErrorDomain && nsError.code == Int(errSecVerifyFailed) {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
throw NSError(domain: "RSASigner", code: 5,
|
|
96
|
+
userInfo: [NSLocalizedDescriptionKey: "Verification error: \(cfError.localizedDescription)"])
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
}
|
package/ios/Rsa.h
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#ifdef __cplusplus
|
|
2
|
+
#import <RsaSpec/RsaSpec.h>
|
|
3
|
+
#endif
|
|
4
|
+
|
|
5
|
+
@interface Rsa : NSObject
|
|
6
|
+
#ifdef __cplusplus
|
|
7
|
+
<NativeRsaSpec>
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
// --- Key Generation ---
|
|
11
|
+
|
|
12
|
+
- (void)generateKeyPair:(double)keySize
|
|
13
|
+
format:(NSString *)format
|
|
14
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
15
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
16
|
+
|
|
17
|
+
- (void)getPublicKeyFromPrivate:(NSString *)privateKeyPEM
|
|
18
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
19
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
20
|
+
|
|
21
|
+
// --- Encrypt / Decrypt ---
|
|
22
|
+
|
|
23
|
+
- (void)encrypt:(NSString *)dataBase64
|
|
24
|
+
publicKeyPEM:(NSString *)publicKeyPEM
|
|
25
|
+
padding:(NSString *)padding
|
|
26
|
+
hash:(NSString *)hash
|
|
27
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
28
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
29
|
+
|
|
30
|
+
- (void)decrypt:(NSString *)dataBase64
|
|
31
|
+
privateKeyPEM:(NSString *)privateKeyPEM
|
|
32
|
+
padding:(NSString *)padding
|
|
33
|
+
hash:(NSString *)hash
|
|
34
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
35
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
36
|
+
|
|
37
|
+
// --- Sign / Verify ---
|
|
38
|
+
|
|
39
|
+
- (void)sign:(NSString *)dataBase64
|
|
40
|
+
privateKeyPEM:(NSString *)privateKeyPEM
|
|
41
|
+
padding:(NSString *)padding
|
|
42
|
+
hash:(NSString *)hash
|
|
43
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
44
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
45
|
+
|
|
46
|
+
- (void)verify:(NSString *)dataBase64
|
|
47
|
+
signatureBase64:(NSString *)signatureBase64
|
|
48
|
+
publicKeyPEM:(NSString *)publicKeyPEM
|
|
49
|
+
padding:(NSString *)padding
|
|
50
|
+
hash:(NSString *)hash
|
|
51
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
52
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
53
|
+
|
|
54
|
+
// --- Key Format Conversion ---
|
|
55
|
+
|
|
56
|
+
- (void)convertPrivateKey:(NSString *)pem
|
|
57
|
+
targetFormat:(NSString *)targetFormat
|
|
58
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
59
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
60
|
+
|
|
61
|
+
@end
|
package/ios/Rsa.mm
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#import "Rsa.h"
|
|
2
|
+
|
|
3
|
+
// Import the auto-generated Swift bridging header
|
|
4
|
+
#if __has_include("react_native_rsa_turbo/react_native_rsa_turbo-Swift.h")
|
|
5
|
+
#import "react_native_rsa_turbo/react_native_rsa_turbo-Swift.h"
|
|
6
|
+
#elif __has_include("react-native-rsa-turbo/react_native_rsa_turbo-Swift.h")
|
|
7
|
+
#import "react-native-rsa-turbo/react_native_rsa_turbo-Swift.h"
|
|
8
|
+
#elif __has_include("Rsa-Swift.h")
|
|
9
|
+
#import "Rsa-Swift.h"
|
|
10
|
+
#else
|
|
11
|
+
// Fallback: forward declare Swift classes used by this file
|
|
12
|
+
@class RSAKeyGenerator;
|
|
13
|
+
@class RSACipher;
|
|
14
|
+
@class RSASigner;
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Objective-C++ bridge between React Native TurboModule and Swift implementations.
|
|
19
|
+
*
|
|
20
|
+
* Each method dispatches to a background queue (QOS_CLASS_USER_INITIATED)
|
|
21
|
+
* and calls the corresponding Swift class method, then resolves/rejects
|
|
22
|
+
* the JS promise based on the result.
|
|
23
|
+
*
|
|
24
|
+
* Dispatches to:
|
|
25
|
+
* - RSAKeyGenerator: key generation, public key extraction, format conversion
|
|
26
|
+
* - RSACipher: encrypt, decrypt
|
|
27
|
+
* - RSASigner: sign, verify
|
|
28
|
+
*/
|
|
29
|
+
@implementation Rsa
|
|
30
|
+
|
|
31
|
+
// MARK: - Key Generation
|
|
32
|
+
|
|
33
|
+
- (void)generateKeyPair:(double)keySize
|
|
34
|
+
format:(NSString *)format
|
|
35
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
36
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
37
|
+
{
|
|
38
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
39
|
+
NSError *error = nil;
|
|
40
|
+
RSAKeyGenerator *generator = [RSAKeyGenerator shared];
|
|
41
|
+
|
|
42
|
+
RSAKeyPairResult *result = [generator generateKeyPairWithKeySize:(int)keySize
|
|
43
|
+
format:format
|
|
44
|
+
error:&error];
|
|
45
|
+
if (error != nil) {
|
|
46
|
+
reject(@"RSAKeyGenerationError", error.localizedDescription, error);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (result == nil) {
|
|
51
|
+
reject(@"RSAKeyGenerationError", @"Unknown error during key generation", nil);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
NSDictionary *map = @{
|
|
56
|
+
@"publicKey": result.publicKey,
|
|
57
|
+
@"privateKey": result.privateKey
|
|
58
|
+
};
|
|
59
|
+
resolve(map);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (void)getPublicKeyFromPrivate:(NSString *)privateKeyPEM
|
|
64
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
65
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
66
|
+
{
|
|
67
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
68
|
+
NSError *error = nil;
|
|
69
|
+
RSAKeyGenerator *generator = [RSAKeyGenerator shared];
|
|
70
|
+
|
|
71
|
+
NSString *publicKeyPEM = [generator getPublicKeyFromPrivateWithPrivateKeyPEM:privateKeyPEM
|
|
72
|
+
error:&error];
|
|
73
|
+
if (error != nil) {
|
|
74
|
+
reject(@"RSAKeyExtractionError", error.localizedDescription, error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
resolve(publicKeyPEM);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// MARK: - Encrypt / Decrypt
|
|
83
|
+
|
|
84
|
+
- (void)encrypt:(NSString *)dataBase64
|
|
85
|
+
publicKeyPEM:(NSString *)publicKeyPEM
|
|
86
|
+
padding:(NSString *)padding
|
|
87
|
+
hash:(NSString *)hash
|
|
88
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
89
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
90
|
+
{
|
|
91
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
92
|
+
NSError *error = nil;
|
|
93
|
+
// RSACipher.encrypt is a static Swift method → class method in Obj-C
|
|
94
|
+
NSString *result = [RSACipher encryptWithDataBase64:dataBase64
|
|
95
|
+
publicKeyPEM:publicKeyPEM
|
|
96
|
+
padding:padding
|
|
97
|
+
hash:hash
|
|
98
|
+
error:&error];
|
|
99
|
+
if (error != nil) {
|
|
100
|
+
reject(@"RSAEncryptError", error.localizedDescription, error);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
resolve(result);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
- (void)decrypt:(NSString *)dataBase64
|
|
108
|
+
privateKeyPEM:(NSString *)privateKeyPEM
|
|
109
|
+
padding:(NSString *)padding
|
|
110
|
+
hash:(NSString *)hash
|
|
111
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
112
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
113
|
+
{
|
|
114
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
115
|
+
NSError *error = nil;
|
|
116
|
+
NSString *result = [RSACipher decryptWithDataBase64:dataBase64
|
|
117
|
+
privateKeyPEM:privateKeyPEM
|
|
118
|
+
padding:padding
|
|
119
|
+
hash:hash
|
|
120
|
+
error:&error];
|
|
121
|
+
if (error != nil) {
|
|
122
|
+
reject(@"RSADecryptError", error.localizedDescription, error);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
resolve(result);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// MARK: - Sign / Verify
|
|
130
|
+
|
|
131
|
+
- (void)sign:(NSString *)dataBase64
|
|
132
|
+
privateKeyPEM:(NSString *)privateKeyPEM
|
|
133
|
+
padding:(NSString *)padding
|
|
134
|
+
hash:(NSString *)hash
|
|
135
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
136
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
137
|
+
{
|
|
138
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
139
|
+
NSError *error = nil;
|
|
140
|
+
NSString *result = [RSASigner signWithDataBase64:dataBase64
|
|
141
|
+
privateKeyPEM:privateKeyPEM
|
|
142
|
+
padding:padding
|
|
143
|
+
hash:hash
|
|
144
|
+
error:&error];
|
|
145
|
+
if (error != nil) {
|
|
146
|
+
reject(@"RSASignError", error.localizedDescription, error);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
resolve(result);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
- (void)verify:(NSString *)dataBase64
|
|
154
|
+
signatureBase64:(NSString *)signatureBase64
|
|
155
|
+
publicKeyPEM:(NSString *)publicKeyPEM
|
|
156
|
+
padding:(NSString *)padding
|
|
157
|
+
hash:(NSString *)hash
|
|
158
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
159
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
160
|
+
{
|
|
161
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
162
|
+
NSError *error = nil;
|
|
163
|
+
// Swift `throws -> Bool` maps to Obj-C `(BOOL)...error:(NSError **)`.
|
|
164
|
+
// - YES + nil error = signature valid
|
|
165
|
+
// - NO + nil error = signature invalid (legitimate false)
|
|
166
|
+
// - NO + error set = actual error occurred
|
|
167
|
+
BOOL result = [RSASigner verifyWithDataBase64:dataBase64
|
|
168
|
+
signatureBase64:signatureBase64
|
|
169
|
+
publicKeyPEM:publicKeyPEM
|
|
170
|
+
padding:padding
|
|
171
|
+
hash:hash
|
|
172
|
+
error:&error];
|
|
173
|
+
if (error != nil) {
|
|
174
|
+
reject(@"RSAVerifyError", error.localizedDescription, error);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
resolve(@(result));
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// MARK: - Key Format Conversion
|
|
182
|
+
|
|
183
|
+
- (void)convertPrivateKey:(NSString *)pem
|
|
184
|
+
targetFormat:(NSString *)targetFormat
|
|
185
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
186
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
187
|
+
{
|
|
188
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
189
|
+
NSError *error = nil;
|
|
190
|
+
RSAKeyGenerator *generator = [RSAKeyGenerator shared];
|
|
191
|
+
|
|
192
|
+
NSString *result = [generator convertPrivateKeyWithPem:pem
|
|
193
|
+
targetFormat:targetFormat
|
|
194
|
+
error:&error];
|
|
195
|
+
if (error != nil) {
|
|
196
|
+
reject(@"RSAConvertKeyError", error.localizedDescription, error);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
resolve(result);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// MARK: - TurboModule Registration
|
|
204
|
+
|
|
205
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
206
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
207
|
+
{
|
|
208
|
+
return std::make_shared<facebook::react::NativeRsaSpecJSI>(params);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
+ (NSString *)moduleName
|
|
212
|
+
{
|
|
213
|
+
return @"Rsa";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Native bridge specification for RSA operations.
|
|
7
|
+
*
|
|
8
|
+
* All methods use flat string parameters (no objects/arrays) because
|
|
9
|
+
* React Native's codegen works best with simple types across the bridge.
|
|
10
|
+
*
|
|
11
|
+
* Data is passed as base64 strings to avoid binary encoding issues.
|
|
12
|
+
* The JS layer (index.ts) handles defaults resolution and UTF-8→base64 encoding.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export default TurboModuleRegistry.getEnforcing('Rsa');
|
|
16
|
+
//# sourceMappingURL=NativeRsa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeRsa.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAgDA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,KAAK,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default values for all RSA operation options.
|
|
5
|
+
*
|
|
6
|
+
* These are applied in `index.ts` when the caller omits an option.
|
|
7
|
+
* Keeping them in one place ensures consistency across encrypt, decrypt, sign, verify.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULTS = {
|
|
10
|
+
/** Default padding for encrypt/decrypt — OAEP is recommended over PKCS#1 */
|
|
11
|
+
ENCRYPTION_PADDING: 'oaep',
|
|
12
|
+
/** Default padding for sign/verify — PSS is recommended over PKCS#1 */
|
|
13
|
+
SIGNATURE_PADDING: 'pss',
|
|
14
|
+
/** Default hash algorithm */
|
|
15
|
+
HASH: 'sha256',
|
|
16
|
+
/** Default input string encoding — UTF-8 text is the common case */
|
|
17
|
+
ENCODING: 'utf8',
|
|
18
|
+
/** Default private key format for key generation */
|
|
19
|
+
KEY_FORMAT: 'pkcs1'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Supported RSA key sizes in bits */
|
|
23
|
+
export const VALID_KEY_SIZES = [1024, 2048, 4096];
|
|
24
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["DEFAULTS","ENCRYPTION_PADDING","SIGNATURE_PADDING","HASH","ENCODING","KEY_FORMAT","VALID_KEY_SIZES"],"sourceRoot":"../../src","sources":["constants.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,QAAQ,GAAG;EACtB;EACAC,kBAAkB,EAAE,MAAe;EAEnC;EACAC,iBAAiB,EAAE,KAAc;EAEjC;EACAC,IAAI,EAAE,QAAiB;EAEvB;EACAC,QAAQ,EAAE,MAAe;EAEzB;EACAC,UAAU,EAAE;AACd,CAAC;;AAED;AACA,OAAO,MAAMC,eAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU","ignoreList":[]}
|