1+ package org.aria.danesh.logmanagingkotlinlib
2+
3+ import org.slf4j.LoggerFactory
4+ import java.security.SecureRandom
5+ import javax.crypto.Cipher
6+ import javax.crypto.spec.IvParameterSpec
7+ import javax.crypto.spec.SecretKeySpec
8+ import java.util.Base64
9+ import java.security.MessageDigest
10+
11+ /* *
12+ * Utility class for handling encryption operations.
13+ */
14+ object EncryptionUtils {
15+ private val logger = LoggerFactory .getLogger(EncryptionUtils ::class .java)
16+ private const val ALGORITHM = " AES/CFB/NoPadding"
17+ private const val KEY_ALGORITHM = " AES"
18+ private const val SALT_LENGTH = 16
19+ private const val IV_LENGTH = 16
20+
21+ /* *
22+ * Encrypts the given data using AES-CFB encryption with a derived key.
23+ * @param data The data to encrypt
24+ * @param key The encryption key
25+ * @return Base64 encoded encrypted data
26+ */
27+ fun encrypt (data : String , key : String ): String {
28+ try {
29+ // Generate random salt
30+ val salt = generateRandomBytes(SALT_LENGTH )
31+
32+ // Generate random IV
33+ val iv = generateRandomBytes(IV_LENGTH )
34+
35+ // Derive key from password and salt
36+ val derivedKey = deriveKey(key, salt)
37+
38+ // Create cipher instance
39+ val cipher = Cipher .getInstance(ALGORITHM )
40+ val keySpec = SecretKeySpec (derivedKey, KEY_ALGORITHM )
41+ val ivSpec = IvParameterSpec (iv)
42+
43+ // Initialize cipher for encryption
44+ cipher.init (Cipher .ENCRYPT_MODE , keySpec, ivSpec)
45+
46+ // Encrypt the data
47+ val encryptedData = cipher.doFinal(data.toByteArray())
48+
49+ // Combine salt, IV, and encrypted data
50+ val combined = ByteArray (salt.size + iv.size + encryptedData.size)
51+ System .arraycopy(salt, 0 , combined, 0 , salt.size)
52+ System .arraycopy(iv, 0 , combined, salt.size, iv.size)
53+ System .arraycopy(encryptedData, 0 , combined, salt.size + iv.size, encryptedData.size)
54+
55+ // Encode as Base64
56+ return Base64 .getEncoder().encodeToString(combined)
57+ } catch (e: Exception ) {
58+ logger.error(" Encryption failed" , e)
59+ throw LogManagingException (" Encryption failed: ${e.message} " , e)
60+ }
61+ }
62+
63+ /* *
64+ * Decrypts the given encrypted data using AES-CFB decryption with a derived key.
65+ * @param encryptedData The Base64 encoded encrypted data
66+ * @param key The encryption key
67+ * @return Decrypted data
68+ */
69+ fun decrypt (encryptedData : String , key : String ): String {
70+ try {
71+ // Decode Base64
72+ val combined = Base64 .getDecoder().decode(encryptedData)
73+
74+ // Extract salt, IV, and encrypted data
75+ val salt = combined.copyOfRange(0 , SALT_LENGTH )
76+ val iv = combined.copyOfRange(SALT_LENGTH , SALT_LENGTH + IV_LENGTH )
77+ val data = combined.copyOfRange(SALT_LENGTH + IV_LENGTH , combined.size)
78+
79+ // Derive key from password and salt
80+ val derivedKey = deriveKey(key, salt)
81+
82+ // Create cipher instance
83+ val cipher = Cipher .getInstance(ALGORITHM )
84+ val keySpec = SecretKeySpec (derivedKey, KEY_ALGORITHM )
85+ val ivSpec = IvParameterSpec (iv)
86+
87+ // Initialize cipher for decryption
88+ cipher.init (Cipher .DECRYPT_MODE , keySpec, ivSpec)
89+
90+ // Decrypt the data
91+ val decryptedData = cipher.doFinal(data)
92+
93+ return String (decryptedData)
94+ } catch (e: Exception ) {
95+ logger.error(" Decryption failed" , e)
96+ throw LogManagingException (" Decryption failed: ${e.message} " , e)
97+ }
98+ }
99+
100+ /* *
101+ * Generates random bytes using SecureRandom.
102+ * @param length The length of the random bytes to generate
103+ * @return Array of random bytes
104+ */
105+ private fun generateRandomBytes (length : Int ): ByteArray {
106+ val bytes = ByteArray (length)
107+ SecureRandom ().nextBytes(bytes)
108+ return bytes
109+ }
110+
111+ /* *
112+ * Derives a key from the password and salt using SHA-256.
113+ * @param password The password
114+ * @param salt The salt
115+ * @return Derived key
116+ */
117+ private fun deriveKey (password : String , salt : ByteArray ): ByteArray {
118+ val md = MessageDigest .getInstance(" SHA-256" )
119+ val combined = password.toByteArray() + salt
120+ return md.digest(combined)
121+ }
122+ }
0 commit comments