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:
[Rusa](README_RU.md)
[Русский](README_RU.md)
---
# Sekreto por Androido
@ -28,4 +28,4 @@ Kaŝu sekretan mesaĝon en teksto!
---
## 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"
minSdk = 21
targetSdk = 33
versionCode = 2
versionName = "1.0.1"
versionCode = 3
versionName = "1.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View file

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

View file

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