Как я могу получить зашифрованный текст в Котлине?

Поскольку веб-сайт моей школы слишком сложен в использовании, в прошлом году я решил написать мобильное приложение, чтобы сделать его более удобным для пользователей. Для этого я использовал Jsoup и другие утилиты.

Я обнаружил, что в последнее время им стало намного сложнее пользоваться, и на веб-сайте появилось больше страниц авторизации. Я потратил целый день, чтобы узнать, как перевести код с JS на Kotlin.


Есть ли способ получить зашифрованный текст значений или как сделать код работоспособным в оценщике JavaScript Rhino.

Код encryptBase64 такой, я не знаю, как получить ciphertext в Котлине

encryptBase64: function (e) {//e=password
    var r = f();
    var t = s.default.MD5(r);
    console.info("r = "+r);//r=GN12MVOFWVZJ51KZ
    console.info("t = "+t);//r=GN12MVOFWVZJ51KZ
    console.info("l = "+l);//l=c2df1fd689074ff84521cea43a677bbf

    var o = s.default.AES.encrypt(r, l, {iv: l, mode: a, padding: d});
    var n = s.default.AES.encrypt(e, t, {iv: t, mode: a, padding: d});
    console.info("o = "+o)//o=tAzMY2LKjgC96ZJHXYjorazfL56s//vgFgoerDmaH3g=
    console.info("n = "+n)//n=GQNoFJVGL5qdLyb7KxFXAA==
    var o_cipher=o.ciphertext

    var n_cipher=n.ciphertext
    console.info("o_cipher = "+o_cipher)o_cipher=b40ccc6362ca8e00bde992475d88e8adacdf2f9eacfffbe0160a1eac399a1f78
    console.info("n_cipher = "+n_cipher)//n_cipher=1903681495462f9a9d2f26fb2b115700
    var i = s.default.lib.WordArray.create([].concat(c(o_cipher.words), c(n_cipher.words)));

    console.info("i = "+i)//i=b40ccc6362ca8e00bde992475d88e8adacdf2f9eacfffbe0160a1eac399a1f781903681495462f9a9d2f26fb2b115700
    var stringifiedI=s.default.enc.Base64.stringify(i)
    console.info("stringified i = "+stringifiedI)//stringified i=tAzMY2LKjgC96ZJHXYjorazfL56s//vgFgoerDmaH3gZA2gUlUYvmp0vJvsrEVcA
    return stringifiedI
}

Я подумал, что ciphertext может быть чем-то вроде AES, а не RSA или MD5, потому что сервер может расшифровать его без ключа (я не нашел ничего, кроме зашифрованного пароля).

Просить школу о помощи невозможно, моя школа это не одобряет.

Полный код приведен ниже (я думаю, это что-то вроде CryptoJS): Полный код на GitHub

Что я пробовал

  • Я попытался написать код на Котлине для выполнения аналогичной функции, но он не работает должным образом.

Я создал функцию расширения шифрования/дешифрования aes следующим образом: aes.kt

И js.kt, содержащий callJs()функцию.

Я пытался использовать callJs(), чтобы получить результат.

//fun main
    val a= callJs(JS.encryptCode,"aesOene","encryptBase64","password")
    println(a)
object JS{
    val encryptCode: String by lazy {
        val bytes = this::class.java.getResourceAsStream("/aesOene.js")!!.readBytes()
        String(bytes)
    }
}

И я получил это

