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:
|
||||
[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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
Loading…
Reference in a new issue