由于我学校的网站太难使用,去年我决定编写一个移动应用程序以获得更好的用户体验。 我已经使用 Jsoup 和其他实用程序来制作它。
最近发现使用起来比较困难,而且网站的授权页面也多了。 我花了一整天的时间才找到如何将代码从 JS 翻译成 Kotlin。
有没有办法可以获取值的密文或如何使代码在Rhino JavaScript评估器中运行。
encryptBase64
代码是这样的,我不知道如何在Kotlin中获得ciphertext
encryptBase64: function (e) {//e=password
var r = f();
var t = s.default.MD5(r);
console.log("r="+r);//r=GN12MVOFWVZJ51KZ
console.log("t="+t);//r=GN12MVOFWVZJ51KZ
console.log("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.log("o="+o)//o=tAzMY2LKjgC96ZJHXYjorazfL56s//vgFgoerDmaH3g=
console.log("n="+n)//n=GQNoFJVGL5qdLyb7KxFXAA==
var o_cipher=o.ciphertext
var n_cipher=n.ciphertext
console.log("o_cipher="+o_cipher)o_cipher=b40ccc6362ca8e00bde992475d88e8adacdf2f9eacfffbe0160a1eac399a1f78
console.log("n_cipher="+n_cipher)//n_cipher=1903681495462f9a9d2f26fb2b115700
var i = s.default.lib.WordArray.create([].concat(c(o_cipher.words), c(n_cipher.words)));
console.log("i="+i)//i=b40ccc6362ca8e00bde992475d88e8adacdf2f9eacfffbe0160a1eac399a1f781903681495462f9a9d2f26fb2b115700
var stringifiedI=s.default.enc.Base64.stringify(i)
console.log("stringified i="+stringifiedI)//stringified i=tAzMY2LKjgC96ZJHXYjorazfL56s//vgFgoerDmaH3gZA2gUlUYvmp0vJvsrEVcA
return stringifiedI
}
我认为
ciphertext
可能是类似AES的东西,而不是RSA或MD5,因为服务器可以在没有密钥的情况下解密它(除了加密的密码之外我没有找到其他任何东西)。
向学校求助是不可能的,我的学校不批准。
完整代码如下(我认为它类似于CryptoJS): GitHub 中的完整代码
我创建了一个 aes 加密/解密扩展函数,如下所示: aes.kt
还有一个包含 callJs()
函数的
js.kt。
我尝试使用
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()
结果的方法为了正确执行,
h.encryptBase64()
函数需要一个WordArrayl
,它可以使用函数e
设置为等于密码h.setSecret(e)
的MD5哈希值,也可以直接定义为任何16个字节WordArray,例如l = CryptoJS.enc.Utf8.parse('0123456789012345')
。
h.encryptBase64()
函数执行以下操作:
f()
生成一个随机的16个字符的字符串r
,它由p
中的字符序列组成。r
生成 MD5 哈希值 t
。r
在 CBC 模式下使用 AES 进行加密,并使用 PKCS#7 填充(密文 o_cipher
)。 l
用于密钥和 IV。n_cipher
)。 t
用于密钥和 IV。o_cipher
和 n_cipher
连接起来(结果 i
)。i
经过 Base64 编码并返回。请注意,当前的 JavaScript 代码过于复杂且充满漏洞(例如 MD5、
Math.Random
)。您发布的
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()
}
下一步是定义一个函数,以 CBC 模式和 PKCS#7 填充执行 AES 加密,例如:
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 代码生成的密文进行解密。如果这段 JavaScript 解密代码也能解密 Kotlin 代码生成的密文,则可以认为 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.log("r="+r);
console.log("t="+t);
console.log("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.log("o="+o)
console.log("n="+n)
var o_cipher=o.ciphertext
var n_cipher=n.ciphertext
console.log("o_cipher="+o_cipher)
console.log("n_cipher="+n_cipher)
var i = CryptoJS.lib.WordArray.create([].concat(c(o_cipher.words), c(n_cipher.words)));
console.log("i="+i)
var stringifiedI=CryptoJS.enc.Base64.stringify(i)
console.log("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.log('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.log('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.log('Decrypted:', e.toString(CryptoJS.enc.Utf8))
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>