diff --git a/mirai-core/src/commonMain/kotlin/network/components/EcdhInitialPublicKeyUpdater.kt b/mirai-core/src/commonMain/kotlin/network/components/EcdhInitialPublicKeyUpdater.kt
index 77d9f3af4..3d57dc221 100644
--- a/mirai-core/src/commonMain/kotlin/network/components/EcdhInitialPublicKeyUpdater.kt
+++ b/mirai-core/src/commonMain/kotlin/network/components/EcdhInitialPublicKeyUpdater.kt
@@ -17,10 +17,14 @@ import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.network.component.ComponentKey
+import net.mamoe.mirai.internal.network.protocol.packet.createChannelProxy
+import net.mamoe.mirai.internal.spi.EncryptService
+import net.mamoe.mirai.internal.spi.EncryptServiceContext
 import net.mamoe.mirai.internal.utils.crypto.QQEcdh
 import net.mamoe.mirai.internal.utils.crypto.QQEcdhInitialPublicKey
 import net.mamoe.mirai.internal.utils.crypto.verify
 import net.mamoe.mirai.utils.MiraiLogger
+import net.mamoe.mirai.utils.buildTypeSafeMap
 import net.mamoe.mirai.utils.currentTimeSeconds
 import kotlin.time.Duration.Companion.seconds
 
@@ -34,6 +38,8 @@ internal interface EcdhInitialPublicKeyUpdater {
      */
     suspend fun refreshInitialPublicKeyAndApplyEcdh()
 
+    suspend fun initializeSsoSecureEcdh()
+
     fun getQQEcdh(): QQEcdh
 
     companion object : ComponentKey<EcdhInitialPublicKeyUpdater>
@@ -105,5 +111,18 @@ internal class EcdhInitialPublicKeyUpdaterImpl(
         qqEcdh = QQEcdh(initialPublicKey)
     }
 
+    override suspend fun initializeSsoSecureEcdh() {
+        val encryptWorker = EncryptService.instance
+
+        if (encryptWorker == null) {
+            logger.info("EncryptService SPI is not provided, sso secure ecdh will not be initialized.")
+            return
+        }
+
+        encryptWorker.initialize(EncryptServiceContext(bot.id, buildTypeSafeMap {
+            set(EncryptServiceContext.KEY_CHANNEL_PROXY, createChannelProxy(bot.client))
+        }))
+    }
+
 
 }
