Skip to content

Commit

Permalink
feat(app): support input AEAD associated data
Browse files Browse the repository at this point in the history
  • Loading branch information
Leon406 committed Apr 24, 2022
1 parent ed33153 commit 59e4072
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 93 deletions.
17 changes: 15 additions & 2 deletions app/src/main/kotlin/me/leon/component/KeyIvInputView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@ import me.leon.Styles
import me.leon.ext.decodeToByteArray
import tornadofx.*

class KeyIvInputView(private val enableIv: SimpleBooleanProperty = SimpleBooleanProperty(true)) :
View() {
class KeyIvInputView(
private val enableIv: SimpleBooleanProperty = SimpleBooleanProperty(true),
private val enableAssociatedData: SimpleBooleanProperty = SimpleBooleanProperty(false)
) : View() {
private val toggleKey = ToggleVerticalView()
private val toggleIv = ToggleVerticalView(show = enableIv)
private val toggleData = ToggleVerticalView(show = enableAssociatedData)
private var tfKey: TextField by singleAssign()
private var tfIv: TextField by singleAssign()
private var tfData: TextField by singleAssign()
val keyByteArray
get() = tfKey.text.decodeToByteArray(toggleKey.selectText)

val ivByteArray
get() = tfIv.text.decodeToByteArray(toggleIv.selectText)
val associatedData
get() = tfData.text.decodeToByteArray(toggleData.selectText)

override val root = hbox {
addClass(Styles.left)
Expand All @@ -30,5 +36,12 @@ class KeyIvInputView(private val enableIv: SimpleBooleanProperty = SimpleBoolean
visibleWhen(enableIv)
}
add(toggleIv.root)
label("associateData:") { visibleWhen(enableAssociatedData) }
tfData =
textfield {
promptText = messages["associateDataHint"]
visibleWhen(enableAssociatedData)
}
add(toggleData.root)
}
}
40 changes: 28 additions & 12 deletions app/src/main/kotlin/me/leon/controller/SymmetricCryptoController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ class SymmetricCryptoController : Controller() {
isSingleLine: Boolean = false,
inputEncode: String = "raw",
outputEncode: String = "base64",
associatedData: ByteArray = byteArrayOf()
): String =
catch({ "encrypt error: $it" }) {
println("encrypt $alg")
if (isSingleLine)
data.lineAction2String {
encrypt(it, inputEncode, charset, key, iv, alg, outputEncode)
encrypt(it, inputEncode, charset, key, iv, alg, outputEncode, associatedData)
}
else encrypt(data, inputEncode, charset, key, iv, alg, outputEncode)
else encrypt(data, inputEncode, charset, key, iv, alg, outputEncode, associatedData)
}

private fun encrypt(
Expand All @@ -34,7 +35,8 @@ class SymmetricCryptoController : Controller() {
key: ByteArray,
iv: ByteArray,
alg: String,
outputEncode: String
outputEncode: String,
associatedData: ByteArray = byteArrayOf()
) =
if (alg.startsWith("XXTEA"))
XXTEA
Expand All @@ -44,10 +46,16 @@ class SymmetricCryptoController : Controller() {
data.decodeToByteArray(inputEncode, charset).xor(key).encodeTo(outputEncode, charset)
} else
data.decodeToByteArray(inputEncode, charset)
.encrypt(key, iv, alg)
.encrypt(key, iv, alg, associatedData)
.encodeTo(outputEncode, charset)

fun encryptByFile(key: ByteArray, path: String, iv: ByteArray, alg: String) =
fun encryptByFile(
key: ByteArray,
path: String,
iv: ByteArray,
alg: String,
associatedData: ByteArray = byteArrayOf()
) =
catch({ "encrypt error: $it" }) {
println("encrypt $alg")
val parentFile = path.toFile().parentFile.absolutePath
Expand All @@ -64,14 +72,20 @@ class SymmetricCryptoController : Controller() {
outFileName.toFile().outputStream().use { out ->
path.toFile().inputStream().use { out.write(it.readBytes().xor(key)) }
}
else path.encryptFile(key, iv, alg, outFileName)
else path.encryptFile(key, iv, alg, outFileName, associatedData)
"加密文件路径(同选择文件目录): ${File(outFileName).absolutePath} \n" +
"alg: $alg\n" +
"key(base64): ${key.base64()}\n" +
"iv(base64): ${iv.base64()}\n"
}

fun decryptByFile(key: ByteArray, path: String, iv: ByteArray, alg: String) =
fun decryptByFile(
key: ByteArray,
path: String,
iv: ByteArray,
alg: String,
associatedData: ByteArray = byteArrayOf()
) =
catch({ "decrypt error: $it" }) {
println("decrypt $alg")
val parentFile = path.toFile().parentFile.absolutePath
Expand All @@ -88,7 +102,7 @@ class SymmetricCryptoController : Controller() {
outFileName.toFile().outputStream().use { out ->
path.toFile().inputStream().use { out.write(it.readBytes().xor(key)) }
}
else path.decryptFile(key, iv, alg, outFileName)
else path.decryptFile(key, iv, alg, outFileName, associatedData)
"解密文件路径(同选择文件目录): $outFileName"
}

Expand All @@ -101,14 +115,15 @@ class SymmetricCryptoController : Controller() {
isSingleLine: Boolean = false,
inputEncode: String = "raw",
outputEncode: String = "base64",
associatedData: ByteArray = byteArrayOf()
): String =
catch({ "decrypt error: $it" }) {
println("decrypt $alg")
if (isSingleLine)
data.lineAction2String {
decrypt(it, inputEncode, charset, key, iv, alg, outputEncode)
decrypt(it, inputEncode, charset, key, iv, alg, outputEncode, associatedData)
}
else decrypt(data, inputEncode, charset, key, iv, alg, outputEncode)
else decrypt(data, inputEncode, charset, key, iv, alg, outputEncode, associatedData)
}

private fun decrypt(
Expand All @@ -118,7 +133,8 @@ class SymmetricCryptoController : Controller() {
key: ByteArray,
iv: ByteArray,
alg: String,
outputEncode: String
outputEncode: String,
associatedData: ByteArray = byteArrayOf()
) =
if (alg.startsWith("XXTEA"))
XXTEA
Expand All @@ -128,6 +144,6 @@ class SymmetricCryptoController : Controller() {
data.decodeToByteArray(inputEncode, charset).xor(key).encodeTo(outputEncode, charset)
} else
data.decodeToByteArray(inputEncode, charset)
.decrypt(key, iv, alg)
.decrypt(key, iv, alg, associatedData)
.encodeTo(outputEncode, charset)
}
1 change: 0 additions & 1 deletion app/src/main/kotlin/me/leon/ext/crypto/AsymmetircCrypto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ fun ByteArray.asymmetricEncrypt(key: Key?, alg: String, reserved: Int = 11): Byt
fun ByteArray.privateEncrypt(key: String, alg: String, reserved: Int = 11): ByteArray =
asymmetricEncrypt(key.toPrivateKey(alg), alg, reserved)


/** 生成密钥对 private key pkcs8 */
fun genBase64KeyArray(alg: String, keySize: Int) =
KeyPairGenerator.getInstance(alg.properKeyPairAlg()).run {
Expand Down
53 changes: 43 additions & 10 deletions app/src/main/kotlin/me/leon/ext/crypto/SymmetricCrypto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,62 @@ import java.io.File
import javax.crypto.*
import javax.crypto.spec.*

fun ByteArray.encrypt(key: ByteArray, iv: ByteArray, alg: String): ByteArray =
makeCipher(alg, key, iv, Cipher.ENCRYPT_MODE).doFinal(this)
val AEAD_MODE_REG = "GCM|EAX|CCM|OCB".toRegex()

fun ByteArray.decrypt(key: ByteArray, iv: ByteArray, alg: String): ByteArray =
makeCipher(alg, key, iv, Cipher.DECRYPT_MODE).doFinal(this)
fun ByteArray.encrypt(
key: ByteArray,
iv: ByteArray,
alg: String,
associatedData: ByteArray = byteArrayOf()
): ByteArray = makeCipher(alg, key, iv, Cipher.ENCRYPT_MODE, associatedData).doFinal(this)

fun makeCipher(alg: String, key: ByteArray, iv: ByteArray, cipherMode: Int): Cipher =
fun ByteArray.decrypt(
key: ByteArray,
iv: ByteArray,
alg: String,
associatedData: ByteArray = byteArrayOf()
): ByteArray = makeCipher(alg, key, iv, Cipher.DECRYPT_MODE, associatedData).doFinal(this)

fun makeCipher(
alg: String,
key: ByteArray,
iv: ByteArray,
cipherMode: Int,
associatedData: ByteArray = byteArrayOf()
): Cipher =
Cipher.getInstance(alg).apply {
val keySpec: SecretKey = SecretKeySpec(key, alg.substringBefore("/"))
if (alg.contains("ECB|RC4".toRegex())) init(cipherMode, keySpec)
// require jdk 11
else if (alg.equals("ChaCha20", true))
init(cipherMode, keySpec, ChaCha20ParameterSpec(iv, 7))
else init(cipherMode, keySpec, IvParameterSpec(iv))
else
init(cipherMode, keySpec, IvParameterSpec(iv)).also {
if (alg.contains(AEAD_MODE_REG) && associatedData.isNotEmpty()) {
updateAAD(associatedData)
}
}
}

fun String.encryptFile(key: ByteArray, iv: ByteArray, alg: String, outFileName: String) {
val cipher = makeCipher(alg, key, iv, Cipher.ENCRYPT_MODE)
fun String.encryptFile(
key: ByteArray,
iv: ByteArray,
alg: String,
outFileName: String,
associatedData: ByteArray = byteArrayOf()
) {
val cipher = makeCipher(alg, key, iv, Cipher.ENCRYPT_MODE, associatedData)
doStreamCrypto(outFileName, cipher, this)
}

fun String.decryptFile(key: ByteArray, iv: ByteArray, alg: String, outFileName: String) {
val cipher = makeCipher(alg, key, iv, Cipher.DECRYPT_MODE)
fun String.decryptFile(
key: ByteArray,
iv: ByteArray,
alg: String,
outFileName: String,
associatedData: ByteArray = byteArrayOf()
) {
val cipher = makeCipher(alg, key, iv, Cipher.DECRYPT_MODE, associatedData)
doStreamCrypto(outFileName, cipher, this)
}

Expand Down
24 changes: 18 additions & 6 deletions app/src/main/kotlin/me/leon/view/SymmetricCryptoView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import me.leon.Styles
import me.leon.component.KeyIvInputView
import me.leon.controller.SymmetricCryptoController
import me.leon.ext.*
import me.leon.ext.crypto.AEAD_MODE_REG
import me.leon.ext.fx.*
import tornadofx.*
import tornadofx.FX.Companion.messages
Expand All @@ -21,6 +22,7 @@ class SymmetricCryptoView : Fragment(messages["symmetricBlock"]) {
private val isProcessing = SimpleBooleanProperty(false)
private val isSingleLine = SimpleBooleanProperty(false)
private val isEnableIv = SimpleBooleanProperty(true)
private val isEnableAEAD = SimpleBooleanProperty(false)
private val isEnableModAndPadding = SimpleBooleanProperty(true)
private lateinit var taInput: TextArea
private lateinit var tgInput: ToggleGroup
Expand All @@ -37,7 +39,7 @@ class SymmetricCryptoView : Fragment(messages["symmetricBlock"]) {
get() =
"Cipher: $cipher charset: ${selectedCharset.get()} file mode: ${isFile.get()} cost: $timeConsumption ms"
private lateinit var labelInfo: Label
private val keyIvInputView = KeyIvInputView(isEnableIv)
private val keyIvInputView = KeyIvInputView(isEnableIv, isEnableAEAD)
private var inputEncode = "raw"
private var outputEncode = "base64"
private val customAlg = arrayOf("XXTEA", "XOR")
Expand Down Expand Up @@ -173,7 +175,13 @@ class SymmetricCryptoView : Fragment(messages["symmetricBlock"]) {
}
}
selectedMod.addListener { _, _, newValue ->
newValue?.run { isEnableIv.value = newValue != "ECB" }
newValue?.run {
isEnableIv.value = newValue != "ECB"
isEnableAEAD.value = newValue.contains(AEAD_MODE_REG)
if (isEnableAEAD.value) {
selectedPadding.value = "NoPadding"
}
}
}

hbox {
Expand Down Expand Up @@ -245,7 +253,8 @@ class SymmetricCryptoView : Fragment(messages["symmetricBlock"]) {
keyIvInputView.keyByteArray,
it,
keyIvInputView.ivByteArray,
cipher
cipher,
keyIvInputView.associatedData
)
}
else
Expand All @@ -257,15 +266,17 @@ class SymmetricCryptoView : Fragment(messages["symmetricBlock"]) {
selectedCharset.get(),
isSingleLine.get(),
inputEncode,
outputEncode
outputEncode,
keyIvInputView.associatedData
)
else if (isFile.get())
inputText.lineAction2String {
controller.decryptByFile(
keyIvInputView.keyByteArray,
it,
keyIvInputView.ivByteArray,
cipher
cipher,
keyIvInputView.associatedData
)
}
else {
Expand All @@ -277,7 +288,8 @@ class SymmetricCryptoView : Fragment(messages["symmetricBlock"]) {
selectedCharset.get(),
isSingleLine.get(),
inputEncode,
outputEncode
outputEncode,
keyIvInputView.associatedData
)
}
} ui
Expand Down
1 change: 1 addition & 0 deletions app/src/main/resources/Messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ outputEncoding=Output Encoding:
ver=Version
ivHint=Plz input initial vector
keyHint=Plz input key
associateDataHint=Optional
qrImg=Qrcode Image:
qrHint=Plz input or use screenshot/clipboard/file recognize
recognize=Recognize:
Expand Down
1 change: 1 addition & 0 deletions app/src/main/resources/Messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ outputEncoding=Output Encoding:
ver=Version
ivHint=Plz input initial vector
keyHint=Plz input key
associateDataHint=Optional
qrImg=Qrcode Image:
qrHint=Plz input or use screenshot/clipboard/file recognize
recognize=Recognize:
Expand Down
1 change: 1 addition & 0 deletions app/src/main/resources/Messages_zh_CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ outputEncoding=\u8f93\u51fa\u7f16\u7801:
ver=\u7248\u672c
ivHint=\u8bf7\u8f93\u5165iv
keyHint=\u8bf7\u8f93\u5165key
associateDataHint=\u53ef\u9009
qrImg=\u4e8c\u7ef4\u7801\u56fe\u7247:
qrHint=\u8bf7\u8f93\u5165\u6587\u672c\u6216\u8005\u4f7f\u7528\u622a\u5c4f/\u526a\u8d34\u677f/\u6587\u4ef6\u8bc6\u522b\u4e8c\u7ef4\u7801
recognize=\u8bc6\u522b:
Expand Down
24 changes: 11 additions & 13 deletions app/src/test/kotlin/me/leon/dh/DHTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import java.security.spec.X509EncodedKeySpec
import javax.crypto.KeyAgreement
import kotlin.test.assertEquals
import me.leon.encode.base.base64
import me.leon.ext.catch
import me.leon.ext.crypto.genKeyPair
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Test
Expand All @@ -32,16 +31,15 @@ class DHTest {

fun KeyPair.generateSecretKey(receivedPubKeyBytes: ByteArray, alg: String = "DH"): ByteArray? =
runCatching {
val keySpec = X509EncodedKeySpec(receivedPubKeyBytes)
val kf = KeyFactory.getInstance(alg, "BC")
val receivedPublicKey: PublicKey = kf.generatePublic(keySpec)
// 生成本地密钥:
val keyAgreement = KeyAgreement.getInstance(alg, "BC")
keyAgreement.init(private) // 自己的PrivateKey
keyAgreement.doPhase(receivedPublicKey, true) // 对方的PublicKey
// 生成SecretKey密钥:
keyAgreement.generateSecret()
}.getOrNull()


val keySpec = X509EncodedKeySpec(receivedPubKeyBytes)
val kf = KeyFactory.getInstance(alg, "BC")
val receivedPublicKey: PublicKey = kf.generatePublic(keySpec)
// 生成本地密钥:
val keyAgreement = KeyAgreement.getInstance(alg, "BC")
keyAgreement.init(private) // 自己的PrivateKey
keyAgreement.doPhase(receivedPublicKey, true) // 对方的PublicKey
// 生成SecretKey密钥:
keyAgreement.generateSecret()
}
.getOrNull()
}
Loading

0 comments on commit 59e4072

Please sign in to comment.