Skip to content

Commit f09c9d2

Browse files
committed
encrypt/decrypt file method also added
1 parent c0d53ff commit f09c9d2

File tree

13 files changed

+879
-356
lines changed

13 files changed

+879
-356
lines changed

README.md

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ Each method can be accessed directly without a default object wrapper.
130130
- **`decryptAsyncAES(data: string, key: string): Promise<string>`**
131131
- **`encryptAsyncRSA(data: string, key: string): Promise<string>`**
132132
- **`decryptAsyncRSA(data: string, key: string): Promise<string>`**
133+
- **`encryptFile(inputPath: string,outputPath: string, key: string): Promise<string>`**
134+
- **`decryptFile(inputPath: string, key: string): Promise<string>`**
133135
---
134136

135137
## 🛠️ **6. Usage Examples**
@@ -156,29 +158,31 @@ import {
156158
encryptAsyncAES,
157159
decryptAsyncAES,
158160
encryptAsyncRSA,
159-
decryptAsyncRSA
161+
decryptAsyncRSA,
162+
encryptFile,
163+
decryptFile
160164
} from 'rn-encryption';
165+
import RNFS from 'react-native-fs';
166+
161167
interface EncryptionError {
162168
name: string;
163169
message: string;
164170
}
165171
export default function DashboardScreen() {
166172
const [result, setResult] = useState(''); // Encryption/Decryption result
167173

168-
function handleRSAEncryption() {
174+
const inputPath = `${RNFS.DocumentDirectoryPath}/data.txt`;
175+
const outputPath = `${RNFS.DocumentDirectoryPath}/data.enc`;
176+
const decryptedPath = `${RNFS.DocumentDirectoryPath}/data-decrypted.txt`;
177+
178+
function handleRSAEncryption() {
169179
const plaintext = 'Hello, RSA Encryption!';
170180
const generatedKeys = generateRSAKeyPair();
171181
try {
172182
// Step 1: Encrypt the plaintext using the Public Key
173-
const encryptedData = encryptRSA(
174-
plaintext,
175-
generatedKeys.publicKey
176-
);
183+
const encryptedData = encryptRSA(plaintext, generatedKeys.publicKey);
177184
// Step 2: Decrypt the encrypted data using the Private Key
178-
const decryptedData = decryptRSA(
179-
encryptedData,
180-
generatedKeys.privateKey
181-
);
185+
const decryptedData = decryptRSA(encryptedData, generatedKeys.privateKey);
182186
// Step 3: Validation
183187
if (decryptedData === plaintext) {
184188
console.log('✅ RSA Encryption and Decryption Successful!');
@@ -254,7 +258,10 @@ export default function DashboardScreen() {
254258
console.log('encrypted Object:', encryptedString);
255259

256260
// Decrypt and parse JSON
257-
const decryptedJsonString = await decryptAsyncAES(encryptedString, generatedKey);
261+
const decryptedJsonString = await decryptAsyncAES(
262+
encryptedString,
263+
generatedKey
264+
);
258265
const decryptedObject = JSON.parse(decryptedJsonString);
259266
console.log('Decrypted Object:', decryptedObject);
260267
} catch (err: unknown) {
@@ -284,8 +291,8 @@ export default function DashboardScreen() {
284291
const hmac = () => {
285292
try {
286293
console.log('--- HMAC ---');
287-
const hmac = hmacSHA256('Hello HMAC', 'MyHMACKey');
288-
console.log('HMAC-SHA256:', hmac);
294+
const hmachash = hmacSHA256('Hello HMAC', 'MyHMACKey');
295+
console.log('HMAC-SHA256:', hmachash);
289296
} catch (err) {
290297
console.log('error is', err);
291298
}
@@ -301,7 +308,7 @@ export default function DashboardScreen() {
301308
console.log('Is Valid Signature:', isValid);
302309
};
303310

304-
const base64 = ()=> {
311+
const base64 = () => {
305312
try {
306313
console.log('--- Base64 Encoding/Decoding ---');
307314
const base64Encoded = base64Encode('Hello Base64 Encoding');
@@ -324,15 +331,54 @@ export default function DashboardScreen() {
324331
}
325332
};
326333

334+
async function handleEncryptFileAES() {
335+
try {
336+
// Step 1: Write Sample Data to a File
337+
await RNFS.writeFile(inputPath, 'This is a sensitive file content.', 'utf8');
338+
console.log(`File written at: ${inputPath}`);
339+
340+
const generatedKey = generateAESKey(256);
341+
console.log('generatedKey ', generatedKey);
342+
343+
344+
// Step 2: Encrypt the File
345+
const encryptedFilePath = await encryptFile(inputPath, outputPath, generatedKey);
346+
console.log('Encrypted File Path:', encryptedFilePath);
347+
348+
// Step 3: Verify Encrypted File
349+
const encryptedFileExists = await RNFS.exists(outputPath);
350+
console.log('Encrypted File Exists:', encryptedFileExists);
351+
352+
const decryptedContent = await decryptFile(outputPath, generatedKey);
353+
console.log('Decrypted File Content:', decryptedContent);
354+
355+
// Step 5: Write Decrypted Content to a New File
356+
await RNFS.writeFile(decryptedPath, decryptedContent, 'utf8');
357+
console.log(`Decrypted file saved at: ${decryptedPath}`);
358+
} catch (error) {
359+
console.error('Encryption Error:', error);
360+
}
361+
}
362+
363+
327364
return (
328365
<View style={{ flex: 1, alignItems: 'center', paddingTop: 80 }}>
329366
<Button title="Encrypt & Decrypt AES" onPress={handleAESEncryption} />
330-
<Button title="Async Encrypt & Decrypt AES" onPress={handleAsyncESEncryption} />
367+
<Button
368+
title="Async Encrypt & Decrypt AES"
369+
onPress={handleAsyncESEncryption}
370+
/>
331371

372+
<Button
373+
title="Encrypt & Decrypt File"
374+
onPress={handleEncryptFileAES}
375+
/>
332376

333377
<Button title="Encrypt & Decrypt RSA" onPress={handleRSAEncryption} />
334-
<Button title="Encrypt & Decrypt RSA" onPress={handleAsyncRSAEncryption} />
335-
378+
<Button
379+
title="Encrypt & Decrypt RSA"
380+
onPress={handleAsyncRSAEncryption}
381+
/>
336382

337383
<Button title="Hashing" onPress={hashing} />
338384

@@ -382,6 +428,7 @@ const styles = StyleSheet.create({
382428
fontSize: 16,
383429
},
384430
});
431+
385432
```
386433

387434
---
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// AESCryptoUtils.kt
2+
import android.util.Base64
3+
import java.nio.charset.Charset
4+
import java.security.SecureRandom
5+
import javax.crypto.Cipher
6+
import javax.crypto.spec.GCMParameterSpec
7+
import javax.crypto.spec.SecretKeySpec
8+
import javax.crypto.KeyGenerator
9+
import javax.crypto.SecretKey
10+
11+
object AESCryptoUtils {
12+
13+
private const val AES_MODE = "AES/GCM/NoPadding"
14+
private const val IV_SIZE = 12
15+
private const val TAG_LENGTH = 128
16+
17+
/**
18+
* Generates an AES encryption key of specified size.
19+
*
20+
* @param keySize The size of the AES key in bits (128, 192, or 256).
21+
* @return A Base64-encoded AES key.
22+
* @throws IllegalArgumentException if the key size is invalid.
23+
* @throws Exception if key generation fails.
24+
*/
25+
@Throws(IllegalArgumentException::class)
26+
fun generateAESKey(keySize: Double): String {
27+
val validKeySizes = setOf(128, 192, 256)
28+
if (keySize.toInt() !in validKeySizes) {
29+
throw IllegalArgumentException("Invalid AES key size. Must be 128, 192, or 256 bits.")
30+
}
31+
32+
return try {
33+
val keyGenerator = KeyGenerator.getInstance("AES")
34+
keyGenerator.init(keySize.toInt())
35+
val secretKey: SecretKey = keyGenerator.generateKey()
36+
val keyBytes = secretKey.encoded
37+
Base64.encodeToString(keyBytes, Base64.DEFAULT)
38+
} catch (e: Exception) {
39+
throw IllegalArgumentException("Failed to generate AES key: ${e.localizedMessage}", e)
40+
}
41+
}
42+
43+
/**
44+
* Encrypts the given plaintext using AES-GCM.
45+
*
46+
* @param data Plaintext string to encrypt.
47+
* @param key AES key in Base64 format.
48+
* @return Encrypted string in Base64 format with IV prepended.
49+
*/
50+
@Throws(IllegalArgumentException::class, Exception::class)
51+
fun encrypt(data: String, key: String): String {
52+
if (data.isEmpty() || key.isEmpty()) {
53+
throw IllegalArgumentException("Data or key cannot be empty.")
54+
}
55+
56+
val keyBytes =
57+
try {
58+
Base64.decode(key, Base64.DEFAULT)
59+
} catch (e: Exception) {
60+
throw IllegalArgumentException("Invalid AES key format: ${e.localizedMessage}")
61+
}
62+
if (keyBytes.size !in listOf(16, 24, 32)) {
63+
throw IllegalArgumentException("Invalid AES key size. Must be 16, 24, or 32 bytes (128, 192, or 256 bits).")
64+
}
65+
66+
// Generate Initialization Vector (IV)
67+
val iv = ByteArray(IV_SIZE)
68+
SecureRandom().nextBytes(iv)
69+
val ivSpec = GCMParameterSpec(TAG_LENGTH, iv)
70+
71+
// Initialize Cipher
72+
val secretKey = SecretKeySpec(keyBytes, "AES")
73+
val cipher = Cipher.getInstance(AES_MODE)
74+
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec)
75+
76+
// Encrypt data
77+
val encryptedData = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
78+
79+
// Combine IV and Encrypted Data
80+
val combined = iv + encryptedData
81+
82+
// Encode as Base64
83+
return Base64.encodeToString(combined, Base64.DEFAULT)
84+
}
85+
86+
/**
87+
* Decrypts the given AES-GCM encrypted string.
88+
*
89+
* @param data Encrypted data in Base64 format (with IV prepended).
90+
* @param key AES key in Base64 format.
91+
* @return Decrypted plaintext string.
92+
*/
93+
@Throws(IllegalArgumentException::class, Exception::class)
94+
fun decrypt(data: String, key: String): String {
95+
if (data.isEmpty() || key.isEmpty()) {
96+
throw IllegalArgumentException("Data or key cannot be empty.")
97+
}
98+
99+
val keyBytes = Base64.decode(key, Base64.DEFAULT)
100+
if (keyBytes.size !in listOf(16, 24, 32)) {
101+
throw IllegalArgumentException("Invalid AES key size. Must be 16, 24, or 32 bytes (128, 192, or 256 bits).")
102+
}
103+
104+
val decodedData = Base64.decode(data, Base64.DEFAULT)
105+
if (decodedData.size < IV_SIZE) {
106+
throw IllegalArgumentException("Invalid encrypted data. Data too short to contain IV.")
107+
}
108+
109+
// Extract IV and Encrypted Data
110+
val iv = decodedData.copyOfRange(0, IV_SIZE)
111+
val encryptedBytes = decodedData.copyOfRange(IV_SIZE, decodedData.size)
112+
val ivSpec = GCMParameterSpec(TAG_LENGTH, iv)
113+
114+
// Initialize Cipher
115+
val secretKey = SecretKeySpec(keyBytes, "AES")
116+
val cipher = Cipher.getInstance(AES_MODE)
117+
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
118+
119+
// Decrypt data
120+
val decryptedData = cipher.doFinal(encryptedBytes)
121+
return String(decryptedData, Charsets.UTF_8)
122+
}
123+
124+
/**
125+
* Encrypt ByteArray directly (e.g., for file encryption).
126+
*/
127+
@Throws(Exception::class)
128+
fun encryptBytes(data: ByteArray, key: String): ByteArray {
129+
val keyBytes = Base64.decode(key, Base64.DEFAULT)
130+
val secretKey = SecretKeySpec(keyBytes, "AES")
131+
val iv = ByteArray(12)
132+
SecureRandom().nextBytes(iv)
133+
134+
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
135+
cipher.init(Cipher.ENCRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
136+
137+
val encryptedData = cipher.doFinal(data)
138+
return iv + encryptedData
139+
}
140+
141+
/**
142+
* Decrypt ByteArray directly (e.g., for file decryption).
143+
*/
144+
@Throws(Exception::class)
145+
fun decryptBytes(data: ByteArray, key: String): ByteArray {
146+
val keyBytes = Base64.decode(key, Base64.DEFAULT)
147+
val secretKey = SecretKeySpec(keyBytes, "AES")
148+
149+
val iv = data.copyOfRange(0, 12)
150+
val encryptedData = data.copyOfRange(12, data.size)
151+
152+
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
153+
cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
154+
155+
return cipher.doFinal(encryptedData)
156+
}
157+
}

0 commit comments

Comments
 (0)