Exception in thread "main" org.mozilla.javascript.EcmaError: ReferenceError: "window" is not defined. (aesOene#1)

Затем я попытался реализовать encryptBase64() следующим образом:

// fun main
    val b=encryptBase64("password")
    println(b)

fun aesKeyBuilder():String{
    val p= arrayListOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")
    val builder=StringBuilder()
    for (t in 0 until 16){
        val o= ceil(35*Math.random()).toInt()
        builder.append(p[o])
    }
    return builder.toString()
}
fun encryptBase64(e:String):String{
    val r=aesKeyBuilder()
    val l = "ass-apex".md5
    val t=r.md5
    val o=r.encryptByAES(l,)
    val n=e.encryptByAES(t,)
    val i=o+n
    return i.base64
}

И не дает правильного результата.

Чего я ожидал

  • Способ получить результат encryptBase64() в Котлине или Java

🤔 А знаете ли вы, что...
JavaScript можно использовать для создания видеоигр, как 2D, так и 3D, с использованием библиотеки Three.js.


1
86
1

Ответ:

Решено

Для корректного выполнения функции h.encryptBase64() требуется WordArray l, который либо устанавливается равным MD5-хешу пароля e с помощью функции h.setSecret(e), либо альтернативно может быть определен непосредственно как любой 16-байтовый WordArray, например l = CryptoJS.enc.Utf8.parse('0123456789012345').

Затем функция h.encryptBase64() выполняет следующее:

  • С помощью функции f() генерируется случайная строка из 16 символов r, состоящая из последовательности символов из p.
  • Из r генерируется хеш MD5 t.
  • r шифруется с помощью AES в режиме CBC с дополнением PKCS#7 (зашифрованный текст o_cipher). l используется как для ключа, так и для капельницы.
  • Открытый текст шифруется с помощью AES в режиме CBC с дополнением PKCS#7 (зашифрованный текст n_cipher). t используется как для ключа, так и для капельницы.
  • Зашифрованные тексты o_cipher и n_cipher объединяются (результат i).
  • i закодирован и возвращен в Base64.

Имейте в виду, что текущий код JavaScript излишне сложен и полон уязвимостей (например, MD5, Math.Random).
Обычный подход будет использовать надежную функцию получения ключа (по крайней мере PBKDF2) в сочетании со случайной, несекретной солью для генерации ключа, который будет использоваться для шифрования открытого текста.
Соль будет передана дешифрующей стороне вместе с зашифрованным текстом, чтобы можно было восстановить ключ.
На самом деле имело бы смысл пересмотреть код JavaScript перед портированием, но поскольку вас, вероятно, интересует портирование неизмененного кода 1:1, это описано ниже:

  • Опубликованную вами функцию aesKeyBuilder() в принципе можно использовать как аналог f(). Однако Math.random() следует заменить на SecureRandom#nextDouble(), поскольку Math.random() не является криптографически безопасным:

    import java.security.SecureRandom
    ...
    fun aesKeyBuilder(): String {
        val p = arrayListOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")
        val builder = StringBuilder()
        val secureRandom = SecureRandom()
        for (t in 0 until 16){
            val o= ceil(35 * secureRandom.nextDouble()).toInt()
            builder.append(p[o])
        }
        return builder.toString()
    }
    
  • Следующим шагом является определение функции, которая выполняет шифрование с использованием AES в режиме CBC и заполнения PKCS#7, например:

    import javax.crypto.Cipher
    import javax.crypto.spec.IvParameterSpec
    import javax.crypto.spec.SecretKeySpec
    ...
    fun aesEncrypt(key: ByteArray, data: ByteArray): ByteArray {
        val secretKeySpec = SecretKeySpec(key, "AES")
        val ivParameterSpec = IvParameterSpec(key)
        val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
        return cipher.doFinal(data)
    }
    
  • При этом логику кода JavaScript можно перенести на Kotlin следующим образом:

    import android.util.Base64
    import java.security.MessageDigest
    ...
    val password = "my passphrase"
    val plaintext = "The quick brown fox jumps over the lazy dog"
    val key_l = MessageDigest.getInstance("MD5").digest(password.toByteArray())
    val key_r = aesKeyBuilder().toByteArray()
    val key_t = MessageDigest.getInstance("MD5").digest(key_r)
    val encKey_o = aesEncrypt(key_l, key_r)
    val ciphertext_n = aesEncrypt(key_t, plaintext.toByteArray())
    val result_i = encKey_o + ciphertext_n
    val resultB64 = Base64.encodeToString(result_i, Base64.NO_WRAP);
    

Тест:

Для теста лучше всего реализовать код JavaScript для расшифровки, который расшифровывает зашифрованный текст, сгенерированный кодом JavaScript. Код Kotlin можно считать совместимым, если этот код дешифрования JavaScript также расшифровывает зашифрованный текст, сгенерированный кодом Kotlin.

Следующий код JavaScript успешно выполняет такой тест:

var a = CryptoJS.mode.CBC, 
    d = CryptoJS.pad.Pkcs7, l = "", u = "",
    p = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"],
    f = function () { 
        return function (e) {
            for (var r = "", t = 0; t < e; t++) {
                var o = Math.ceil(35 * Math.random());
                r += p[o]
            }
            return r
        }(16)
    }, 
    h = { 
        encryptBase64: function (e) {
            var r = f();
            var t = CryptoJS.MD5(r);
            console.info("r = "+r);
            console.info("t = "+t);
            console.info("l = "+l);
                        
            var o = CryptoJS.AES.encrypt(r, l, {iv: l, mode: a, padding: d});
            var n = CryptoJS.AES.encrypt(e, t, {iv: t, mode: a, padding: d});
            console.info("o = "+o)
            console.info("n = "+n)
            var o_cipher=o.ciphertext
                        
            var n_cipher=n.ciphertext
            console.info("o_cipher = "+o_cipher)
            console.info("n_cipher = "+n_cipher)
            var i = CryptoJS.lib.WordArray.create([].concat(c(o_cipher.words), c(n_cipher.words)));
                        
            console.info("i = "+i)
            var stringifiedI=CryptoJS.enc.Base64.stringify(i)
            console.info("stringified i = "+stringifiedI)
            return stringifiedI
        }, 
        setSecret: function (e) {
            u = e, l = CryptoJS.MD5(u)
        }
    };

function c(e) {
    if (Array.isArray(e)) {
        for (var r = 0, t = Array(e.length); r < e.length; r++) t[r] = e[r];
        return t
    }
    return Array.from(e)
}
      
// encryption
h.setSecret('my passphrase') 
var ciphertext = h.encryptBase64('The quick brown fox jumps over the lazy dog')
console.info('Ciphertext:', ciphertext)

// decryption from JavaScript ciphertext
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertext)
var encryptedKeyWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(0, 256 / 32))
var encryptedDataWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(256 / 32))
var l = CryptoJS.MD5('my passphrase') // apply passphrase from setSecret
var r = CryptoJS.AES.decrypt({ciphertext: encryptedKeyWA}, l, {iv: l, mode: a, padding: d})
var t = CryptoJS.MD5(r)
var e = CryptoJS.AES.decrypt({ciphertext: encryptedDataWA}, t, {iv: t, mode: a, padding: d})
console.info('Decrypted:', e.toString(CryptoJS.enc.Utf8))

// decryption from Kotlin ciphertext 
var ciphertextFromKotlin = 'rFXxMQjsIhfCOPiIbgLTJJioVqb7ONCp9Tx3WoTpbF9QxlJNdpR4r6mPtBbnSsEl9PL30hoER6P+RJS9hVUbs/i7ipxhwqiFEbbH97ryZmM='
var ciphertextWA = CryptoJS.enc.Base64.parse(ciphertextFromKotlin)
var encryptedKeyWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(0, 256 / 32))
var encryptedDataWA = CryptoJS.lib.WordArray.create(ciphertextWA.words.slice(256 / 32))
var l = CryptoJS.MD5('my passphrase') // apply passphrase from Kotlin code
var r = CryptoJS.AES.decrypt({ciphertext: encryptedKeyWA}, l, {iv: l, mode: a, padding: d})
var t = CryptoJS.MD5(r)
var e = CryptoJS.AES.decrypt({ciphertext: encryptedDataWA}, t, {iv: t, mode: a, padding: d})
console.info('Decrypted:', e.toString(CryptoJS.enc.Utf8))
<script src = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>