Compare commits

..

3 commits

Author SHA1 Message Date
1010 eebde09279 Versio 1.0.2 2024-03-22 16:43:20 +07:00
1010 59d1ee5c0f Anstataŭigis AES-CBC per AES-GCM 2024-03-22 16:32:38 +07:00
1010 5afa27e1fc Malgrandaj redaktoj 2024-03-22 16:25:58 +07:00
6 changed files with 111 additions and 74 deletions

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Nexus_4_API_22.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2024-03-22T09:22:07.756257223Z" />
</component>
</project>

View file

@ -1,5 +1,5 @@
Ĉi tiu paĝo estas en aliaj lingvoj: Ĉi tiu paĝo estas en aliaj lingvoj:
[Rusa](README_RU.md) [Русский](README_RU.md)
--- ---
# Sekreto por Androido # Sekreto por Androido
@ -28,4 +28,4 @@ Kaŝu sekretan mesaĝon en teksto!
--- ---
## Komputilo ## Komputilo
[Sekreto](https://git.disroot.org/1010/Sekreto) [Sekreto](https://git.disroot.org/1010/Sekreto)

View file

@ -11,8 +11,8 @@ android {
applicationId = "com.example.sekreto" applicationId = "com.example.sekreto"
minSdk = 21 minSdk = 21
targetSdk = 33 targetSdk = 33
versionCode = 2 versionCode = 3
versionName = "1.0.1" versionName = "1.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View file

@ -1,97 +1,118 @@
package com.example.sekreto package com.example.sekreto
import java.nio.ByteBuffer
import java.nio.charset.Charset
import java.security.SecureRandom import java.security.SecureRandom
import javax.crypto.BadPaddingException
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.SecretKey import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.PBEKeySpec import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
class Crypto { class Crypto {
companion object { private val TAG_LENGTH = 16
private const val KEY_SIZE_BITS = 128 private val IV_LENGTH = 12
private const val ITERATION_COUNT = 10000 private val SALT_LENGTH = 16
private const val SALT_SIZE_BYTES = 16 private val KEY_LENGTH = 32
private const val AES_KEY_SIZE_BYTES = KEY_SIZE_BITS / 8 private val ITERATIONS = 65535
private const val IV_SIZE_BYTES = 16
private fun generateSalt(): ByteArray { private val CIPHER_ALGORITHM = "AES/GCM/NoPadding"
val secureRandom = SecureRandom() private val FACTORY_INSTANCE = "PBKDF2WithHmacSHA1"
val salt = ByteArray(SALT_SIZE_BYTES) private val UTF_8: Charset = Charsets.UTF_8
secureRandom.nextBytes(salt)
return salt private fun getSecretKey(password: String, salt: ByteArray): SecretKey {
val spec = PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH * 8)
val factory = SecretKeyFactory.getInstance(FACTORY_INSTANCE)
return SecretKeySpec(factory.generateSecret(spec).encoded, "AES")
}
private fun getRandomNonce(length: Int): ByteArray {
val nonce = ByteArray(length)
SecureRandom().nextBytes(nonce)
return nonce
}
private fun initCipher(mode: Int, secretKey: SecretKey, iv: ByteArray): Cipher {
val cipher = Cipher.getInstance(CIPHER_ALGORITHM)
cipher.init(mode, secretKey, GCMParameterSpec(TAG_LENGTH * 8, iv))
return cipher
}
private fun byte2hex(byteArray: ByteArray): String {
val stringBuilder = StringBuilder()
for (byte in byteArray) {
val hex = String.format("%02X", byte)
stringBuilder.append(hex)
} }
return stringBuilder.toString()
}
private fun generateKey(password: String, salt: ByteArray): SecretKey { private fun hex2byte(hexString: String): ByteArray {
val keySpec = PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, AES_KEY_SIZE_BYTES * 8) val byteArray = ByteArray(hexString.length / 2)
val secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1") for (i in byteArray.indices) {
val secretKey = secretKeyFactory.generateSecret(keySpec) val index = i * 2
return SecretKeySpec(secretKey.encoded, "AES") val hex = hexString.substring(index, index + 2)
val byte = hex.toInt(16).toByte()
byteArray[i] = byte
} }
return byteArray
}
private fun generateIV(): IvParameterSpec { private fun getAESKeyFromPassword(password: CharArray, salt: ByteArray): SecretKey {
val iv = ByteArray(IV_SIZE_BYTES) val factory = SecretKeyFactory.getInstance(FACTORY_INSTANCE)
SecureRandom().nextBytes(iv) val spec = PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH * 8)
return IvParameterSpec(iv) val secret = SecretKeySpec(factory.generateSecret(spec).encoded, "AES")
} return secret
}
fun byte2hex(byteArray: ByteArray): String { fun encrypt(password: String, plainMessage: String): String {
val stringBuilder = StringBuilder() val salt = getRandomNonce(SALT_LENGTH)
for (byte in byteArray) { val secretKey = getSecretKey(password, salt)
val hex = String.format("%02X", byte)
stringBuilder.append(hex)
}
return stringBuilder.toString()
}
fun hex2byte(hexString: String): ByteArray { val iv = getRandomNonce(IV_LENGTH)
val byteArray = ByteArray(hexString.length / 2)
for (i in byteArray.indices) {
val index = i * 2
val hex = hexString.substring(index, index + 2)
val byte = hex.toInt(16).toByte()
byteArray[i] = byte
}
return byteArray
}
fun encrypt(plaintext: String, password: String): String { val cipher = initCipher(Cipher.ENCRYPT_MODE, secretKey, iv)
val salt = generateSalt()
val secretKey = generateKey(password, salt)
val iv = generateIV() val encryptedMessageByte = cipher.doFinal(plainMessage.toByteArray(UTF_8))
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv)
val ciphertext = cipher.doFinal(plaintext.toByteArray(Charsets.UTF_8)) val cipherByte = ByteBuffer.allocate(salt.size + iv.size + encryptedMessageByte.size)
val encryptedData = ByteArray(salt.size + iv.iv.size + ciphertext.size) .put(salt)
.put(iv)
.put(encryptedMessageByte)
.array()
System.arraycopy(salt, 0, encryptedData, 0, salt.size) return byte2hex(cipherByte)
System.arraycopy(iv.iv, 0, encryptedData, salt.size, iv.iv.size) }
System.arraycopy(ciphertext, 0, encryptedData, salt.size + iv.iv.size, ciphertext.size)
return byte2hex(encryptedData) fun decrypt(cipherContent: String, password: String): String? {
} try {
val decode = hex2byte(cipherContent)
val byteBuffer = ByteBuffer.wrap(decode)
fun decrypt(hexString: String, password: String): String? { val salt = ByteArray(SALT_LENGTH)
try { byteBuffer.get(salt)
val ciphertext = hex2byte(hexString)
val salt = ciphertext.copyOfRange(0, SALT_SIZE_BYTES)
val iv = ciphertext.copyOfRange(SALT_SIZE_BYTES, SALT_SIZE_BYTES + IV_SIZE_BYTES)
val encryptedData = ciphertext.copyOfRange(SALT_SIZE_BYTES + IV_SIZE_BYTES, ciphertext.size)
val secretKey = generateKey(password, salt) val iv = ByteArray(IV_LENGTH)
byteBuffer.get(iv)
val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding") val content = ByteArray(byteBuffer.remaining())
cipher.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv)) byteBuffer.get(content)
val decryptedData = cipher.doFinal(encryptedData) val cipher = Cipher.getInstance(CIPHER_ALGORITHM)
return decryptedData.toString(Charsets.UTF_8) val aesKeyFromPassword = getAESKeyFromPassword(password.toCharArray(), salt)
} catch (e: Exception) { cipher.init(Cipher.DECRYPT_MODE, aesKeyFromPassword, GCMParameterSpec(TAG_LENGTH * 8, iv))
return null val plainText = cipher.doFinal(content)
} return String(plainText, UTF_8)
} catch (e: BadPaddingException) {
e.printStackTrace()
return null
} catch (e: Exception) {
e.printStackTrace()
return null
} }
} }
} }

View file

@ -60,7 +60,7 @@ class HideActivity : AppCompatActivity() {
val warningDialog = WarningDialog() val warningDialog = WarningDialog()
warningDialog.show(supportFragmentManager, "warning_dialog") warningDialog.show(supportFragmentManager, "warning_dialog")
} else { } else {
val hex = Crypto.encrypt(secret, password) val hex = Crypto().encrypt(password, secret)
val hiding = Stegano.hex2hid(hex) val hiding = Stegano.hex2hid(hex)
val startIndex = pub_msg.length / 2 val startIndex = pub_msg.length / 2

View file

@ -52,8 +52,7 @@ class ViewActivity : AppCompatActivity() {
if (result.isNotEmpty() && password.isNotEmpty()) { if (result.isNotEmpty() && password.isNotEmpty()) {
val extraction = Stegano.hid2hex(result) val extraction = Stegano.hid2hex(result)
val decrypt = Crypto().decrypt(extraction, password)
val decrypt = Crypto.decrypt(extraction, password)
if (decrypt != null) { if (decrypt != null) {