Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions android/src/main/java/com/encryption/EncryptionModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)
Expand All @@ -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))
Expand All @@ -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) {
Expand Down
22 changes: 11 additions & 11 deletions ios/Encryption.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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), ^{
Expand All @@ -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);
Expand All @@ -345,15 +345,15 @@ - (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"
userInfo:nil];
}

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"
Expand All @@ -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;
Expand All @@ -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);
Expand Down
34 changes: 21 additions & 13 deletions ios/EncryptionCryptokitIml.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<CFError>?
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(
Expand Down Expand Up @@ -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<CFError>?
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(
Expand Down
19 changes: 15 additions & 4 deletions src/NativeEncryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<string>;
decryptAsyncRSA(data: string, privateKey: string): Promise<string>;
encryptRSA(data: string, publicKey: string, padding: string): string;
decryptRSA(data: string, privateKey: string, padding: string): string;
encryptAsyncRSA(
data: string,
publicKey: string,
padding: string
): Promise<string>;
decryptAsyncRSA(
data: string,
privateKey: string,
padding: string
): Promise<string>;

hashSHA256(input: string): string;
hashSHA512(input: string): string;
Expand Down
3 changes: 3 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
36 changes: 27 additions & 9 deletions src/native/index.tsx
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -27,17 +29,33 @@ export function decryptAsyncAES(data: string, key: string): Promise<string> {
return Encryption.decryptAsyncAES(data, key);
}

export function encryptAsyncRSA(data: string, key: string): Promise<string> {
return Encryption.encryptAsyncRSA(data, key);
export function encryptAsyncRSA(
data: string,
key: string,
padding: RSAPadding = 'PKCS1'
): Promise<string> {
return Encryption.encryptAsyncRSA(data, key, padding);
}
export function decryptAsyncRSA(data: string, key: string): Promise<string> {
return Encryption.decryptAsyncRSA(data, key);
export function decryptAsyncRSA(
data: string,
key: string,
padding: RSAPadding = 'PKCS1'
): Promise<string> {
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);
Expand Down