diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt index 0d5aed1c3..afeb13747 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt @@ -6,6 +6,7 @@ import kotlinx.io.core.toByteArray import net.mamoe.mirai.BotAccount import net.mamoe.mirai.qqandroid.utils.* import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.cryptor.ECDH import net.mamoe.mirai.utils.io.hexToBytes /* @@ -27,7 +28,7 @@ internal open class QQAndroidClient( val context: Context, val account: BotAccount, - val ecdh: ECDH = ECDH.Default, + val ecdh: ECDH = ECDH(), val device: DeviceInfo = SystemDeviceInfo(context) ) { val tgtgtKey: ByteArray = generateTgtgtKey(device.guid) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ECDH.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ECDH.kt index 44e1594cd..45de835ec 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ECDH.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ECDH.kt @@ -1,7 +1,5 @@ package net.mamoe.mirai.qqandroid.utils -import net.mamoe.mirai.utils.io.chunkedHexToBytes - /** * From QQAndroid 8.2.0 * `oicq.wlogin_sdk.tools.EcdhCrypt` @@ -9,11 +7,12 @@ import net.mamoe.mirai.utils.io.chunkedHexToBytes * Constant to avoid calculations */ interface ECDH { + /* object Default : ECDH { override val publicKey: ByteArray = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes() override val shareKey: ByteArray = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes() override val privateKey: ByteArray = ByteArray(16) - } + }*/ val publicKey: ByteArray diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index fa5f77526..cb327ba39 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -136,6 +136,7 @@ kotlin { api(kotlinx("io-jvm", kotlinXIoVersion)) api(kotlinx("serialization-runtime", serializationVersion)) + api("org.bouncycastle:bcprov-jdk15on:1.64") runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE } } diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDH.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDH.kt new file mode 100644 index 000000000..6a972572c --- /dev/null +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDH.kt @@ -0,0 +1,48 @@ +package net.mamoe.mirai.utils.cryptor + +import net.mamoe.mirai.utils.md5 +import java.security.* +import java.security.spec.X509EncodedKeySpec +import javax.crypto.KeyAgreement + + +actual typealias ECDHPrivateKey = PrivateKey +actual typealias ECDHPublicKey = PublicKey + +actual class ECDHKeyPair( + private val delegate: KeyPair +) { + actual val privateKey: ECDHPrivateKey get() = delegate.private + actual val publicKey: ECDHPublicKey get() = delegate.public + + actual val shareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey) +} + +@Suppress("FunctionName") +actual fun ECDH() = ECDH(ECDH.generateKeyPair()) + +actual class ECDH actual constructor(val keyPair: ECDHKeyPair) { + actual companion object { + actual fun generateKeyPair(): ECDHKeyPair { + return ECDHKeyPair(KeyPairGenerator.getInstance("ECDH").genKeyPair()) + } + + actual fun calculateShareKey( + privateKey: ECDHPrivateKey, + publicKey: ECDHPublicKey + ): ByteArray { + val instance = KeyAgreement.getInstance("ECDH", "BC") + instance.init(privateKey) + instance.doPhase(publicKey, true) + return md5(instance.generateSecret()) + } + + actual fun constructPublicKey(key: ByteArray): ECDHPublicKey { + return KeyFactory.getInstance("EC", "BC").generatePublic(X509EncodedKeySpec(key)) + } + } + + actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray { + return calculateShareKey(keyPair.privateKey, peerPublicKey) + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt new file mode 100644 index 000000000..f3ac85d1c --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt @@ -0,0 +1,51 @@ +package net.mamoe.mirai.utils.cryptor + +import net.mamoe.mirai.utils.io.chunkedHexToBytes +import net.mamoe.mirai.utils.io.toUHexString + +expect interface ECDHPrivateKey { + fun getEncoded(): ByteArray +} + +expect interface ECDHPublicKey { + fun getEncoded(): ByteArray +} + +expect class ECDHKeyPair { + val privateKey: ECDHPrivateKey + val publicKey: ECDHPublicKey + + val shareKey: ByteArray +} + +expect class ECDH(keyPair: ECDHKeyPair) { + fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray + + companion object { + fun constructPublicKey(key: ByteArray): ECDHPublicKey + fun generateKeyPair(): ECDHKeyPair + fun calculateShareKey(privateKey: ECDHPrivateKey, publicKey: ECDHPublicKey): ByteArray + } +} + +@Suppress("FunctionName") +expect fun ECDH(): ECDH + +internal val initialPublicKey = + ECDH.constructPublicKey("3046301006072A8648CE3D020106052B8104001F03320004928D8850673088B343264E0C6BACB8496D697799F37211DEB25BB73906CB089FEA9639B4E0260498B51A992D50813DA8".chunkedHexToBytes()) +private val commonHeadFor02 = "302E301006072A8648CE3D020106052B8104001F031A00".chunkedHexToBytes() +private val commonHeadForNot02 = "3046301006072A8648CE3D020106052B8104001F033200".chunkedHexToBytes() +private const val constantHead = "3046301006072A8648CE3D020106052B8104001F03320004" +private val byteArray_04 = byteArrayOf(0x04) + +fun ByteArray.adjustToPublicKey(): ECDHPublicKey { + val key = if (this[0].toInt() == 0x02) { // from server + commonHeadFor02 + this + } else if (!this.toUHexString("").startsWith(constantHead)) { + commonHeadForNot02 + + if (this[0].toInt() == 0x04) this + else (byteArray_04 + this) + } else this + + return ECDH.constructPublicKey(this) +} \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDH.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDH.kt new file mode 100644 index 000000000..fed0df562 --- /dev/null +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/ECDH.kt @@ -0,0 +1,48 @@ +package net.mamoe.mirai.utils.cryptor + +import net.mamoe.mirai.utils.md5 +import java.security.* +import java.security.spec.X509EncodedKeySpec +import javax.crypto.KeyAgreement + + +actual typealias ECDHPrivateKey = PrivateKey +actual typealias ECDHPublicKey = PublicKey + +actual class ECDHKeyPair( + private val delegate: KeyPair +) { + actual val privateKey: ECDHPrivateKey get() = delegate.private + actual val publicKey: ECDHPublicKey get() = delegate.public + + actual val shareKey: ByteArray = ECDH.calculateShareKey(privateKey, publicKey) +} + +@Suppress("FunctionName") +actual fun ECDH() = ECDH(ECDH.generateKeyPair()) + +actual class ECDH actual constructor(val keyPair: ECDHKeyPair) { + actual companion object { + actual fun generateKeyPair(): ECDHKeyPair { + return ECDHKeyPair(KeyPairGenerator.getInstance("ECDH").genKeyPair()) + } + + actual fun calculateShareKey( + privateKey: ECDHPrivateKey, + publicKey: ECDHPublicKey + ): ByteArray { + val instance = KeyAgreement.getInstance("ECDH", "BC") + instance.init(privateKey) + instance.doPhase(publicKey, true) + return md5(instance.generateSecret()) + } + + actual fun constructPublicKey(key: ByteArray): ECDHPublicKey { + return KeyFactory.getInstance("EC", "BC").generatePublic(X509EncodedKeySpec(key)) + } + } + + actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray { + return calculateShareKey(keyPair.privateKey, peerPublicKey) + } +} \ No newline at end of file diff --git a/mirai-debug/build.gradle.kts b/mirai-debug/build.gradle.kts index a13766129..bb3c47204 100644 --- a/mirai-debug/build.gradle.kts +++ b/mirai-debug/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":mirai-core-timpc")) // runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE - implementation("org.bouncycastle:bcprov-jdk15:1.46") + implementation("org.bouncycastle:bcprov-jdk15on:1.64") implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")