Compare commits
3 commits
37c515fc46
...
eebde09279
Author | SHA1 | Date | |
---|---|---|---|
|
eebde09279 | ||
|
59d1ee5c0f | ||
|
5afa27e1fc |
17
.idea/deploymentTargetDropDown.xml
Normal file
17
.idea/deploymentTargetDropDown.xml
Normal 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>
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue