diff --git a/android/src/main/java/com/encryption/EncryptionModule.kt b/android/src/main/java/com/encryption/EncryptionModule.kt index 0fa3d44..5c5f6d3 100644 --- a/android/src/main/java/com/encryption/EncryptionModule.kt +++ b/android/src/main/java/com/encryption/EncryptionModule.kt @@ -301,18 +301,23 @@ override fun getPublicRSAkey(privateKeyBase64: String): String { * * @param data The plaintext to be encrypted. * @param publicKeyBase64 The Base64-encoded RSA public key. + * @param padding The padding scheme to use: "PKCS1" or "OAEP". * @return A Base64-encoded string with encrypted data. * @throws IllegalArgumentException if data or key is invalid. * @throws Exception if encryption fails. */ @Throws(Exception::class) - override fun encryptRSA(data: String, publicKeyBase64: String): String ? { + override fun encryptRSA(data: String, publicKeyBase64: String, padding: String): String ? { return try { val keyFactory = KeyFactory.getInstance("RSA") val publicKeyBytes = Base64.decode(publicKeyBase64, Base64.DEFAULT) val publicKey = keyFactory.generatePublic(X509EncodedKeySpec(publicKeyBytes)) - val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding") + val cipherTransformation = when (padding) { + "OAEP" -> "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" + else -> "RSA/ECB/PKCS1Padding" + } + val cipher = Cipher.getInstance(cipherTransformation) cipher.init(Cipher.ENCRYPT_MODE, publicKey) val encryptedData = cipher.doFinal(data.toByteArray(Charsets.UTF_8)) @@ -328,18 +333,23 @@ override fun getPublicRSAkey(privateKeyBase64: String): String { * * @param data The Base64-encoded encrypted data (including IV). * @param privateKeyBase64 The Base64-encoded RSA private key. + * @param padding The padding scheme to use: "PKCS1" or "OAEP". * @return The decrypted plaintext string. * @throws IllegalArgumentException if data or key is invalid. * @throws Exception if decryption fails. */ @Throws(Exception::class) - override fun decryptRSA(data: String, privateKeyBase64: String): String ? { + override fun decryptRSA(data: String, privateKeyBase64: String, padding: String): String ? { return try { val keyFactory = KeyFactory.getInstance("RSA") val privateKeyBytes = Base64.decode(privateKeyBase64, Base64.DEFAULT) val privateKey = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes)) - val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding") + val cipherTransformation = when (padding) { + "OAEP" -> "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" + else -> "RSA/ECB/PKCS1Padding" + } + val cipher = Cipher.getInstance(cipherTransformation) cipher.init(Cipher.DECRYPT_MODE, privateKey) val encryptedData = Base64.decode(data, Base64.DEFAULT) @@ -355,19 +365,24 @@ override fun getPublicRSAkey(privateKeyBase64: String): String { * * @param data The plaintext to be encrypted. * @param publicKeyBase64 The Base64-encoded RSA public key. + * @param padding The padding scheme to use: "PKCS1" or "OAEP". * @return A Base64-encoded string with encrypted data. * @throws IllegalArgumentException if data or key is invalid. * @throws Exception if encryption fails. */ @Throws(Exception::class) - override fun encryptAsyncRSA(data: String, publicKeyBase64: String,promise: Promise) { + override fun encryptAsyncRSA(data: String, publicKeyBase64: String, padding: String, promise: Promise) { coroutineScope.launch { try { val keyFactory = KeyFactory.getInstance("RSA") val publicKeyBytes = Base64.decode(publicKeyBase64, Base64.DEFAULT) val publicKey = keyFactory.generatePublic(X509EncodedKeySpec(publicKeyBytes)) - val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding") + val cipherTransformation = when (padding) { + "OAEP" -> "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" + else -> "RSA/ECB/PKCS1Padding" + } + val cipher = Cipher.getInstance(cipherTransformation) cipher.init(Cipher.ENCRYPT_MODE, publicKey) val encryptedData = cipher.doFinal(data.toByteArray(Charsets.UTF_8)) @@ -383,23 +398,28 @@ override fun getPublicRSAkey(privateKeyBase64: String): String { * * @param data The Base64-encoded encrypted data (including IV). * @param privateKeyBase64 The Base64-encoded RSA private key. + * @param padding The padding scheme to use: "PKCS1" or "OAEP". * @return The decrypted plaintext string. * @throws IllegalArgumentException if data or key is invalid. * @throws Exception if decryption fails. */ @Throws(Exception::class) - override fun decryptAsyncRSA(data: String, privateKeyBase64: String,promise: Promise) { + override fun decryptAsyncRSA(data: String, privateKeyBase64: String, padding: String, promise: Promise) { coroutineScope.launch { try { val keyFactory = KeyFactory.getInstance("RSA") val privateKeyBytes = Base64.decode(privateKeyBase64, Base64.DEFAULT) val privateKey = keyFactory.generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes)) - val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding") + val cipherTransformation = when (padding) { + "OAEP" -> "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" + else -> "RSA/ECB/PKCS1Padding" + } + val cipher = Cipher.getInstance(cipherTransformation) cipher.init(Cipher.DECRYPT_MODE, privateKey) val encryptedData = Base64.decode(data, Base64.DEFAULT) - + promise.resolve(String(cipher.doFinal(encryptedData), Charsets.UTF_8)) } catch (e: Exception) { diff --git a/ios/Encryption.mm b/ios/Encryption.mm index 490c223..2b77d56 100644 --- a/ios/Encryption.mm +++ b/ios/Encryption.mm @@ -303,10 +303,10 @@ - (NSString *)getPublicRSAkey:(NSString *)privateRSAkey { } } -- (NSString *)encryptRSA:(NSString *)data publicKey:(NSString *)publicKey { +- (NSString *)encryptRSA:(NSString *)data publicKey:(NSString *)publicKey padding:(NSString *)padding { NSError *error = nil; - NSString *encryptedString = [cryptoUtil encryptRSA:data publicKeyBase64:publicKey errorObj:&error]; - + NSString *encryptedString = [cryptoUtil encryptRSA:data publicKeyBase64:publicKey padding:padding errorObj:&error]; + if (error) { @throw [NSException exceptionWithName:@"EncryptionError" reason:error.localizedDescription @@ -316,9 +316,9 @@ - (NSString *)encryptRSA:(NSString *)data publicKey:(NSString *)publicKey { } } -- (void)encryptAsyncRSA:(NSString *)data publicKey:(NSString *)publicKey resolve:(RCTPromiseResolveBlock)resolve +- (void)encryptAsyncRSA:(NSString *)data publicKey:(NSString *)publicKey padding:(NSString *)padding resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - + __typeof(self) __weak weakSelf = self; // Run on a background thread to ensure it doesn't block the UI dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @@ -327,10 +327,10 @@ - (void)encryptAsyncRSA:(NSString *)data publicKey:(NSString *)publicKey resolve reject(@"ENCRYPTION_ERROR", @"Encryption failed: self was deallocated", nil); return; } - + @try { NSError *error = nil; - NSString *encryptedString = [strongSelf->cryptoUtil encryptRSA:data publicKeyBase64:publicKey errorObj:&error]; + NSString *encryptedString = [strongSelf->cryptoUtil encryptRSA:data publicKeyBase64:publicKey padding:padding errorObj:&error]; if (error) { reject(@"ENCRYPTION_ERROR", error.localizedDescription, nil); @@ -345,7 +345,7 @@ - (void)encryptAsyncRSA:(NSString *)data publicKey:(NSString *)publicKey resolve }); } -- (NSString *)decryptRSA:(NSString *)data privateKey:(NSString *)privateKey { +- (NSString *)decryptRSA:(NSString *)data privateKey:(NSString *)privateKey padding:(NSString *)padding { if (!data || !privateKey) { @throw [NSException exceptionWithName:@"DecryptionError" reason:@"Invalid encrypted data or private key" @@ -353,7 +353,7 @@ - (NSString *)decryptRSA:(NSString *)data privateKey:(NSString *)privateKey { } NSError *error = nil; - NSString *encryptedString = [cryptoUtil decryptRSA:data privateKeyBase64:privateKey errorObj:&error]; + NSString *encryptedString = [cryptoUtil decryptRSA:data privateKeyBase64:privateKey padding:padding errorObj:&error]; if (error) { @throw [NSException exceptionWithName:@"EncryptionError" @@ -364,7 +364,7 @@ - (NSString *)decryptRSA:(NSString *)data privateKey:(NSString *)privateKey { } } -- (void)decryptAsyncRSA:(NSString *)data privateKey:(NSString *)privateKey resolve:(RCTPromiseResolveBlock)resolve +- (void)decryptAsyncRSA:(NSString *)data privateKey:(NSString *)privateKey padding:(NSString *)padding resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { __typeof(self) __weak weakSelf = self; @@ -378,7 +378,7 @@ - (void)decryptAsyncRSA:(NSString *)data privateKey:(NSString *)privateKey resol @try { NSError *error = nil; - NSString *encryptedString = [strongSelf->cryptoUtil decryptRSA:data privateKeyBase64:privateKey errorObj:&error]; + NSString *encryptedString = [strongSelf->cryptoUtil decryptRSA:data privateKeyBase64:privateKey padding:padding errorObj:&error]; if (error) { reject(@"ENCRYPTION_ERROR", error.localizedDescription, nil); diff --git a/ios/EncryptionCryptokitIml.swift b/ios/EncryptionCryptokitIml.swift index f681f81..3d8ed93 100644 --- a/ios/EncryptionCryptokitIml.swift +++ b/ios/EncryptionCryptokitIml.swift @@ -325,34 +325,38 @@ public class CryptoUtility: NSObject { // MARK: - RSA Encryption - + /// Encrypts a string using RSA with a Base64 public key. /// - Parameters: /// - data: Plain text string to be encrypted. /// - publicKeyBase64: Base64-encoded RSA public key. + /// - padding: The padding scheme to use: "PKCS1" or "OAEP". /// - errorObj: NSErrorPointer for capturing errors. /// - Returns: Base64-encoded encrypted string or nil on failure. - @objc public func encryptRSA(_ data: String, publicKeyBase64: String, errorObj: NSErrorPointer) -> String? { + @objc public func encryptRSA(_ data: String, publicKeyBase64: String, padding: String, errorObj: NSErrorPointer) -> String? { do { // Create Public Key from Base64 String let publicKey = try constructSecKey(from: publicKeyBase64, isPublicKey: true) - + // Validate Data guard let dataToEncrypt = data.data(using: .utf8) else { throw EncryptionError.invalidData } - + + // Select algorithm based on padding + let algorithm: SecKeyAlgorithm = padding == "OAEP" ? .rsaEncryptionOAEPSHA256 : .rsaEncryptionPKCS1 + // Encrypt Data using RSA var error: Unmanaged? guard let encryptedData = SecKeyCreateEncryptedData( publicKey, - .rsaEncryptionPKCS1, + algorithm, dataToEncrypt as CFData, &error ) as Data? else { throw error?.takeRetainedValue() ?? EncryptionError.encryptionFailed } - + return encryptedData.base64EncodedString() } catch let encryptionError as EncryptionError { errorObj?.pointee = NSError( @@ -493,39 +497,43 @@ public class CryptoUtility: NSObject { } // MARK: - RSA Decryption - + /// Decrypts a Base64-encoded string using RSA with a Base64 private key. /// - Parameters: /// - data: Base64-encoded encrypted string. /// - privateKeyBase64: Base64-encoded RSA private key. + /// - padding: The padding scheme to use: "PKCS1" or "OAEP". /// - errorObj: NSErrorPointer for capturing errors. /// - Returns: Decrypted plain text string or nil on failure. - @objc public func decryptRSA(_ data: String, privateKeyBase64: String, errorObj: NSErrorPointer) -> String? { + @objc public func decryptRSA(_ data: String, privateKeyBase64: String, padding: String, errorObj: NSErrorPointer) -> String? { do { // Create Private Key from Base64 String let privateKey = try constructSecKey(from: privateKeyBase64, isPublicKey: false) - + // Validate Base64 Data guard let encryptedData = Data(base64Encoded: data) else { throw EncryptionError.invalidBase64 } - + + // Select algorithm based on padding + let algorithm: SecKeyAlgorithm = padding == "OAEP" ? .rsaEncryptionOAEPSHA256 : .rsaEncryptionPKCS1 + // Decrypt Data using RSA var error: Unmanaged? guard let decryptedData = SecKeyCreateDecryptedData( privateKey, - .rsaEncryptionPKCS1, + algorithm, encryptedData as CFData, &error ) as Data? else { throw error?.takeRetainedValue() ?? EncryptionError.decryptionFailed } - + // Convert Decrypted Data to String guard let decryptedString = String(data: decryptedData, encoding: .utf8) else { throw EncryptionError.decryptionFailed } - + return decryptedString } catch let decryptionError as EncryptionError { errorObj?.pointee = NSError( diff --git a/src/NativeEncryption.ts b/src/NativeEncryption.ts index 8462945..1b99644 100644 --- a/src/NativeEncryption.ts +++ b/src/NativeEncryption.ts @@ -4,6 +4,9 @@ export interface keypair { publicKey: string; privateKey: string; } + +export type RSAPadding = 'PKCS1' | 'OAEP'; + export interface Spec extends TurboModule { generateAESKey(keySize: number): string; encryptAES(data: string, key: string): string; @@ -19,10 +22,18 @@ export interface Spec extends TurboModule { generateRSAKeyPair(): keypair; getPublicRSAkey(privateRSAkey: string): string; - encryptRSA(data: string, publicKey: string): string; - decryptRSA(data: string, privateKey: string): string; - encryptAsyncRSA(data: string, publicKey: string): Promise; - decryptAsyncRSA(data: string, privateKey: string): Promise; + encryptRSA(data: string, publicKey: string, padding: string): string; + decryptRSA(data: string, privateKey: string, padding: string): string; + encryptAsyncRSA( + data: string, + publicKey: string, + padding: string + ): Promise; + decryptAsyncRSA( + data: string, + privateKey: string, + padding: string + ): Promise; hashSHA256(input: string): string; hashSHA512(input: string): string; diff --git a/src/index.tsx b/src/index.tsx index bc53cd7..42d70fe 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,6 +10,9 @@ if (Platform.OS === 'web') { Encryption = require('./native/index'); } +// Export RSAPadding type +export type { RSAPadding } from './NativeEncryption'; + // Export all encryption methods export const { generateAESKey, diff --git a/src/native/index.tsx b/src/native/index.tsx index 1aef068..7e2f6cf 100644 --- a/src/native/index.tsx +++ b/src/native/index.tsx @@ -1,4 +1,6 @@ -import Encryption, { type keypair } from '../NativeEncryption'; +import Encryption, { type keypair, type RSAPadding } from '../NativeEncryption'; + +export type { RSAPadding }; export function generateAESKey(input: number): string { return Encryption.generateAESKey(input); @@ -27,17 +29,33 @@ export function decryptAsyncAES(data: string, key: string): Promise { return Encryption.decryptAsyncAES(data, key); } -export function encryptAsyncRSA(data: string, key: string): Promise { - return Encryption.encryptAsyncRSA(data, key); +export function encryptAsyncRSA( + data: string, + key: string, + padding: RSAPadding = 'PKCS1' +): Promise { + return Encryption.encryptAsyncRSA(data, key, padding); } -export function decryptAsyncRSA(data: string, key: string): Promise { - return Encryption.decryptAsyncRSA(data, key); +export function decryptAsyncRSA( + data: string, + key: string, + padding: RSAPadding = 'PKCS1' +): Promise { + return Encryption.decryptAsyncRSA(data, key, padding); } -export function encryptRSA(data: string, key: string): string { - return Encryption.encryptRSA(data, key); +export function encryptRSA( + data: string, + key: string, + padding: RSAPadding = 'PKCS1' +): string { + return Encryption.encryptRSA(data, key, padding); } -export function decryptRSA(data: string, key: string): string { - return Encryption.decryptRSA(data, key); +export function decryptRSA( + data: string, + key: string, + padding: RSAPadding = 'PKCS1' +): string { + return Encryption.decryptRSA(data, key, padding); } export function generateHMACKey(keySize: number): string { return Encryption.generateHMACKey(keySize);