diff --git a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt
index 8f7ec0248..2b64d85a0 100644
--- a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt
+++ b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt
@@ -122,21 +122,25 @@ internal class PacketCodecImpl : PacketCodec {
 
             val raw = try {
                 when (encryptMethod) {
+                    // empty key
                     2 -> TEA.decrypt(buffer, DECRYPTER_16_ZERO, size)
-                1 -> {
-                    TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse {
-                        throw PacketCodecException(
-                            "Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal",
-                            PROTOCOL_UPDATED
-                        )
-                    }, size)
-                }
-
+                    // d2 key
+                    1 -> {
+                        TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse {
+                            throw PacketCodecException(
+                                "Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal",
+                                PROTOCOL_UPDATED
+                            )
+                        }, size)
+                    }
+                    // no encrypt
                     0 -> buffer
                     else -> throw PacketCodecException("Unknown encrypt type=$encryptMethod", PROTOCOL_UPDATED)
                 }.let { decryptedData ->
                     when (type) {
+                        // login
                         0x0A -> parseSsoFrame(client, decryptedData)
+                        // simple
                         0x0B -> parseSsoFrame(client, decryptedData) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
                         else -> throw PacketCodecException(
                             "unknown packet type: ${type.toByte().toUHexString()}",
@@ -171,7 +175,7 @@ internal class PacketCodecImpl : PacketCodec {
                     raw.sequenceId,
                     raw.body.withUse {
                         try {
-                                parseOicqResponse(client, raw.commandName)
+                            parseOicqResponse(client, raw.commandName)
                         } catch (e: Throwable) {
                             throw PacketCodecException(e, PacketCodecException.Kind.OTHER)
                         }
diff --git a/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt b/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt
index dafc7d02d..355ffcb24 100644
--- a/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt
+++ b/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt
@@ -39,7 +39,6 @@ import net.mamoe.mirai.network.*
 import net.mamoe.mirai.utils.*
 import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol
 import kotlin.coroutines.cancellation.CancellationException
-import kotlin.jvm.Volatile
 
 /**
  * Handles login, and acts also as a mediator of [BotInitProcessor]
@@ -213,8 +212,8 @@ internal open class SsoProcessorImpl(
             }
 
             components[CacheValidator].validate()
-
             components[BdhSessionSyncer].loadServerListFromCache()
+            components[EcdhInitialPublicKeyUpdater].initializeSsoSecureEcdh()
 
             try {
                 ssoContext.bot.requestQimei(qimeiLogger)
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SSOReserveField.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SSOReserveField.kt
new file mode 100644
index 000000000..467050106
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SSOReserveField.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019-2023 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.network.protocol.data.proto
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.protobuf.ProtoNumber
+import net.mamoe.mirai.internal.utils.io.ProtoBuf
+import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
+
+internal class SSOReserveField {
+    @Serializable
+    internal class ReserveFields(
+        @JvmField @ProtoNumber(1) val client_ipcookie: ByteArray = EMPTY_BYTE_ARRAY,
+        @JvmField @ProtoNumber(2) val flag: Int = 0,
+        @JvmField @ProtoNumber(3) val env_id: Int = 0,
+        @JvmField @ProtoNumber(4) val locale_id: Int = 0,
+        @JvmField @ProtoNumber(5) val qimei: ByteArray = EMPTY_BYTE_ARRAY,
+        @JvmField @ProtoNumber(6) val env: String = "",
+        @JvmField @ProtoNumber(7) val newconn_flag: Int = 0,
+        @JvmField @ProtoNumber(8) val trace_parent: String = "",
+        @JvmField @ProtoNumber(9) val uid: String = "",
+        @JvmField @ProtoNumber(10) val imsi: Int = 0,
+        @JvmField @ProtoNumber(11) val network_type: Int = 0,
+        @JvmField @ProtoNumber(12) val ip_stack_type: Int = 0,
+        @JvmField @ProtoNumber(13) val message_type: Int = 0,
+        @JvmField @ProtoNumber(14) val trpc_rsp: SsoTrpcResponse? = null,
+        @JvmField @ProtoNumber(15) val trans_info: List<SsoMapEntry>? = null,
+        @JvmField @ProtoNumber(16) val sec_info: SsoSecureInfo? = null,
+        @JvmField @ProtoNumber(17) val sec_sig_flag: Int = 0,
+        @JvmField @ProtoNumber(18) val nt_core_version: Int = 0,
+        @JvmField @ProtoNumber(19) val sso_route_cost: Int = 0,
+        @JvmField @ProtoNumber(20) val sso_ip_origin: Int = 0,
+        @JvmField @ProtoNumber(21) val presure_token: ByteArray = EMPTY_BYTE_ARRAY,
+    ) : ProtoBuf
+
+    @Serializable
+    internal class SsoSecureInfo(
+        @JvmField @ProtoNumber(1) val sec_sig: ByteArray = EMPTY_BYTE_ARRAY,
+        @JvmField @ProtoNumber(2) val sec_device_token: ByteArray = EMPTY_BYTE_ARRAY,
+        @JvmField @ProtoNumber(3) val sec_extra: ByteArray = EMPTY_BYTE_ARRAY,
+    ) : ProtoBuf
+
+    @Serializable
+    internal class SsoTrpcResponse(
+        @JvmField @ProtoNumber(1) val ret: Int = 0,
+        @JvmField @ProtoNumber(2) val func_ret: Int = 0,
+        @JvmField @ProtoNumber(3) val error_msg: ByteArray = EMPTY_BYTE_ARRAY,
+    ) : ProtoBuf
+
+    @Serializable
+    internal class SsoMapEntry(
+        @JvmField @ProtoNumber(1) val key: String = "",
+        @JvmField @ProtoNumber(2) val value: ByteArray = EMPTY_BYTE_ARRAY,
+    ) : ProtoBuf
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt
index f46bd6345..0f41833b7 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt
@@ -11,18 +11,19 @@ package net.mamoe.mirai.internal.network.protocol.packet
 
 
 import io.ktor.utils.io.core.*
-import net.mamoe.mirai.internal.network.Packet
-import net.mamoe.mirai.internal.network.QQAndroidClient
-import net.mamoe.mirai.internal.network.appClientVersion
+import kotlinx.serialization.encodeToByteArray
+import net.mamoe.mirai.internal.network.*
 import net.mamoe.mirai.internal.network.components.EcdhInitialPublicKeyUpdater
+import net.mamoe.mirai.internal.network.protocol.data.proto.SSOReserveField
+import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
+import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
+import net.mamoe.mirai.internal.spi.EncryptService
+import net.mamoe.mirai.internal.spi.EncryptServiceContext
 import net.mamoe.mirai.internal.utils.io.encryptAndWrite
 import net.mamoe.mirai.internal.utils.io.writeHex
 import net.mamoe.mirai.internal.utils.io.writeIntLVPacket
-import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
-import net.mamoe.mirai.utils.Either
+import net.mamoe.mirai.utils.*
 import net.mamoe.mirai.utils.Either.Companion.fold
-import net.mamoe.mirai.utils.KEY_16_ZEROS
-import net.mamoe.mirai.utils.TestOnly
 import kotlin.random.Random
 
 @Suppress("unused")
@@ -242,6 +243,37 @@ internal fun <R : Packet?> OutgoingPacketFactory<R>.buildLoginOutgoingPacket(
 
 private inline val BRP_STUB get() = ByteReadPacket.Empty
 
+internal fun createChannelProxy(client: QQAndroidClient): EncryptService.ChannelProxy {
+    return object : EncryptService.ChannelProxy {
+        override suspend fun sendMessage(
+            remark: String,
+            commandName: String,
+            uin: Long,
+            data: ByteArray
+        ): EncryptService.ChannelResult? {
+            if (commandName == "trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey") {
+                val packet = client.bot.network.sendAndExpect<Packet>(
+                    WtLogin.Login.buildLoginOutgoingPacket(
+                        client = client,
+                        encryptMethod = PacketEncryptType.Empty,
+                        uin = uin.toString(),
+                        remark = remark
+                    ) {
+                        writeSsoPacket(
+                            client,
+                            client.subAppId,
+                            sequenceId = it,
+                            commandName = commandName,
+                            body = { writeFully(data) }
+                        )
+                    }
+                )
+                TODO("parse packet to ChannelResult")
+            }
+            return null
+        }
+    }
+}
 
 internal inline fun BytePacketBuilder.writeSsoPacket(
     client: QQAndroidClient,
@@ -269,6 +301,42 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
      *
      * 00 00 00 04
      */
+    val encryptWorker = EncryptService.instance
+
+    val reserveField = if (
+        commandName.startsWith("wtlogin")
+        || commandName == MessageSvcPbSendMsg.commandName
+        || encryptWorker != null
+    ) {
+
+        val signResult = encryptWorker?.qSecurityGetSign(
+            EncryptServiceContext(client.uin, buildTypeSafeMap {
+                set(EncryptServiceContext.KEY_APP_QUA, "V1_AND_SQ_8.9.58_4106_YYB_D") // 8.9.58
+                set(EncryptServiceContext.KEY_CHANNEL_PROXY, createChannelProxy(client))
+            }),
+            sequenceId,
+            commandName,
+            buildPacket(body).readBytes()
+        )
+        if (signResult != null) ProtoBufForCache.encodeToByteArray(
+            SSOReserveField.ReserveFields(
+                flag = 0,
+                qimei = client.qimei16?.toByteArray() ?: EMPTY_BYTE_ARRAY,
+                newconn_flag = 0,
+                uid = client.uin.toString(),
+                imsi = 0,
+                network_type = 1,
+                ip_stack_type = 1,
+                message_type = 0,
+                sec_info = SSOReserveField.SsoSecureInfo(
+                    sec_sig = signResult.sign,
+                    sec_device_token = signResult.token,
+                    sec_extra = signResult.extra
+                )
+            )
+        ) else EMPTY_BYTE_ARRAY
+    } else EMPTY_BYTE_ARRAY
+
     writeIntLVPacket(lengthOffset = { it + 4 }) {
         writeInt(sequenceId)
         writeInt(subAppId.toInt())
@@ -281,27 +349,32 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
             writeInt((extraData.remaining + 4).toInt())
             writePacket(extraData)
         }
-        commandName.let {
-            writeInt(it.length + 4)
-            writeText(it)
-        }
 
-        writeInt(4 + 4)
+        writeInt(commandName.length + 4)
+        writeText(commandName)
+
+        writeInt(client.outgoingPacketSessionId.size + 4)
         writeFully(client.outgoingPacketSessionId) //  02 B0 5B 8B
 
-        client.device.imei.let {
-            writeInt(it.length + 4)
-            writeText(it)
+        if (commandName.startsWith("wtlogin")) {
+            writeText(client.device.imei)
+            writeInt(0x4)
+
+            writeShort((client.ksid.size + 2).toShort())
+            writeFully(client.ksid)
+
+            writeInt(reserveField.size + 4)
+            writeFully(reserveField)
         }
 
-        writeInt(4)
-
-        client.ksid.let {
-            writeShort((it.size + 2).toShort())
-            writeFully(it)
+        if (commandName == MessageSvcPbSendMsg.commandName && encryptWorker != null) {
+            writeInt(reserveField.size + 4)
+            writeFully(reserveField)
         }
 
-        writeInt(4)
+        val qimei16Bytes = client.qimei16?.toByteArray() ?: EMPTY_BYTE_ARRAY
+        writeInt(qimei16Bytes.size + 4)
+        writeFully(qimei16Bytes)
     }
 
     // body
diff --git a/mirai-core/src/commonMain/kotlin/spi/EncryptService.kt b/mirai-core/src/commonMain/kotlin/spi/EncryptService.kt
index a9c921b66..3c2657bb4 100644
--- a/mirai-core/src/commonMain/kotlin/spi/EncryptService.kt
+++ b/mirai-core/src/commonMain/kotlin/spi/EncryptService.kt
@@ -13,10 +13,7 @@ package net.mamoe.mirai.internal.spi
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.spi.BaseService
 import net.mamoe.mirai.spi.SpiServiceLoader
-import net.mamoe.mirai.utils.BotConfiguration
-import net.mamoe.mirai.utils.MiraiInternalApi
-import net.mamoe.mirai.utils.TypeKey
-import net.mamoe.mirai.utils.TypeSafeMap
+import net.mamoe.mirai.utils.*
 
 
 /**
@@ -32,6 +29,8 @@ public class EncryptServiceContext @MiraiInternalApi constructor(
     public companion object {
         public val KEY_COMMAND_STR: TypeKey<String> = TypeKey("KEY_COMMAND_STR")
         public val KEY_BOT_PROTOCOL: TypeKey<BotConfiguration.MiraiProtocol> = TypeKey("BOT_PROTOCOL")
+        public val KEY_APP_QUA: TypeKey<String> = TypeKey("KEY_APP_QUA")
+        public val KEY_CHANNEL_PROXY: TypeKey<EncryptService.ChannelProxy> = TypeKey("KEY_CHANNEL_PROXY")
     }
 }
 
@@ -39,6 +38,7 @@ public class EncryptServiceContext @MiraiInternalApi constructor(
  * @since 2.15.0
  */
 public interface EncryptService : BaseService {
+    public fun initialize(context: EncryptServiceContext)
 
     /**
      * Returns `false` if not supported.
@@ -56,9 +56,34 @@ public interface EncryptService : BaseService {
         payload: ByteArray, // Do not write to payload
     ): ByteArray?
 
+    public fun qSecurityGetSign(
+        context: EncryptServiceContext,
+        sequenceId: Int,
+        commandName: String,
+        payload: ByteArray
+    ): SignResult?
+
+    public class SignResult(
+        public val sign: ByteArray = EMPTY_BYTE_ARRAY,
+        public val token: ByteArray = EMPTY_BYTE_ARRAY,
+        public val extra: ByteArray = EMPTY_BYTE_ARRAY,
+    )
+
+    public class ChannelResult(
+        public val cmd: String,
+        public val data: ByteArray,
+        public val success: Int,
+        public val callbackId: Long
+    )
+
+    public interface ChannelProxy {
+        public suspend fun sendMessage(remark: String, commandName: String, uin: Long, data: ByteArray): ChannelResult?
+    }
+
     public companion object {
         private val loader = SpiServiceLoader(EncryptService::class)
 
         internal val instance: EncryptService? get() = loader.service
     }
+
 }
diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt
index 0312bf5e3..47cdd8946 100644
--- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt
+++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt
@@ -180,6 +180,9 @@ internal abstract class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abs
             override suspend fun refreshInitialPublicKeyAndApplyEcdh() {
             }
 
+            override suspend fun initializeSsoSecureEcdh() {
+            }
+
             override fun getQQEcdh(): QQEcdh = QQEcdh()
         })