Commit d3c97fb6 authored by Him188's avatar Him188

Add ECDH

parent 101d655e
...@@ -6,6 +6,7 @@ import kotlinx.io.core.toByteArray ...@@ -6,6 +6,7 @@ import kotlinx.io.core.toByteArray
import net.mamoe.mirai.BotAccount import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.qqandroid.utils.* import net.mamoe.mirai.qqandroid.utils.*
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.ECDH
import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
/* /*
...@@ -27,7 +28,7 @@ internal open class QQAndroidClient( ...@@ -27,7 +28,7 @@ internal open class QQAndroidClient(
val context: Context, val context: Context,
val account: BotAccount, val account: BotAccount,
val ecdh: ECDH = ECDH.Default, val ecdh: ECDH = ECDH(),
val device: DeviceInfo = SystemDeviceInfo(context) val device: DeviceInfo = SystemDeviceInfo(context)
) { ) {
val tgtgtKey: ByteArray = generateTgtgtKey(device.guid) val tgtgtKey: ByteArray = generateTgtgtKey(device.guid)
......
package net.mamoe.mirai.qqandroid.utils package net.mamoe.mirai.qqandroid.utils
import net.mamoe.mirai.utils.io.chunkedHexToBytes
/** /**
* From QQAndroid 8.2.0 * From QQAndroid 8.2.0
* `oicq.wlogin_sdk.tools.EcdhCrypt` * `oicq.wlogin_sdk.tools.EcdhCrypt`
...@@ -9,11 +7,12 @@ import net.mamoe.mirai.utils.io.chunkedHexToBytes ...@@ -9,11 +7,12 @@ import net.mamoe.mirai.utils.io.chunkedHexToBytes
* Constant to avoid calculations * Constant to avoid calculations
*/ */
interface ECDH { interface ECDH {
/*
object Default : ECDH { object Default : ECDH {
override val publicKey: ByteArray = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes() override val publicKey: ByteArray = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes()
override val shareKey: ByteArray = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes() override val shareKey: ByteArray = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
override val privateKey: ByteArray = ByteArray(16) override val privateKey: ByteArray = ByteArray(16)
} }*/
val publicKey: ByteArray val publicKey: ByteArray
......
...@@ -136,6 +136,7 @@ kotlin { ...@@ -136,6 +136,7 @@ kotlin {
api(kotlinx("io-jvm", kotlinXIoVersion)) api(kotlinx("io-jvm", kotlinXIoVersion))
api(kotlinx("serialization-runtime", serializationVersion)) 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 runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
} }
} }
......
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
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
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
...@@ -45,7 +45,7 @@ dependencies { ...@@ -45,7 +45,7 @@ dependencies {
implementation(project(":mirai-core-timpc")) implementation(project(":mirai-core-timpc"))
// runtimeOnly(files("../mirai-core/build/classes/kotlin/jvm/main")) // classpath is not added correctly by IDE // 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") implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment