first commit
This commit is contained in:
117
clientsdk/mmtls/mmcrypto.go
Normal file
117
clientsdk/mmtls/mmcrypto.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
var verifyPubKey = []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8uOhBSSfVijKin+SZO/0IXUrmf8l
|
||||
9sa7VgqOIH/AO3Xb1MF4Xm25bBSb5znHsknQsNPSye3vVo80NUi2gEHw8g==
|
||||
-----END PUBLIC KEY-----`)
|
||||
|
||||
// Sha256 Sha256
|
||||
func Sha256(data []byte) []byte {
|
||||
hash256 := sha256.New()
|
||||
hash256.Write(data)
|
||||
hashRet := hash256.Sum(nil)
|
||||
|
||||
return hashRet
|
||||
}
|
||||
|
||||
// HmacHash256 HmacHash256
|
||||
func HmacHash256(key []byte, data []byte) []byte {
|
||||
hmacTool := hmac.New(sha256.New, key)
|
||||
hmacTool.Write(data)
|
||||
return hmacTool.Sum(nil)
|
||||
}
|
||||
|
||||
// HkdfExpand HkdfExpand
|
||||
func HkdfExpand(key, message []byte, outLen int) []byte {
|
||||
result := make([]byte, 0)
|
||||
count := outLen / 32
|
||||
if outLen%32 != 0 {
|
||||
count = count + 1
|
||||
}
|
||||
for i := 1; i <= count; i++ {
|
||||
h := hmac.New(sha256.New, key)
|
||||
tmp := append(message, byte(i))
|
||||
tmp = append(result, tmp...)
|
||||
h.Write(tmp)
|
||||
result = append(result, h.Sum(nil)...)
|
||||
}
|
||||
return result[:outLen]
|
||||
}
|
||||
|
||||
// AesGcmEncrypt AesGcmEncrypt
|
||||
func AesGcmEncrypt(key, nonce, aad, data []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return []byte{}, nil
|
||||
}
|
||||
ciphertext := aesgcm.Seal(nil, nonce, data, aad)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// AesGcmDecrypt AesGcmDecrypt
|
||||
func AesGcmDecrypt(key, nonce, aad, data []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plain, err := aesgcm.Open(nil, nonce, data, aad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plain, nil
|
||||
}
|
||||
|
||||
// GetNonce GetNonce
|
||||
func GetNonce(data []byte, seq uint32) []byte {
|
||||
ret := make([]byte, len(data))
|
||||
copy(ret, data)
|
||||
seqBytes := baseutils.Int32ToBytes(seq)
|
||||
baseOffset := 8
|
||||
for index := 0; index < 4; index++ {
|
||||
ret[baseOffset+index] = ret[baseOffset+index] ^ byte(seqBytes[index])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ECDSAVerifyData 校验服务端握手数据
|
||||
func ECDSAVerifyData(message []byte, signature []byte) (bool, error) {
|
||||
block, _ := pem.Decode(verifyPubKey)
|
||||
publicStream, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
//接口转换成公钥
|
||||
publicKey := publicStream.(*ecdsa.PublicKey)
|
||||
|
||||
// 反序列化ecdsaSignature
|
||||
ecdsaSignature := &EcdsaSignature{}
|
||||
_, err = asn1.Unmarshal(signature, ecdsaSignature)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
flag := ecdsa.Verify(publicKey, Sha256(message), ecdsaSignature.R, ecdsaSignature.S)
|
||||
|
||||
return flag, nil
|
||||
}
|
||||
271
clientsdk/mmtls/mmdatapack.go
Normal file
271
clientsdk/mmtls/mmdatapack.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
// RecordHeadSerialize 序列化RecordHead
|
||||
func RecordHeadSerialize(recordHead *RecordHead) []byte {
|
||||
retBytes := make([]byte, 0)
|
||||
// Type
|
||||
retBytes = append(retBytes, recordHead.Type)
|
||||
// Tag
|
||||
retBytes = append(retBytes, baseutils.Int16ToBytesBigEndian(recordHead.Tag)[0:]...)
|
||||
// size
|
||||
retBytes = append(retBytes, baseutils.Int16ToBytesBigEndian(recordHead.Size)[0:]...)
|
||||
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// ClientHelloSerialize 序列化ClientHello
|
||||
func ClientHelloSerialize(clientHello *ClientHello) []byte {
|
||||
bodyData := make([]byte, 0)
|
||||
// Type
|
||||
bodyData = append(bodyData, ClientHelloType)
|
||||
// Version
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesLittleEndian(clientHello.Version)[0:]...)
|
||||
// suiteCount
|
||||
suiteCount := byte(len(clientHello.CipherSuiteList))
|
||||
bodyData = append(bodyData, suiteCount)
|
||||
// suiteList
|
||||
suiteList := clientHello.CipherSuiteList
|
||||
for index := 0; index < int(suiteCount); index++ {
|
||||
// suiteCode
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(suiteList[index].SuiteCode)[0:]...)
|
||||
}
|
||||
// RandomBytes
|
||||
bodyData = append(bodyData, clientHello.RandomBytes[0:]...)
|
||||
// ClientGmtTime
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(clientHello.ClientGmtTime)[0:]...)
|
||||
// Extensions
|
||||
extensionsData := ExtensionsSerialize(clientHello.ExtensionList)
|
||||
bodyData = append(bodyData, extensionsData[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retBytes := make([]byte, 0)
|
||||
totalLength := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(totalLength)[0:]...)
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// ExtensionsSerialize 序列化Extensions
|
||||
func ExtensionsSerialize(extensionList []*Extension) []byte {
|
||||
retBytes := make([]byte, 0)
|
||||
|
||||
// bodyData
|
||||
bodyData := make([]byte, 0)
|
||||
// MapSize
|
||||
extensionCount := byte(len(extensionList))
|
||||
bodyData = append(bodyData, extensionCount)
|
||||
for index := 0; index < int(extensionCount); index++ {
|
||||
// Extension TotalLength
|
||||
extensionLength := uint32(len(extensionList[index].ExtensionData))
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(extensionLength)[0:]...)
|
||||
// extensionData
|
||||
bodyData = append(bodyData, extensionList[index].ExtensionData[0:]...)
|
||||
}
|
||||
|
||||
// Extensions Size
|
||||
extensionsSize := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(extensionsSize)[0:]...)
|
||||
|
||||
// ExtensionsData
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// PskSerialize 序列化Psk
|
||||
func PskSerialize(psk *Psk) []byte {
|
||||
// BodyData
|
||||
bodyData := make([]byte, 0)
|
||||
// Type
|
||||
bodyData = append(bodyData, psk.Type)
|
||||
// TicketLifeTimeHint
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(psk.TicketKLifeTimeHint)[0:]...)
|
||||
// MacValue
|
||||
macValueLen := uint16(len(psk.MacValue))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(macValueLen)[0:]...)
|
||||
bodyData = append(bodyData, psk.MacValue[0:]...)
|
||||
// KeyVersion
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(psk.KeyVersion)[0:]...)
|
||||
// IV
|
||||
ivLen := uint16(len(psk.Iv))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(ivLen)[0:]...)
|
||||
bodyData = append(bodyData, psk.Iv[0:]...)
|
||||
// EncryptTicket
|
||||
encryptTicketLen := uint16(len(psk.EncryptedTicket))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(encryptTicketLen)[0:]...)
|
||||
bodyData = append(bodyData, psk.EncryptedTicket[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retBytes := make([]byte, 0)
|
||||
bodyLen := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(bodyLen)[0:]...)
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// ClientKeyOfferSerialize 序列化ClientKeyOffer
|
||||
func ClientKeyOfferSerialize(clientKeyOffer *ClientKeyOffer) []byte {
|
||||
// BodyData
|
||||
bodyData := make([]byte, 0)
|
||||
// Version
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(clientKeyOffer.Version)[0:]...)
|
||||
// PublicValue
|
||||
publicValueLen := uint16(len(clientKeyOffer.PublicValue))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(publicValueLen)[0:]...)
|
||||
if publicValueLen > 0 {
|
||||
bodyData = append(bodyData, clientKeyOffer.PublicValue[0:]...)
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
retBytes := make([]byte, 0)
|
||||
bodyDataLen := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(bodyDataLen)[0:]...)
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// EncryptedExtensionsSerialize 序列化EncryptedExtensions
|
||||
func EncryptedExtensionsSerialize(encryptedExtensions *EncryptedExtensions) []byte {
|
||||
bodyData := make([]byte, 0)
|
||||
// Type
|
||||
bodyData = append(bodyData, EncryptedExtensionsType)
|
||||
// ExtensionList
|
||||
extensionsData := ExtensionsSerialize(encryptedExtensions.ExtensionList)
|
||||
bodyData = append(bodyData, extensionsData[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retBytes := make([]byte, 0)
|
||||
bodyDataLen := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(bodyDataLen)[0:]...)
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// HTTPHandlerSerialize 序列化HTTPHandler
|
||||
func HTTPHandlerSerialize(httpHandler *HTTPHandler) []byte {
|
||||
bodyData := make([]byte, 0)
|
||||
// URL
|
||||
urlLength := uint16(len(httpHandler.URL))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(urlLength)[0:]...)
|
||||
bodyData = append(bodyData, []byte(httpHandler.URL)[0:]...)
|
||||
|
||||
// Host
|
||||
hostLength := uint16(len(httpHandler.Host))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(hostLength)[0:]...)
|
||||
bodyData = append(bodyData, []byte(httpHandler.Host)[0:]...)
|
||||
|
||||
// MMPkg
|
||||
mmpkgLength := uint32(len(httpHandler.MMPkg))
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(mmpkgLength)[0:]...)
|
||||
bodyData = append(bodyData, httpHandler.MMPkg[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retBytes := make([]byte, 0)
|
||||
bodyDataLen := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(bodyDataLen)[0:]...)
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// FinishedSerialize 序列化Finished包
|
||||
func FinishedSerialize(finished *Finished) []byte {
|
||||
bodyData := make([]byte, 0)
|
||||
// Type
|
||||
bodyData = append(bodyData, FinishedType)
|
||||
// VerifyData
|
||||
verifyDataLen := uint16(uint32(len(finished.VerifyData)))
|
||||
bodyData = append(bodyData, baseutils.Int16ToBytesBigEndian(verifyDataLen)[0:]...)
|
||||
bodyData = append(bodyData, finished.VerifyData[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retBytes := make([]byte, 0)
|
||||
bodyDataLen := uint32(len(bodyData))
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(bodyDataLen)[0:]...)
|
||||
retBytes = append(retBytes, bodyData[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// --------------- 各类Extension的序列化 ---------------
|
||||
|
||||
// PreSharedKeyExtensionSerialize 序列化PreSharedKeyExtension
|
||||
func PreSharedKeyExtensionSerialize(preSharedKeyExtension *PreSharedKeyExtension) *Extension {
|
||||
// ExtensionBytes
|
||||
extensionBytes := make([]byte, 0)
|
||||
// PreSharedKeyExtensionType
|
||||
extensionBytes = append(extensionBytes, baseutils.Int16ToBytesBigEndian(PreSharedKeyExtensionType)[0:]...)
|
||||
// pskCount
|
||||
pskCount := byte(len(preSharedKeyExtension.PskList))
|
||||
extensionBytes = append(extensionBytes, pskCount)
|
||||
// PskList
|
||||
for index := 0; index < int(pskCount); index++ {
|
||||
psk := preSharedKeyExtension.PskList[index]
|
||||
pskData := PskSerialize(psk)
|
||||
extensionBytes = append(extensionBytes, pskData[0:]...)
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
retExtension := &Extension{}
|
||||
retExtension.ExtensionType = PreSharedKeyExtensionType
|
||||
retExtension.ExtensionData = extensionBytes
|
||||
return retExtension
|
||||
}
|
||||
|
||||
// ClientKeyShareExtensionSerialize 序列化ClientKeyShareExtension
|
||||
func ClientKeyShareExtensionSerialize(clientKeyShareExtension *ClientKeyShareExtension) *Extension {
|
||||
// ExtensionBytes
|
||||
extensionBytes := make([]byte, 0)
|
||||
// ClientKeyShareType
|
||||
extensionBytes = append(extensionBytes, baseutils.Int16ToBytesBigEndian(ClientKeyShareType)[0:]...)
|
||||
// KeyOfferCount
|
||||
keyOfferCount := byte(len(clientKeyShareExtension.ClientKeyOfferList))
|
||||
extensionBytes = append(extensionBytes, keyOfferCount)
|
||||
// KeyOfferList
|
||||
for index := 0; index < int(keyOfferCount); index++ {
|
||||
keyOffer := clientKeyShareExtension.ClientKeyOfferList[index]
|
||||
keyOfferData := ClientKeyOfferSerialize(keyOffer)
|
||||
extensionBytes = append(extensionBytes, keyOfferData[0:]...)
|
||||
}
|
||||
// CertificateVersion
|
||||
extensionBytes = append(extensionBytes, baseutils.Int32ToBytes(clientKeyShareExtension.CertificateVersion)[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retExtension := &Extension{}
|
||||
retExtension.ExtensionType = ClientKeyShareType
|
||||
retExtension.ExtensionData = extensionBytes
|
||||
return retExtension
|
||||
}
|
||||
|
||||
// EarlyEncryptDataExtensionSerialize 序列化EarlyEncryptDataExtension
|
||||
func EarlyEncryptDataExtensionSerialize(earlyEncryptDataExtension *EarlyEncryptDataExtension) *Extension {
|
||||
// ExtensionBytes
|
||||
extensionBytes := make([]byte, 0)
|
||||
// ClientKeyShareType
|
||||
extensionBytes = append(extensionBytes, baseutils.Int16ToBytesBigEndian(EarlyEncryptDataType)[0:]...)
|
||||
// ClientGmtTime
|
||||
extensionBytes = append(extensionBytes, baseutils.Int32ToBytes(earlyEncryptDataExtension.ClientGmtTime)[0:]...)
|
||||
|
||||
// 返回数据
|
||||
retExtension := &Extension{}
|
||||
retExtension.ExtensionType = EarlyEncryptDataType
|
||||
retExtension.ExtensionData = extensionBytes
|
||||
return retExtension
|
||||
}
|
||||
|
||||
// LongPackHeaderInfoSerialize 序列化LongPackHeaderInfo
|
||||
func LongPackHeaderInfoSerialize(packTagInfo *LongPackHeaderInfo) []byte {
|
||||
retBytes := make([]byte, 0)
|
||||
// Type
|
||||
retBytes = append(retBytes, baseutils.Int16ToBytesBigEndian(packTagInfo.HeaderLen)[0:]...)
|
||||
// Version
|
||||
retBytes = append(retBytes, baseutils.Int16ToBytesBigEndian(packTagInfo.Version)[0:]...)
|
||||
// Operation
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(packTagInfo.Operation)[0:]...)
|
||||
// SequenceNumber
|
||||
retBytes = append(retBytes, baseutils.Int32ToBytes(packTagInfo.SequenceNumber)[0:]...)
|
||||
|
||||
return retBytes
|
||||
}
|
||||
555
clientsdk/mmtls/mmdataunpack.go
Normal file
555
clientsdk/mmtls/mmdataunpack.go
Normal file
@@ -0,0 +1,555 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
// DataPackerDeserialize 反序列化数据
|
||||
func DataPackerDeserialize(data []byte) {
|
||||
totalLength := uint32(len(data))
|
||||
current := uint32(0)
|
||||
|
||||
for current < totalLength {
|
||||
// recordHead
|
||||
recordHead := RecordHeadDeSerialize(data[current:])
|
||||
baseutils.ShowObjectValue(recordHead)
|
||||
current = current + 5
|
||||
offset := uint32(0)
|
||||
// tmpType
|
||||
pkgSize := baseutils.BytesToInt32(data[current+offset : current+offset+4])
|
||||
offset = offset + 4
|
||||
tmpType := data[current+offset]
|
||||
|
||||
// ClientHelloType
|
||||
if tmpType == ClientHelloType {
|
||||
clientHello, _ := ClientHelloDeSerialize(data[current+offset : current+offset+pkgSize])
|
||||
baseutils.ShowObjectValue(clientHello)
|
||||
ShowMMTLSExtensions(clientHello.ExtensionList)
|
||||
}
|
||||
|
||||
// ServerHelloType
|
||||
if tmpType == ServerHelloType {
|
||||
serverHello, _ := ServerHelloDeSerialize(data[current+offset : current+offset+pkgSize])
|
||||
baseutils.ShowObjectValue(serverHello)
|
||||
ShowMMTLSExtensions(serverHello.ExtensionList)
|
||||
}
|
||||
offset = offset + pkgSize
|
||||
current = current + offset
|
||||
}
|
||||
}
|
||||
|
||||
// GetCipherSuiteInfoByCode 返回code对应的 CipherSuiteInfo
|
||||
func GetCipherSuiteInfoByCode(code uint16) *CipherSuite {
|
||||
if code == 0xc02b {
|
||||
cipherSuite := &CipherSuite{}
|
||||
// SuiteCode
|
||||
cipherSuite.SuiteCode = code
|
||||
|
||||
// cipherSuiteInfo
|
||||
cipherSuiteInfo := &CipherSuiteInfo{}
|
||||
cipherSuiteInfo.SuiteCode = code
|
||||
cipherSuiteInfo.Clipher1 = "ECDHE"
|
||||
cipherSuiteInfo.Clipher2 = "ECDSA"
|
||||
cipherSuiteInfo.Clipher3 = "SHA256"
|
||||
cipherSuiteInfo.Clipher4 = "AES_128_GCM"
|
||||
cipherSuiteInfo.Clipher5 = "AEAD"
|
||||
cipherSuiteInfo.Length1 = 16
|
||||
cipherSuiteInfo.Length2 = 0
|
||||
cipherSuiteInfo.Length3 = 12
|
||||
cipherSuite.SuiteInfo = cipherSuiteInfo
|
||||
return cipherSuite
|
||||
}
|
||||
|
||||
if code == 0x00a8 {
|
||||
cipherSuite := &CipherSuite{}
|
||||
// SuiteCode
|
||||
cipherSuite.SuiteCode = code
|
||||
|
||||
// cipherSuiteInfo
|
||||
cipherSuiteInfo := &CipherSuiteInfo{}
|
||||
cipherSuiteInfo.SuiteCode = code
|
||||
cipherSuiteInfo.Clipher1 = "PSK"
|
||||
cipherSuiteInfo.Clipher2 = "ECDSA"
|
||||
cipherSuiteInfo.Clipher3 = "SHA256"
|
||||
cipherSuiteInfo.Clipher4 = "AES_128_GCM"
|
||||
cipherSuiteInfo.Clipher5 = "AEAD"
|
||||
cipherSuiteInfo.Length1 = 16
|
||||
cipherSuiteInfo.Length2 = 0
|
||||
cipherSuiteInfo.Length3 = 12
|
||||
cipherSuite.SuiteInfo = cipherSuiteInfo
|
||||
return cipherSuite
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordHeadDeSerialize 反序列化RecordHead
|
||||
func RecordHeadDeSerialize(data []byte) *RecordHead {
|
||||
retRecordHead := &RecordHead{}
|
||||
// 偏移
|
||||
current := uint32(0)
|
||||
|
||||
// Type
|
||||
retRecordHead.Type = data[current]
|
||||
current = current + 1
|
||||
|
||||
// Tag
|
||||
retRecordHead.Tag = baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
|
||||
// Size
|
||||
retRecordHead.Size = baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
|
||||
return retRecordHead
|
||||
}
|
||||
|
||||
// ClientHelloDeSerialize 反序列化ClientHello
|
||||
func ClientHelloDeSerialize(data []byte) (*ClientHello, error) {
|
||||
current := uint32(0)
|
||||
clientHello := &ClientHello{}
|
||||
|
||||
tmptype := data[current]
|
||||
if tmptype != ClientHelloType {
|
||||
return nil, errors.New("ClientHelloDeSerialize err: not ClientHelloType")
|
||||
}
|
||||
current = current + 1
|
||||
|
||||
// Version
|
||||
clientHello.Version = baseutils.BytesToUint16LittleEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
|
||||
// ciphersuiteSize
|
||||
ciphersuiteSize := data[current]
|
||||
current = current + 1
|
||||
|
||||
// CipherSuiteList
|
||||
clientHello.CipherSuiteList = make([]*CipherSuite, ciphersuiteSize)
|
||||
for index := 0; index < int(ciphersuiteSize); index++ {
|
||||
suitecode := baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
clientHello.CipherSuiteList[index] = GetCipherSuiteInfoByCode(suitecode)
|
||||
}
|
||||
|
||||
// RandomBytes
|
||||
clientHello.RandomBytes = data[current : current+32]
|
||||
current = current + 32
|
||||
|
||||
// ClientGmtTime
|
||||
clientHello.ClientGmtTime = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// ExtensionList
|
||||
clientHello.ExtensionList = ExtensionsDeSerialize(data[current:])
|
||||
|
||||
return clientHello, nil
|
||||
}
|
||||
|
||||
// ExtensionsDeSerialize 反序列号Extensions
|
||||
func ExtensionsDeSerialize(data []byte) []*Extension {
|
||||
// 初始化返回数组
|
||||
retExtensions := make([]*Extension, 0)
|
||||
|
||||
// 初始化索引
|
||||
current := uint32(0)
|
||||
|
||||
// totalLength
|
||||
totalLength := baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// extensionCount := data[current]
|
||||
current = current + 1
|
||||
// ExtensionList
|
||||
for current-4 < totalLength {
|
||||
extension := &Extension{}
|
||||
|
||||
// ExtensionData
|
||||
extensionLength := baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// ExtensionType
|
||||
extension.ExtensionType = baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
|
||||
// ExtensionData
|
||||
extension.ExtensionData = data[current : current+extensionLength]
|
||||
|
||||
// 放入列表
|
||||
retExtensions = append(retExtensions, extension)
|
||||
current = current + extensionLength
|
||||
}
|
||||
|
||||
return retExtensions
|
||||
}
|
||||
|
||||
// PskDeSerialize 反序列化Psk
|
||||
func PskDeSerialize(data []byte) (*Psk, error) {
|
||||
tmpPsk := &Psk{}
|
||||
|
||||
// current
|
||||
current := uint32(0)
|
||||
|
||||
// Type
|
||||
tmpPsk.Type = data[current]
|
||||
current = current + 1
|
||||
|
||||
// TicketKLifeTimeHint
|
||||
tmpPsk.TicketKLifeTimeHint = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// MacValue
|
||||
macValueLength := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
tmpPsk.MacValue = data[current : current+macValueLength]
|
||||
current = current + macValueLength
|
||||
|
||||
// KeyVersion
|
||||
tmpPsk.KeyVersion = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// IV
|
||||
ivLength := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
tmpPsk.Iv = data[current : current+ivLength]
|
||||
current = current + ivLength
|
||||
|
||||
// EncryptedTicket
|
||||
encryptedTicketLength := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
tmpPsk.EncryptedTicket = data[current : current+encryptedTicketLength]
|
||||
current = current + encryptedTicketLength
|
||||
if current != uint32(len(data)) {
|
||||
return nil, errors.New("PskDeSerialize err: current - startPos != pskTotalLength")
|
||||
}
|
||||
|
||||
return tmpPsk, nil
|
||||
}
|
||||
|
||||
// EncryptedExtensionsDeSerialize 反序列化EncryptedExtensions
|
||||
func EncryptedExtensionsDeSerialize(data []byte) (*EncryptedExtensions, error) {
|
||||
current := uint32(0)
|
||||
retEncryptedExtensions := &EncryptedExtensions{}
|
||||
|
||||
// Type
|
||||
tmptype := data[current]
|
||||
if tmptype != EncryptedExtensionsType {
|
||||
return nil, errors.New("EncryptedExtensionsDeSerialize err: not ServerHelloType")
|
||||
}
|
||||
current = current + 1
|
||||
|
||||
// ExtensionList
|
||||
retEncryptedExtensions.ExtensionList = ExtensionsDeSerialize(data[current:])
|
||||
|
||||
return retEncryptedExtensions, nil
|
||||
}
|
||||
|
||||
// ServerHelloDeSerialize 反序列化ServerHello
|
||||
func ServerHelloDeSerialize(data []byte) (*ServerHello, error) {
|
||||
current := uint32(0)
|
||||
serverHello := &ServerHello{}
|
||||
|
||||
tmptype := data[current]
|
||||
if tmptype != ServerHelloType {
|
||||
return nil, errors.New("ServerHelloDeSerialize err: not ServerHelloType")
|
||||
}
|
||||
current = current + 1
|
||||
|
||||
// Version
|
||||
serverHello.Version = baseutils.BytesToUint16LittleEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
|
||||
// CipherSuite
|
||||
suiteCode := baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
serverHello.CipherSuite = GetCipherSuiteInfoByCode(suiteCode)
|
||||
|
||||
// RandomBytes
|
||||
serverHello.RandomBytes = data[current : current+32]
|
||||
current = current + 32
|
||||
|
||||
// ExtensionList
|
||||
serverHello.ExtensionList = ExtensionsDeSerialize(data[current:])
|
||||
|
||||
return serverHello, nil
|
||||
}
|
||||
|
||||
// NewSessionTicketDeSerialize 反序列化NewSessionTicket
|
||||
func NewSessionTicketDeSerialize(data []byte) (*NewSessionTicket, error) {
|
||||
retNewSessionTicket := &NewSessionTicket{}
|
||||
|
||||
// current
|
||||
current := uint32(0)
|
||||
|
||||
// tmpType
|
||||
tmpType := data[current]
|
||||
if tmpType != NewSessionTicketType {
|
||||
return nil, errors.New("NewSessionTicketDeSerialize err: not NewSessionTicketType")
|
||||
}
|
||||
current = current + 1
|
||||
|
||||
// pskListSize
|
||||
pskListSize := data[current]
|
||||
current = current + 1
|
||||
|
||||
// PskList
|
||||
retNewSessionTicket.PskList = make([]*Psk, pskListSize)
|
||||
for index := 0; index < int(pskListSize); index++ {
|
||||
// pskTotalLength
|
||||
pskTotalLength := baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// PskDeSerialize
|
||||
retPsk, err := PskDeSerialize(data[current : current+pskTotalLength])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add to PskList
|
||||
retNewSessionTicket.PskList[index] = retPsk
|
||||
current = current + pskTotalLength
|
||||
}
|
||||
|
||||
return retNewSessionTicket, nil
|
||||
}
|
||||
|
||||
// CertificateVerifyDeSerialize CertificateVerifyDeSerialize
|
||||
func CertificateVerifyDeSerialize(data []byte) (*CertificateVerify, error) {
|
||||
retCertificateVerify := &CertificateVerify{}
|
||||
|
||||
// current
|
||||
current := uint32(0)
|
||||
|
||||
// tmpType
|
||||
tmpType := data[current]
|
||||
if tmpType != CertificateVerifyType {
|
||||
return nil, errors.New("CertificateVerifyDeSerialize err: not CertificateVerifyType")
|
||||
}
|
||||
current = current + 1
|
||||
|
||||
// SignatureSize
|
||||
size := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
|
||||
// Signature
|
||||
retCertificateVerify.Signature = data[current : current+size]
|
||||
current = current + size
|
||||
|
||||
// 判断数据是否完整解析
|
||||
if current != uint32(len(data)) {
|
||||
return nil, errors.New("CertificateVerifyDeSerialize err: current != uint32(len(data)")
|
||||
}
|
||||
|
||||
return retCertificateVerify, nil
|
||||
}
|
||||
|
||||
// HTTPHandlerDeSerialize 反序列化HttpHandler
|
||||
func HTTPHandlerDeSerialize(data []byte) (*HTTPHandler, error) {
|
||||
retHTTPHandler := &HTTPHandler{}
|
||||
|
||||
current := 0
|
||||
// URL
|
||||
urlLength := int(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
if urlLength > 0 {
|
||||
retHTTPHandler.URL = string(data[current : current+urlLength])
|
||||
current = current + int(urlLength)
|
||||
}
|
||||
|
||||
// Host
|
||||
hostLength := int(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
if urlLength > 0 {
|
||||
retHTTPHandler.Host = string(data[current : current+hostLength])
|
||||
current = current + int(hostLength)
|
||||
}
|
||||
|
||||
// MMPkg
|
||||
mmpkgLength := int(baseutils.BytesToInt32(data[current : current+4]))
|
||||
current = current + 4
|
||||
if mmpkgLength > 0 {
|
||||
retHTTPHandler.MMPkg = data[current : current+int(mmpkgLength)]
|
||||
current = current + mmpkgLength
|
||||
}
|
||||
|
||||
// 判断数据是否正常完整解析
|
||||
if current != len(data) {
|
||||
return nil, errors.New("HTTPHandlerDeSerialize err: current != len(data)")
|
||||
}
|
||||
|
||||
return retHTTPHandler, nil
|
||||
}
|
||||
|
||||
// FinishedDeSerialize 反序列化 Finished
|
||||
func FinishedDeSerialize(data []byte) (*Finished, error) {
|
||||
retFinished := &Finished{}
|
||||
|
||||
// current
|
||||
current := uint32(0)
|
||||
|
||||
// tmpType
|
||||
tmpType := data[current]
|
||||
if tmpType != FinishedType {
|
||||
return nil, errors.New("FinishedDeSerialize err: not FinishedType")
|
||||
}
|
||||
current = current + 1
|
||||
|
||||
// VerifyData
|
||||
verifyDataLen := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
retFinished.VerifyData = data[current : current+verifyDataLen]
|
||||
current = current + verifyDataLen
|
||||
|
||||
// 判断数据是否完整解析
|
||||
if current != uint32(len(data)) {
|
||||
return nil, errors.New("FinishedDeSerialize err: current != uint32(len(data)")
|
||||
}
|
||||
|
||||
return retFinished, nil
|
||||
}
|
||||
|
||||
// ------------------ 各类Extension反序列化 ------------------
|
||||
|
||||
// PreSharedKeyExtensionDeSerialize 反序列化PreSharedKeyExtension
|
||||
func PreSharedKeyExtensionDeSerialize(data []byte) (*PreSharedKeyExtension, error) {
|
||||
retPreSharedKeyExtensions := &PreSharedKeyExtension{}
|
||||
current := uint32(0)
|
||||
tmpType := baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
if tmpType != PreSharedKeyExtensionType {
|
||||
return nil, errors.New("PreSharedKeyExtensionDeSerialize err: not PreSharedKeyExtensionType")
|
||||
}
|
||||
current = current + 2
|
||||
|
||||
// pskCount
|
||||
pskCount := data[current]
|
||||
current = current + 1
|
||||
|
||||
// PskList
|
||||
retPreSharedKeyExtensions.PskList = make([]*Psk, pskCount)
|
||||
for index := 0; index < int(pskCount); index++ {
|
||||
// pskTotalLength
|
||||
pskTotalLength := baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// PskDeSerialize
|
||||
retPsk, err := PskDeSerialize(data[current : current+pskTotalLength])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add to PskList
|
||||
retPreSharedKeyExtensions.PskList[index] = retPsk
|
||||
current = current + pskTotalLength
|
||||
}
|
||||
|
||||
return retPreSharedKeyExtensions, nil
|
||||
}
|
||||
|
||||
// ClientKeyShareExtensionDeSerialize 解析ClientKeyShareExtension
|
||||
func ClientKeyShareExtensionDeSerialize(data []byte) (*ClientKeyShareExtension, error) {
|
||||
retClientKeyShareExtension := &ClientKeyShareExtension{}
|
||||
current := uint32(0)
|
||||
tmpType := baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
if tmpType != ClientKeyShareType {
|
||||
return nil, errors.New("ClientKeyShareExtensionDeSerialize err: tmpType != ClientKeyShareType")
|
||||
}
|
||||
current = current + 2
|
||||
|
||||
// clientKeyOfferCount
|
||||
clientKeyOfferCount := data[current]
|
||||
current = current + 1
|
||||
|
||||
retClientKeyShareExtension.ClientKeyOfferList = make([]*ClientKeyOffer, clientKeyOfferCount)
|
||||
// ClientKeyOfferList
|
||||
for index := 0; index < int(clientKeyOfferCount); index++ {
|
||||
clientKeyOffer := &ClientKeyOffer{}
|
||||
clientKeyOfferTotalLength := baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
startPos := current
|
||||
|
||||
// Version
|
||||
clientKeyOffer.Version = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// PublicValue
|
||||
publicValueSize := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
clientKeyOffer.PublicValue = data[current : current+publicValueSize]
|
||||
current = current + publicValueSize
|
||||
|
||||
if current-startPos != clientKeyOfferTotalLength {
|
||||
return nil, errors.New("ClientKeyShareExtensionDeSerialize err: current - startPos != clientKeyOfferTotalLength")
|
||||
}
|
||||
retClientKeyShareExtension.ClientKeyOfferList[index] = clientKeyOffer
|
||||
}
|
||||
|
||||
// CertificateVersion
|
||||
retClientKeyShareExtension.CertificateVersion = baseutils.BytesToInt32(data[current : current+4])
|
||||
|
||||
return retClientKeyShareExtension, nil
|
||||
}
|
||||
|
||||
// ServerKeyShareExtensionDeSerialize 反序列化ServerKeyShareExtension
|
||||
func ServerKeyShareExtensionDeSerialize(data []byte) (*ServerKeyShareExtension, error) {
|
||||
retServerKeyShareExtension := &ServerKeyShareExtension{}
|
||||
// 索引
|
||||
current := uint32(0)
|
||||
|
||||
// tmpType
|
||||
tmpType := baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
if tmpType != ServerKeyShareType {
|
||||
return nil, errors.New("ServerKeyShareExtensionDeSerialize err: tmpType != ServerKeyShareType")
|
||||
}
|
||||
current = current + 2
|
||||
|
||||
// KeyOfferNameGroup
|
||||
retServerKeyShareExtension.KeyOfferNameGroup = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
// PublicValue
|
||||
publicValueSize := uint32(baseutils.BytesToUint16BigEndian(data[current : current+2]))
|
||||
current = current + 2
|
||||
retServerKeyShareExtension.PublicValue = data[current : current+publicValueSize]
|
||||
|
||||
return retServerKeyShareExtension, nil
|
||||
}
|
||||
|
||||
// EarlyEncryptedDataExtensionDeSerialize 反序列化EarlyEncryptedDataExtension
|
||||
func EarlyEncryptedDataExtensionDeSerialize(data []byte) (*EarlyEncryptDataExtension, error) {
|
||||
retEarlyEncryptDataExtension := &EarlyEncryptDataExtension{}
|
||||
// 索引
|
||||
current := uint32(0)
|
||||
// Type
|
||||
tmpType := baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
if tmpType != EarlyEncryptDataType {
|
||||
return nil, errors.New("EarlyEncryptedDataExtensionDeSerialize err: tmpType != EarlyEncryptDataType")
|
||||
}
|
||||
current = current + 2
|
||||
// ClientGmtTime
|
||||
retEarlyEncryptDataExtension.ClientGmtTime = baseutils.BytesToInt32(data[current : current+4])
|
||||
|
||||
return retEarlyEncryptDataExtension, nil
|
||||
}
|
||||
|
||||
// LongPackHeaderInfoDeSerialize 序列化LongPackHeaderInfo
|
||||
func LongPackHeaderInfoDeSerialize(data []byte) (*LongPackHeaderInfo, error) {
|
||||
retLongPackHeaderInfo := &LongPackHeaderInfo{}
|
||||
|
||||
current := 0
|
||||
// HeaderLen
|
||||
retLongPackHeaderInfo.HeaderLen = baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
// Version
|
||||
retLongPackHeaderInfo.Version = baseutils.BytesToUint16BigEndian(data[current : current+2])
|
||||
current = current + 2
|
||||
// Operation
|
||||
retLongPackHeaderInfo.Operation = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
// SequenceNumber
|
||||
retLongPackHeaderInfo.SequenceNumber = baseutils.BytesToInt32(data[current : current+4])
|
||||
current = current + 4
|
||||
|
||||
if current != len(data) {
|
||||
return retLongPackHeaderInfo, errors.New("LongPackHeaderInfoDeSerialize err: current != len(data)")
|
||||
}
|
||||
return retLongPackHeaderInfo, nil
|
||||
}
|
||||
135
clientsdk/mmtls/mmhttp.go
Normal file
135
clientsdk/mmtls/mmhttp.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
// MMHTTPPost mmtls方式发送数据包
|
||||
func MMHTTPPost(mmInfo *MMInfo, data []byte) ([]byte, error) {
|
||||
requestURL := "http://" + mmInfo.ShortHost + mmInfo.ShortURL
|
||||
request, err := http.NewRequest("POST", requestURL, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request.Header.Add("UserAgent", "MicroMessenger Client")
|
||||
request.Header.Add("Accept", "*/*")
|
||||
request.Header.Add("Cache-Control", "no-cache")
|
||||
request.Header.Add("Connection", "close")
|
||||
request.Header.Add("content-type", "application/octet-stream")
|
||||
request.Header.Add("Upgrade", "mmtls")
|
||||
request.Header.Add("Host", mmInfo.ShortHost)
|
||||
//fmt.Println(mmInfo.ShortHost)//szshort.weixin.qq.com
|
||||
|
||||
// 发送请求
|
||||
httpTransport := &http.Transport{
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
conn, err := net.DialTimeout(network, addr, time.Second*15) //设置建立连接超时
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.SetDeadline(time.Now().Add(time.Second * 15)) //设置发送接受数据超时
|
||||
return conn, nil
|
||||
},
|
||||
ResponseHeaderTimeout: time.Second * 15,
|
||||
MaxIdleConnsPerHost: -1, //禁用连接池缓存
|
||||
DisableKeepAlives: true, //禁用客户端连接缓存到连接池
|
||||
}
|
||||
|
||||
// 如果有代理
|
||||
if mmInfo.Dialer != nil {
|
||||
httpTransport.Dial = mmInfo.Dialer.Dial
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: httpTransport}
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
baseutils.PrintLog(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 接收响应
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 返回响应数据
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// MMHTTPPostData mmtls短链接方式发送请求数据包
|
||||
func MMHTTPPostData(mmInfo *MMInfo, url string, data []byte) ([]byte, error) {
|
||||
// 创建HttpHandler
|
||||
httpHandler := &HTTPHandler{}
|
||||
httpHandler.URL = url
|
||||
httpHandler.Host = mmInfo.ShortHost
|
||||
httpHandler.MMPkg = data
|
||||
|
||||
// 创建发送请求项列表
|
||||
sendItems, err := CreateSendPackItems(mmInfo, httpHandler)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
// MMTLS-加密要发送的数据
|
||||
packData, err := MMHTTPPackData(mmInfo, sendItems)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
respData, err := MMHTTPPost(mmInfo, packData)
|
||||
// 解包响应数据
|
||||
decodeData, err := MMDecodeResponseData(mmInfo, sendItems, respData)
|
||||
if err != nil {
|
||||
baseutils.PrintLog(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return decodeData, nil
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
纯Http请求
|
||||
*/
|
||||
func HTTPPost(mmInfo *MMInfo, cgi string, data []byte) ([]byte, error) {
|
||||
requestURL := "http://" + mmInfo.ShortURL + cgi
|
||||
request, err := http.NewRequest("POST", requestURL, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Add("UserAgent", "MicroMessenger Client")
|
||||
request.Header.Add("Accept", "*/*")
|
||||
request.Header.Add("Cache-Control", "no-cache")
|
||||
request.Header.Add("Connection", "Keep-Alive")
|
||||
request.Header.Add("content-type", "application/octet-stream")
|
||||
// 发送请求
|
||||
httpTransport := &http.Transport{}
|
||||
// 如果有代理
|
||||
if mmInfo.Dialer != nil {
|
||||
httpTransport.Dial = mmInfo.Dialer.Dial
|
||||
}
|
||||
client := &http.Client{Transport: httpTransport}
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
baseutils.PrintLog(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 接收响应
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 返回响应数据
|
||||
return body, nil
|
||||
}
|
||||
162
clientsdk/mmtls/mmlonglinknet.go
Normal file
162
clientsdk/mmtls/mmlonglinknet.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
// MMLongConnect 链接MMtls服务器
|
||||
func MMLongConnect(mmInfo *MMInfo) error {
|
||||
strPort := strconv.Itoa(int(mmInfo.LONGPort))
|
||||
serverAddr := mmInfo.LongHost + ":" + strPort
|
||||
fmt.Println("MMLongConnect serverAddr: ", serverAddr)
|
||||
mminfoDialer := "0"
|
||||
if mmInfo.Dialer != nil {
|
||||
mminfoDialer = "1"
|
||||
}
|
||||
// 打印 mmInfo.Dialer
|
||||
fmt.Println("MMLongConnect mmInfo.Dialer: ", mminfoDialer)
|
||||
// 定义
|
||||
var conn net.Conn
|
||||
var err error
|
||||
for i := 0; i < 30; i++ {
|
||||
if mmInfo.Dialer != nil {
|
||||
conn, err = mmInfo.Dialer.Dial("tcp4", serverAddr)
|
||||
if err != nil {
|
||||
baseutils.PrintLog(fmt.Sprintf("MMLongConnect attempt %d failed: %s", i+1, err.Error()))
|
||||
// 等待 500 毫秒重时 (最大 30 次)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
mmInfo.Conn = conn
|
||||
mmInfo.reader = bufio.NewReader(conn)
|
||||
fmt.Println("MMLongConnect success!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 没有使用代理
|
||||
conn, err = net.Dial("tcp4", serverAddr)
|
||||
if err != nil {
|
||||
baseutils.PrintLog(fmt.Sprintf("MMLongConnect attempt %d failed: %s", i+1, err.Error()))
|
||||
// 等待 500 毫秒重时 (最大 30 次)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
mmInfo.Conn = conn
|
||||
mmInfo.reader = bufio.NewReader(conn)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MMTCPSendData MMTCPSendData 长链接发送数据
|
||||
func MMTCPSendData(mmInfo *MMInfo, data []byte) error {
|
||||
// 连接服务器
|
||||
if mmInfo.Conn == nil {
|
||||
// 提前设置好长链接Host Port
|
||||
err := MMLongConnect(mmInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
length, err := mmInfo.Conn.Write(data)
|
||||
// 判断是否出错
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 判断数据是否发送完毕
|
||||
if length != len(data) {
|
||||
return errors.New("MMTCPSendData err: length != len(data)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MMTCPRecvItems 循环接收长链接数据
|
||||
// Deprecated
|
||||
func MMTCPRecvItems(mmInfo *MMInfo) ([]*PackItem, error) {
|
||||
// 接收返回数据
|
||||
recvBuf := make([]byte, 10240)
|
||||
recvLen, err := mmInfo.Conn.Read(recvBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retData := recvBuf[0:recvLen]
|
||||
retItems, err := ParserMMtlsResponseData(retData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
// MMTCPRecvOneItem
|
||||
func MMTCPRecvOneItem(mmInfo *MMInfo) (*PackItem, error) {
|
||||
if mmInfo == nil || mmInfo.Conn == nil || mmInfo.reader == nil {
|
||||
return nil, errors.New("mmInfo.Conn or mmInfo.reader is nil")
|
||||
}
|
||||
|
||||
// 读取头部数据
|
||||
recordHeadData := make([]byte, 5)
|
||||
if _, err := io.ReadFull(mmInfo.reader, recordHeadData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取Content
|
||||
recordHead := RecordHeadDeSerialize(recordHeadData)
|
||||
bodyData := make([]byte, recordHead.Size)
|
||||
if _, err := io.ReadFull(mmInfo.reader, bodyData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PackItem{
|
||||
RecordHead: recordHeadData,
|
||||
PackData: bodyData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MMTCPSendReq 长链接发送请求
|
||||
func MMTCPSendReq(mmInfo *MMInfo, opCode uint32, data []byte) error {
|
||||
sendData, err := MMLongPackData(mmInfo, opCode, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 发送数据
|
||||
err = MMTCPSendData(mmInfo, sendData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MMTCPRecvData 接受MMTLS信息
|
||||
func MMTCPRecvData(mmInfo *MMInfo) (*LongRecvInfo, error) {
|
||||
// 接收响应数据
|
||||
recvItem, err := MMTCPRecvOneItem(mmInfo)
|
||||
if err != nil {
|
||||
fmt.Println("MMTCPRecvData err", err.Error())
|
||||
// log.("接收mmtls长链接响应失败", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 解包响应数据
|
||||
recvInfo, err := MMLongUnPackData(mmInfo, recvItem)
|
||||
if err != nil {
|
||||
fmt.Println("MMTCPRecvData err", err.Error())
|
||||
//log.Info("解包响应数据失败", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return recvInfo, nil
|
||||
}
|
||||
218
clientsdk/mmtls/mmlongprotocol.go
Normal file
218
clientsdk/mmtls/mmlongprotocol.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
// InitMMTLSInfoLong 初始化MMTLS信息通过长链接
|
||||
func InitMMTLSInfoLong(dialer proxy.Dialer, longHostName string, shortHostName string, pskList []*Psk) (*MMInfo, error) {
|
||||
// 初始化MMInfo
|
||||
mmInfo := CreateNewMMInfo()
|
||||
mmInfo.ShortHost = shortHostName
|
||||
mmInfo.LongHost = longHostName
|
||||
mmInfo.Dialer = dialer
|
||||
// 第一次设置为空,后面设置成前面握手返回的Psk列表
|
||||
mmInfo.ShortPskList = pskList
|
||||
// 随机生成ClientEcdhKeys,握手用
|
||||
mmInfo.ClientEcdhKeys = CreateClientEcdhKeys()
|
||||
|
||||
// 开始握手
|
||||
err := MMHandShakeByLongLink(mmInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 发送一次心跳包
|
||||
err = SendHeartBeat(mmInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 握手成功,设置短链接的HOST 和 新的URL
|
||||
shortURL := []byte("/mmtls/")
|
||||
mmInfo.ShortURL = string(append(shortURL, []byte(baseutils.RandomSmallHexString(8))[0:]...))
|
||||
|
||||
return mmInfo, nil
|
||||
}
|
||||
|
||||
// MMHandShakeByLongLink 长链接握手(与短链接握手类似,只需选择其中一种握手方式,微信是采用长链接握手,每次登陆前都需要握手)
|
||||
func MMHandShakeByLongLink(mmInfo *MMInfo) error {
|
||||
// 发送握手请求 - ClientHello
|
||||
clientHelloData := CreateHandShakeClientHelloData(mmInfo)
|
||||
sendData := CreateRecordData(ServerHandShakeType, clientHelloData)
|
||||
|
||||
// 发送ClientHello
|
||||
err := MMTCPSendData(mmInfo, sendData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 接收响应
|
||||
retItems, err := MMTCPRecvItems(mmInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理握手信息
|
||||
clientFinishedData, err := DealHandShakePackItems(mmInfo, retItems, clientHelloData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送clientFinished, 长链接必须要发送,但服务器不会响应
|
||||
err = MMTCPSendData(mmInfo, clientFinishedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendHeartBeat 发送心跳包
|
||||
func SendHeartBeat(mmInfo *MMInfo) error {
|
||||
// 发送心跳包
|
||||
heartData, err := GetHeartBeatData(mmInfo)
|
||||
err = MMTCPSendData(mmInfo, heartData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 接收心跳包响应
|
||||
retItem, err := MMTCPRecvOneItem(mmInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析心跳包响应数据,但不需处理
|
||||
_, err = MMLongUnPackData(mmInfo, retItem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetHeartBeatData 获取心跳包数据
|
||||
func GetHeartBeatData(mmInfo *MMInfo) ([]byte, error) {
|
||||
retBytes := make([]byte, 0)
|
||||
|
||||
bodyData := make([]byte, 0)
|
||||
// LongPackTagInfo
|
||||
packTagInfo := &LongPackHeaderInfo{}
|
||||
packTagInfo.HeaderLen = 16
|
||||
packTagInfo.Version = MMLongVersion
|
||||
packTagInfo.Operation = MMLongOperationSmartHeartBeat
|
||||
packTagInfo.SequenceNumber = 0xffffffff
|
||||
tagInfoBytes := LongPackHeaderInfoSerialize(packTagInfo)
|
||||
|
||||
// BodyData
|
||||
dataLength := uint32(len(tagInfoBytes) + 4)
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(dataLength)[0:]...)
|
||||
bodyData = append(bodyData, tagInfoBytes[0:]...)
|
||||
|
||||
// RecordHead
|
||||
tmpLen := uint16(dataLength + 16)
|
||||
recordHeader := GetRecordDataByLength(BodyType, tmpLen)
|
||||
|
||||
clientSeq := atomic.AddUint32(&mmInfo.LONGClientSeq, 1) - 1
|
||||
// 加密
|
||||
tmpNonce := GetNonce(mmInfo.LongHdkfKey.EncodeNonce, uint32(clientSeq))
|
||||
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
tmpAad = append(tmpAad, baseutils.Int32ToBytes(uint32(clientSeq))...)
|
||||
tmpAad = append(tmpAad, recordHeader...)
|
||||
encodeData, err := AesGcmEncrypt(mmInfo.LongHdkfKey.EncodeAesKey, tmpNonce, tmpAad, bodyData)
|
||||
if err != nil {
|
||||
return retBytes, err
|
||||
}
|
||||
|
||||
// 组包
|
||||
retBytes = append(retBytes, recordHeader...)
|
||||
retBytes = append(retBytes, encodeData...)
|
||||
|
||||
return retBytes, nil
|
||||
}
|
||||
|
||||
// MMLongPackData 长链接方式 打包请求数据
|
||||
func MMLongPackData(mmInfo *MMInfo, opCode uint32, data []byte) ([]byte, error) {
|
||||
retBytes := make([]byte, 0)
|
||||
|
||||
bodyData := make([]byte, 0)
|
||||
// LongPackTagInfo
|
||||
packTagInfo := &LongPackHeaderInfo{}
|
||||
packTagInfo.HeaderLen = 16
|
||||
packTagInfo.Version = MMLongVersion
|
||||
packTagInfo.Operation = opCode
|
||||
packTagInfo.SequenceNumber = atomic.LoadUint32(&mmInfo.LONGClientSeq)
|
||||
tagInfoBytes := LongPackHeaderInfoSerialize(packTagInfo)
|
||||
|
||||
// BodyData
|
||||
dataLength := uint32(len(data) + len(tagInfoBytes) + 4)
|
||||
bodyData = append(bodyData, baseutils.Int32ToBytes(dataLength)[0:]...)
|
||||
bodyData = append(bodyData, tagInfoBytes[0:]...)
|
||||
bodyData = append(bodyData, data[0:]...)
|
||||
|
||||
// RecordHead
|
||||
tmpLen := uint16(dataLength + 16)
|
||||
recordHeader := GetRecordDataByLength(BodyType, tmpLen)
|
||||
|
||||
// 加密
|
||||
tmpNonce := GetNonce(mmInfo.LongHdkfKey.EncodeNonce, atomic.LoadUint32(&mmInfo.LONGClientSeq))
|
||||
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
tmpAad = append(tmpAad, baseutils.Int32ToBytes(atomic.LoadUint32(&mmInfo.LONGClientSeq))...)
|
||||
tmpAad = append(tmpAad, recordHeader...)
|
||||
encodeData, err := AesGcmEncrypt(mmInfo.LongHdkfKey.EncodeAesKey, tmpNonce, tmpAad, bodyData)
|
||||
if err != nil {
|
||||
return retBytes, err
|
||||
}
|
||||
|
||||
// 组包,ClientSeq 索引值+1
|
||||
atomic.AddUint32(&mmInfo.LONGClientSeq, 1)
|
||||
//mmInfo.LONGClientSeq++
|
||||
retBytes = append(retBytes, recordHeader...)
|
||||
retBytes = append(retBytes, encodeData...)
|
||||
return retBytes, nil
|
||||
}
|
||||
|
||||
// MMLongUnPackData 长链接方式 解包数据
|
||||
func MMLongUnPackData(mmInfo *MMInfo, packItem *PackItem) (*LongRecvInfo, error) {
|
||||
// 解密
|
||||
tmpNonce := GetNonce(mmInfo.LongHdkfKey.DecodeNonce, atomic.LoadUint32(&mmInfo.LONGServerSeq))
|
||||
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
tmpAad = append(tmpAad, baseutils.Int32ToBytes(atomic.LoadUint32(&mmInfo.LONGServerSeq))...)
|
||||
tmpAad = append(tmpAad, packItem.RecordHead...)
|
||||
DecodeData, err := AesGcmDecrypt(mmInfo.LongHdkfKey.DecodeAesKey, tmpNonce, tmpAad, packItem.PackData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TotalLength
|
||||
current := 0
|
||||
totalLength := baseutils.BytesToInt32(DecodeData[current : current+4])
|
||||
current = current + 4
|
||||
if totalLength != uint32(len(DecodeData)) {
|
||||
return nil, errors.New("MMLongUnPackData err: totalLength != uint32(len(DecodeData))")
|
||||
}
|
||||
|
||||
headerInfo, err := LongPackHeaderInfoDeSerialize(DecodeData[current : current+12])
|
||||
current = current + 12
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
retLongRecvInfo := &LongRecvInfo{}
|
||||
retLongRecvInfo.HeaderInfo = headerInfo
|
||||
// 如果大于 说明是业务包,如果等于则是长链接心跳包
|
||||
if totalLength > uint32(headerInfo.HeaderLen) {
|
||||
retLongRecvInfo.RespData = DecodeData[current:]
|
||||
}
|
||||
|
||||
// ServerSeq 索引值+1
|
||||
//mmInfo.LONGServerSeq = mmInfo.LONGServerSeq + 1
|
||||
atomic.AddUint32(&mmInfo.LONGServerSeq, 1)
|
||||
return retLongRecvInfo, nil
|
||||
}
|
||||
627
clientsdk/mmtls/mmprotocol.go
Normal file
627
clientsdk/mmtls/mmprotocol.go
Normal file
@@ -0,0 +1,627 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
|
||||
"github.com/micro/go-micro/util/log"
|
||||
"github.com/wsddn/go-ecdh"
|
||||
)
|
||||
|
||||
// CreateNewMMInfo 创建新的MMInfo
|
||||
func CreateNewMMInfo() *MMInfo {
|
||||
mmInfo := &MMInfo{}
|
||||
mmInfo.ShortHost = "szshort.weixin.qq.com"
|
||||
mmInfo.LongHost = "szlong.weixin.qq.com"
|
||||
mmInfo.LONGPort = 8080
|
||||
mmInfo.LONGClientSeq = 1
|
||||
mmInfo.LONGServerSeq = 1
|
||||
|
||||
return mmInfo
|
||||
}
|
||||
|
||||
// InitMMTLSInfoShort 如果使用MMTLS,每次登陆前都需要初始化MMTLSInfo信息
|
||||
// pskList: 之前握手服务端返回的,要保存起来,后面握手时使用, 第一次握手传空数组即可
|
||||
func InitMMTLSInfoShort(dialer proxy.Dialer, hostName string, pskList []*Psk) *MMInfo {
|
||||
// 初始化MMInfo
|
||||
mmInfo := CreateNewMMInfo()
|
||||
mmInfo.ShortPskList = pskList
|
||||
// 随机生成ClientEcdhKeys
|
||||
mmInfo.ClientEcdhKeys = CreateClientEcdhKeys()
|
||||
mmInfo.ShortHost = hostName
|
||||
mmInfo.Dialer = dialer
|
||||
// 握手
|
||||
mmInfo, err := MMHandShakeByShortLink(mmInfo, hostName)
|
||||
// 如果握手失败 就不使用MMTLS
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 握手成功,设置好HOST 和 新的URL
|
||||
shortURL := []byte("/mmtls/")
|
||||
mmInfo.ShortHost = hostName
|
||||
mmInfo.ShortURL = string(append(shortURL, []byte(baseutils.RandomSmallHexString(8))[0:]...))
|
||||
|
||||
return mmInfo
|
||||
}
|
||||
|
||||
// CreateClientEcdhKeys 创建新的ClientEcdhKeys
|
||||
func CreateClientEcdhKeys() *ClientEcdhKeys {
|
||||
// 随机
|
||||
clientEcdhKeys := &ClientEcdhKeys{}
|
||||
e := ecdh.NewEllipticECDH(elliptic.P256())
|
||||
priKey1, pubKey1, _ := e.GenerateKey(rand.Reader)
|
||||
priKey2, pubKey2, _ := e.GenerateKey(rand.Reader)
|
||||
clientEcdhKeys.PriKey1 = priKey1
|
||||
clientEcdhKeys.PriKey2 = priKey2
|
||||
clientEcdhKeys.PubKeyBuf1 = e.Marshal(pubKey1)
|
||||
clientEcdhKeys.PubKeyBuf2 = e.Marshal(pubKey2)
|
||||
|
||||
return clientEcdhKeys
|
||||
}
|
||||
|
||||
// MMHandShakeByShortLink 通过短链接握手
|
||||
func MMHandShakeByShortLink(mmInfo *MMInfo, hostName string) (*MMInfo, error) {
|
||||
shortURL := []byte("/mmtls/")
|
||||
mmURL := append(shortURL, []byte(baseutils.RandomSmallHexString(8))[0:]...)
|
||||
mmInfo.ShortURL = string(mmURL)
|
||||
|
||||
// 发送握手请求 - ClientHello
|
||||
clientHelloData := CreateHandShakeClientHelloData(mmInfo)
|
||||
sendData := CreateRecordData(ServerHandShakeType, clientHelloData)
|
||||
retBytes, err := MMHTTPPost(mmInfo, sendData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 解析握手相应数据
|
||||
retItems, err := ParserMMtlsResponseData(retBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 处理握手信息
|
||||
clientFinishData, err := DealHandShakePackItems(mmInfo, retItems, clientHelloData)
|
||||
_ = clientFinishData
|
||||
|
||||
return mmInfo, nil
|
||||
}
|
||||
|
||||
// ParserMMtlsResponseData 解析mmtls响应数据
|
||||
func ParserMMtlsResponseData(data []byte) ([]*PackItem, error) {
|
||||
// RecodeHead *RecodeHead
|
||||
retItems := make([]*PackItem, 0)
|
||||
|
||||
// 总数据大小
|
||||
totalLength := uint32(len(data))
|
||||
|
||||
current := uint32(0)
|
||||
// 解析所有包
|
||||
for current < totalLength {
|
||||
packItem := &PackItem{}
|
||||
|
||||
// recordHead
|
||||
if current+5 > totalLength {
|
||||
return retItems, errors.New("ParserMMtlsResponseData err: current+5 >= totalLength")
|
||||
}
|
||||
recordHead := RecordHeadDeSerialize(data[current:])
|
||||
packItem.RecordHead = data[current : current+5]
|
||||
current = current + 5
|
||||
// PackData
|
||||
// 判断数据是否有问题
|
||||
if current+uint32(recordHead.Size) > totalLength {
|
||||
return retItems, errors.New("ParserMMtlsResponseData err: current+uint32(recordHead.Size) >= totalLength")
|
||||
}
|
||||
packItem.PackData = data[current : current+uint32(recordHead.Size)]
|
||||
|
||||
// current
|
||||
current = current + uint32(recordHead.Size)
|
||||
retItems = append(retItems, packItem)
|
||||
}
|
||||
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
// DealHandShakePackItems 解密packItems
|
||||
func DealHandShakePackItems(mmInfo *MMInfo, packItems []*PackItem, clientHelloReq []byte) ([]byte, error) {
|
||||
retClientFinishData := make([]byte, 0)
|
||||
|
||||
// 先解析 ServerHello
|
||||
secretKey, err := DealServerHello(mmInfo, packItems[0])
|
||||
if err != nil {
|
||||
return retClientFinishData, err
|
||||
}
|
||||
|
||||
// 计算HashRet
|
||||
hashData := make([]byte, 0)
|
||||
hashData = append(hashData, clientHelloReq[0:]...)
|
||||
hashData = append(hashData, packItems[0].PackData...)
|
||||
hashRet := Sha256(hashData)
|
||||
|
||||
// 密钥扩展
|
||||
message := []byte("handshake key expansion")
|
||||
message = append(message, hashRet...)
|
||||
aesKeyExpand := HkdfExpand(secretKey, message, 56)
|
||||
gcmAesKey := aesKeyExpand[0x10:0x20]
|
||||
oriNonce := aesKeyExpand[0x2c:]
|
||||
|
||||
// 解密后面的包
|
||||
count := len(packItems)
|
||||
for index := 1; index < count; index++ {
|
||||
tmpPackItem := packItems[index]
|
||||
|
||||
// 解密数据
|
||||
tmpNonce := GetNonce(oriNonce, uint32(index))
|
||||
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
tmpAad = append(tmpAad, baseutils.Int32ToBytes(atomic.LoadUint32(&mmInfo.LONGServerSeq))...)
|
||||
tmpAad = append(tmpAad, tmpPackItem.RecordHead...)
|
||||
decodeData, err := AesGcmDecrypt(gcmAesKey, tmpNonce, tmpAad, tmpPackItem.PackData)
|
||||
// 设置解密后的数据
|
||||
tmpPackItem.PackData = decodeData
|
||||
if err != nil {
|
||||
return retClientFinishData, err
|
||||
}
|
||||
atomic.AddUint32(&mmInfo.LONGServerSeq, 1)
|
||||
//mmInfo.LONGServerSeq++
|
||||
|
||||
// 处理CertificateVerifyType
|
||||
tmpType := decodeData[4]
|
||||
if tmpType == CertificateVerifyType {
|
||||
// 校验服务器
|
||||
flag, err := DealCertificateVerify(clientHelloReq, packItems[0].PackData, decodeData)
|
||||
if err != nil {
|
||||
return retClientFinishData, err
|
||||
}
|
||||
if !flag {
|
||||
return retClientFinishData, errors.New("DealHandShakePackItems err: CertificateVerify failed")
|
||||
}
|
||||
}
|
||||
|
||||
// 处理NewSessionTicketType
|
||||
if tmpType == NewSessionTicketType {
|
||||
err := DealNewSessionTicket(mmInfo, decodeData)
|
||||
if err != nil {
|
||||
return retClientFinishData, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 处理Server FinishType
|
||||
if tmpType == FinishedType {
|
||||
// 第一步验证ServerFinished数据
|
||||
tmpHashData := make([]byte, 0)
|
||||
tmpHashData = append(tmpHashData, clientHelloReq[0:]...)
|
||||
tmpHashData = append(tmpHashData, packItems[0].PackData[0:]...)
|
||||
tmpHashData = append(tmpHashData, packItems[1].PackData[0:]...)
|
||||
tmpHashData = append(tmpHashData, packItems[2].PackData[0:]...)
|
||||
tmpHashValue := Sha256(tmpHashData)
|
||||
serverFinished, err := FinishedDeSerialize(tmpPackItem.PackData[4:])
|
||||
if err != nil {
|
||||
return retClientFinishData, err
|
||||
}
|
||||
bSuccess := VerifyFinishedData(secretKey, tmpHashValue, serverFinished.VerifyData)
|
||||
if !bSuccess {
|
||||
return retClientFinishData, errors.New("DealHandShakePackItems err: Finished verify failed")
|
||||
}
|
||||
|
||||
// 第二步生成ClientFinished数据,然后加密
|
||||
hkdfClientFinish := HkdfExpand(secretKey, []byte("client finished"), 32)
|
||||
hmacRet := HmacHash256(hkdfClientFinish, tmpHashValue)
|
||||
aesGcmParam := &AesGcmParam{}
|
||||
aesGcmParam.AesKey = aesKeyExpand[0x00:0x10]
|
||||
aesGcmParam.Nonce = aesKeyExpand[0x20:0x2c]
|
||||
// 创建Finished
|
||||
finished := CreateFinished(hmacRet)
|
||||
// 加密
|
||||
finishedData := FinishedSerialize(finished)
|
||||
clientSeq := atomic.AddUint32(&mmInfo.LONGClientSeq, 1) - 1
|
||||
encodeData, err := EncryptedReqData(aesGcmParam, finishedData, ServerHandShakeType, clientSeq)
|
||||
if err != nil {
|
||||
return retClientFinishData, err
|
||||
}
|
||||
retClientFinishData = CreateRecordData(ServerHandShakeType, encodeData)
|
||||
//mmInfo.LONGClientSeq++
|
||||
//atomic.AddUint32(&mmInfo.LONGClientSeq, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 计算扩展出来的用于后续加密的Key
|
||||
tmpExpandHashData := make([]byte, 0)
|
||||
tmpExpandHashData = append(tmpExpandHashData, clientHelloReq[0:]...)
|
||||
tmpExpandHashData = append(tmpExpandHashData, packItems[0].PackData[0:]...)
|
||||
tmpExpandHashData = append(tmpExpandHashData, packItems[1].PackData[0:]...)
|
||||
tmpExpandHashValue := Sha256(tmpExpandHashData)
|
||||
|
||||
// PskAccessKey 短连接MMTLS密钥
|
||||
expandPskAccessData := []byte("PSK_ACCESS")
|
||||
expandPskAccessData = append(expandPskAccessData, tmpExpandHashValue[0:]...)
|
||||
mmInfo.PskAccessKey = HkdfExpand(secretKey, expandPskAccessData, 32)
|
||||
|
||||
// AppDataKeyExtension 长链接MMTLS密钥
|
||||
tmpExpandHashData = append(tmpExpandHashData, packItems[2].PackData[0:]...)
|
||||
tmpLongHashValue := Sha256(tmpExpandHashData)
|
||||
expandedSecret := append([]byte("expanded secret"), tmpLongHashValue[0:]...)
|
||||
retExpandSecret := HkdfExpand(secretKey, expandedSecret, 32)
|
||||
appDataKeyData := append([]byte("application data key expansion"), tmpLongHashValue[0:]...)
|
||||
appDataKeyExtension := HkdfExpand(retExpandSecret, appDataKeyData, 56)
|
||||
mmInfo.LongHdkfKey = &HkdfKey56{}
|
||||
mmInfo.LongHdkfKey.EncodeAesKey = appDataKeyExtension[0x00:0x10]
|
||||
mmInfo.LongHdkfKey.DecodeAesKey = appDataKeyExtension[0x10:0x20]
|
||||
mmInfo.LongHdkfKey.EncodeNonce = appDataKeyExtension[0x20:0x2c]
|
||||
mmInfo.LongHdkfKey.DecodeNonce = appDataKeyExtension[0x2c:]
|
||||
|
||||
// 返回ClientFinishData
|
||||
return retClientFinishData, nil
|
||||
}
|
||||
|
||||
// DealServerHello 处理ServerHello
|
||||
func DealServerHello(mmInfo *MMInfo, packItem *PackItem) ([]byte, error) {
|
||||
// 解析ServerHello
|
||||
serverHello, err := ServerHelloDeSerialize(packItem.PackData[4:])
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
// 解析ServerKeyShare
|
||||
serverKeyShareExtension, err := ServerKeyShareExtensionDeSerialize(serverHello.ExtensionList[0].ExtensionData)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
// 解析ServerPublicKey
|
||||
ecdhTool := ecdh.NewEllipticECDH(elliptic.P256())
|
||||
serverPubKey, isOk := ecdhTool.Unmarshal(serverKeyShareExtension.PublicValue)
|
||||
if !isOk {
|
||||
return []byte{}, errors.New("DecodePackItems ecdhTool.Unmarshal(serverKeyShareExtension.PublicValue) failed")
|
||||
}
|
||||
|
||||
// 根据NameGroup 决定使用哪个Privakey
|
||||
ecdhPriKey := mmInfo.ClientEcdhKeys.PriKey1
|
||||
if serverKeyShareExtension.KeyOfferNameGroup == 2 {
|
||||
ecdhPriKey = mmInfo.ClientEcdhKeys.PriKey2
|
||||
}
|
||||
|
||||
// 协商密钥
|
||||
secretKey, err := ecdhTool.GenerateSharedSecret(ecdhPriKey, serverPubKey) //服务器公钥和本地第一个私钥协商出安全密钥
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return Sha256(secretKey), nil
|
||||
}
|
||||
|
||||
// DealCertificateVerify 处理CertificateVerify数据: 校验服务器-判断是不是微信服务器,请求返回数据有没有被串改
|
||||
func DealCertificateVerify(clientHelloData []byte, serverHelloData []byte, data []byte) (bool, error) {
|
||||
// 解析数据
|
||||
totalSize := baseutils.BytesToInt32(data[0:4])
|
||||
certificateVerify, err := CertificateVerifyDeSerialize(data[4 : 4+totalSize])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 合并请求数据
|
||||
message := make([]byte, 0)
|
||||
message = append(message, clientHelloData[0:]...)
|
||||
message = append(message, serverHelloData[0:]...)
|
||||
message = Sha256(message)
|
||||
|
||||
// 校验数据
|
||||
flag, err := ECDSAVerifyData(message, certificateVerify.Signature)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return flag, nil
|
||||
}
|
||||
|
||||
// DealNewSessionTicket 处理NewSessionTicket数据
|
||||
func DealNewSessionTicket(mmInfo *MMInfo, data []byte) error {
|
||||
// 解析数据
|
||||
totalSize := baseutils.BytesToInt32(data[0:4])
|
||||
newSessionTicket, err := NewSessionTicketDeSerialize(data[4 : 4+totalSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mmInfo.ShortPskList = newSessionTicket.PskList
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------- 上面是握手阶段 -----------
|
||||
// ----------- 接下来是发送请求 -----------
|
||||
|
||||
// EncryptedReqData EncryptedReqData
|
||||
func EncryptedReqData(aesGcmParam *AesGcmParam, data []byte, recordHeadType byte, clientSeq uint32) ([]byte, error) {
|
||||
tmpNonce := GetNonce(aesGcmParam.Nonce, clientSeq)
|
||||
tmpHead := GetRecordDataByLength(recordHeadType, uint16(len(data)+0x10))
|
||||
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
tmpAad = append(tmpAad, baseutils.Int32ToBytes(clientSeq)...)
|
||||
tmpAad = append(tmpAad, tmpHead[0:]...)
|
||||
encodeData, err := AesGcmEncrypt(aesGcmParam.AesKey, tmpNonce, tmpAad, data)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return encodeData, nil
|
||||
}
|
||||
|
||||
// DecryptedRecvData 解析响应数据包
|
||||
func DecryptedRecvData(aesGcmParam *AesGcmParam, recvItem *PackItem, serverSeq uint32) ([]byte, error) {
|
||||
tmpNonce := GetNonce(aesGcmParam.Nonce, serverSeq)
|
||||
tmpAad := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
tmpAad = append(tmpAad, baseutils.Int32ToBytes(serverSeq)...)
|
||||
tmpAad = append(tmpAad, recvItem.RecordHead[0:]...)
|
||||
encodeData, err := AesGcmDecrypt(aesGcmParam.AesKey, tmpNonce, tmpAad, recvItem.PackData)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return encodeData, nil
|
||||
}
|
||||
|
||||
// CreateSendPackItems 创建发送的请求项列表
|
||||
func CreateSendPackItems(mmInfo *MMInfo, httpHandler *HTTPHandler) ([]*PackItem, error) {
|
||||
retItems := make([]*PackItem, 0)
|
||||
|
||||
// ClientHelloItem
|
||||
clientHelloItem := &PackItem{}
|
||||
clientHello, err := CreateClientHelloData(mmInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientHelloData := ClientHelloSerialize(clientHello)
|
||||
clientHelloItem.RecordHead = GetRecordDataByLength(ClientHandShakeType, uint16(len(clientHelloData)))
|
||||
clientHelloItem.PackData = clientHelloData
|
||||
|
||||
// EncryptedExtensionsItem
|
||||
encryptedExtensionsItem := &PackItem{}
|
||||
encryptedExtensions := CreateEncryptedExtensions()
|
||||
encryptedExtensionsData := EncryptedExtensionsSerialize(encryptedExtensions)
|
||||
encryptedExtensionsItem.RecordHead = GetRecordDataByLength(ClientHandShakeType, uint16(len(encryptedExtensionsData)))
|
||||
encryptedExtensionsItem.PackData = encryptedExtensionsData
|
||||
|
||||
// HTTPHandlerItem
|
||||
httpHandlerItem := &PackItem{}
|
||||
httpHandlerData := HTTPHandlerSerialize(httpHandler)
|
||||
httpHandlerItem.RecordHead = GetRecordDataByLength(BodyType, uint16(len(httpHandlerData)))
|
||||
httpHandlerItem.PackData = httpHandlerData
|
||||
|
||||
// AlertItem
|
||||
alertItem := &PackItem{}
|
||||
alertData := GetAlertData()
|
||||
alertItem.RecordHead = GetRecordDataByLength(AlertType, uint16(len(alertData)))
|
||||
alertItem.PackData = alertData
|
||||
|
||||
// 返回数据
|
||||
retItems = append(retItems, clientHelloItem)
|
||||
retItems = append(retItems, encryptedExtensionsItem)
|
||||
retItems = append(retItems, httpHandlerItem)
|
||||
retItems = append(retItems, alertItem)
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
// MMHTTPPackData MMPackData
|
||||
func MMHTTPPackData(mmInfo *MMInfo, items []*PackItem) ([]byte, error) {
|
||||
// 密钥扩展
|
||||
sha256Value := Sha256(items[0].PackData)
|
||||
expandSecretData := []byte("early data key expansion")
|
||||
expandSecretData = append(expandSecretData, sha256Value[0:]...)
|
||||
tmpHkdfValue := HkdfExpand(mmInfo.PskAccessKey, expandSecretData, 28)
|
||||
aesGcmParam := &AesGcmParam{}
|
||||
aesGcmParam.AesKey = tmpHkdfValue[0x00:0x10]
|
||||
aesGcmParam.Nonce = tmpHkdfValue[0x10:0x1c]
|
||||
|
||||
// 加密EncryptedExtensions
|
||||
encryptData, err := EncryptedReqData(aesGcmParam, items[1].PackData, ClientHandShakeType, 1)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
partData2 := CreateRecordData(ClientHandShakeType, encryptData)
|
||||
|
||||
// 加密HTTPHandler
|
||||
httpHandlerEncryptData, err := EncryptedReqData(aesGcmParam, items[2].PackData, BodyType, 2)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
partData3 := CreateRecordData(BodyType, httpHandlerEncryptData)
|
||||
|
||||
// 加密Alert
|
||||
alertDataEncryptData, err := EncryptedReqData(aesGcmParam, items[3].PackData, AlertType, 3)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
partData4 := CreateRecordData(AlertType, alertDataEncryptData)
|
||||
|
||||
// 返回数据
|
||||
retData := make([]byte, 0)
|
||||
retData = append(retData, items[0].RecordHead[0:]...)
|
||||
retData = append(retData, items[0].PackData[0:]...)
|
||||
retData = append(retData, partData2[0:]...)
|
||||
retData = append(retData, partData3[0:]...)
|
||||
retData = append(retData, partData4[0:]...)
|
||||
return retData, err
|
||||
}
|
||||
|
||||
// MMDecodeResponseData 解码响应数据
|
||||
// func MMDecodeResponseData(mmInfo *MMInfo, sendItems []*PackItem, respData []byte) ([]byte, error) {
|
||||
// retData := make([]byte, 0)
|
||||
|
||||
// // 解析 对响应数据进行分包
|
||||
// recvItems, err := ParserMMtlsResponseData(respData)
|
||||
// if err != nil {
|
||||
// return retData, err
|
||||
// }
|
||||
|
||||
// if len(recvItems) < 4 {
|
||||
// return retData, errors.New("MMDecodeResponseData err: recvItems Length < 4")
|
||||
// }
|
||||
|
||||
// // 密钥扩展 用于后面的解密
|
||||
// shaData := make([]byte, 0)
|
||||
// shaData = append(shaData, sendItems[0].PackData[0:]...)
|
||||
// shaData = append(shaData, sendItems[1].PackData[0:]...)
|
||||
// shaData = append(shaData, recvItems[0].PackData[0:]...)
|
||||
// sha256Value := Sha256(shaData)
|
||||
// expandSecretData := []byte("handshake key expansion")
|
||||
// expandSecretData = append(expandSecretData, sha256Value[0:]...)
|
||||
// tmpHkdfValue := HkdfExpand(mmInfo.PskAccessKey, expandSecretData, 28)
|
||||
// aesGcmParam := &AesGcmParam{}
|
||||
// aesGcmParam.AesKey = tmpHkdfValue[0x00:0x10]
|
||||
// aesGcmParam.Nonce = tmpHkdfValue[0x10:0x1c]
|
||||
|
||||
// // 解密剩下的包
|
||||
// count := len(recvItems)
|
||||
// for index := 1; index < count; index++ {
|
||||
// // 解密Finished数据包
|
||||
// decodeData, err := DecryptedRecvData(aesGcmParam, recvItems[index], uint32(index))
|
||||
// if err != nil {
|
||||
// return retData, err
|
||||
// }
|
||||
|
||||
// // RecordHeadType
|
||||
// recordHeadType := recvItems[index].RecordHead[0]
|
||||
// // ServerHandShakeType 校验收到的数据是否完整,是否又被串改
|
||||
// if recordHeadType == ServerHandShakeType {
|
||||
// // 判断数据长度是否正常
|
||||
// current := 0
|
||||
// totalLength := int(baseutils.BytesToInt32(decodeData[current : current+4]))
|
||||
// current = current + 4
|
||||
// if totalLength < 0 {
|
||||
// return retData, errors.New("MMDecodeResponseData err: totalLength < 0")
|
||||
// }
|
||||
|
||||
// // ReceiveSubType
|
||||
// subType := decodeData[current]
|
||||
// // FinishedType 校验数据是否正常
|
||||
// if subType == FinishedType {
|
||||
// // 反序列化
|
||||
// finished, err := FinishedDeSerialize(decodeData[current : current+totalLength])
|
||||
// if err != nil {
|
||||
// return retData, err
|
||||
// }
|
||||
// bSuccess := VerifyFinishedData(mmInfo.PskAccessKey, sha256Value, finished.VerifyData)
|
||||
// if !bSuccess {
|
||||
// return retData, errors.New("MMDecodeResponseData err: VerifyFinishedData failed")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 解析响应数据
|
||||
// if recordHeadType == BodyType {
|
||||
// retData = append(retData, decodeData[0:]...)
|
||||
// }
|
||||
|
||||
// // 解析AlertType
|
||||
// if recordHeadType == AlertType {
|
||||
// // 关闭连接的数据包(对长链接有用) 固定为0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x01
|
||||
// }
|
||||
// }
|
||||
|
||||
// return retData, nil
|
||||
// }
|
||||
|
||||
// MMDecodeResponseData 解码响应数据
|
||||
func MMDecodeResponseData(mmInfo *MMInfo, sendItems []*PackItem, respData []byte) ([]byte, error) {
|
||||
var retData []byte
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Printf("Recovered from panic: %v\n", r)
|
||||
// 这里可以记录日志或者执行其他的恢复操作
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// 解析响应数据
|
||||
recvItems, err := ParserMMtlsResponseData(respData)
|
||||
if err != nil {
|
||||
return retData, fmt.Errorf("failed to parse response data: %w", err)
|
||||
}
|
||||
|
||||
if len(recvItems) < 4 {
|
||||
return retData, errors.New("MMDecodeResponseData err: recvItems Length < 4")
|
||||
}
|
||||
|
||||
if len(sendItems) < 2 || sendItems[0] == nil || sendItems[1] == nil {
|
||||
return retData, errors.New("MMDecodeResponseData err: sendItems Length < 2 or contains nil")
|
||||
}
|
||||
|
||||
// 密钥扩展 用于后面的解密, 确保 sendItems 和 recvItems 有效
|
||||
shaData := append(sendItems[0].PackData, sendItems[1].PackData...)
|
||||
shaData = append(shaData, recvItems[0].PackData...)
|
||||
|
||||
sha256Value := Sha256(shaData)
|
||||
expandSecretData := append([]byte("handshake key expansion"), sha256Value...)
|
||||
tmpHkdfValue := HkdfExpand(mmInfo.PskAccessKey, expandSecretData, 28)
|
||||
|
||||
aesGcmParam := &AesGcmParam{
|
||||
AesKey: tmpHkdfValue[:0x10],
|
||||
Nonce: tmpHkdfValue[0x10:0x1c],
|
||||
}
|
||||
|
||||
// 解密每个包
|
||||
for index := 1; index < len(recvItems); index++ {
|
||||
decodeData, err := DecryptedRecvData(aesGcmParam, recvItems[index], uint32(index))
|
||||
if err != nil {
|
||||
return retData, fmt.Errorf("error decrypting packet %d: %w", index, err)
|
||||
}
|
||||
|
||||
recordHeadType := recvItems[index].RecordHead[0]
|
||||
|
||||
if recordHeadType == ServerHandShakeType {
|
||||
if len(decodeData) < 4 {
|
||||
return retData, errors.New("MMDecodeResponseData err: decodeData Length < 4")
|
||||
}
|
||||
current := 4
|
||||
totalLength := int(baseutils.BytesToInt32(decodeData[:current]))
|
||||
if totalLength < 0 || len(decodeData) < totalLength {
|
||||
return retData, errors.New("MMDecodeResponseData err: invalid total length")
|
||||
}
|
||||
|
||||
subType := decodeData[current]
|
||||
if subType == FinishedType {
|
||||
finished, err := FinishedDeSerialize(decodeData[current : current+totalLength])
|
||||
if err != nil {
|
||||
return retData, fmt.Errorf("failed to deserialize finished data: %w", err)
|
||||
}
|
||||
bSuccess := VerifyFinishedData(mmInfo.PskAccessKey, sha256Value, finished.VerifyData)
|
||||
if !bSuccess {
|
||||
return retData, errors.New("MMDecodeResponseData err: VerifyFinishedData failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if recordHeadType == BodyType {
|
||||
retData = append(retData, decodeData...)
|
||||
}
|
||||
|
||||
if recordHeadType == AlertType {
|
||||
// 如果是告警数据包,可以在此处理
|
||||
}
|
||||
}
|
||||
|
||||
return retData, nil
|
||||
}
|
||||
|
||||
// VerifyFinishedData 校验服务端返回数据是否正确
|
||||
func VerifyFinishedData(aesKey []byte, shaValue []byte, finishedData []byte) bool {
|
||||
count := len(finishedData)
|
||||
message := []byte("server finished")
|
||||
tmpHkdfValue := HkdfExpand(aesKey, message, count)
|
||||
verifyData := HmacHash256(tmpHkdfValue, shaValue)
|
||||
|
||||
// 比对结果是否一致
|
||||
for index := 0; index < count; index++ {
|
||||
if verifyData[index] != finishedData[index] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
157
clientsdk/mmtls/mmrequest.go
Normal file
157
clientsdk/mmtls/mmrequest.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
// CreateRecordData 根据请求创建完整的mmtls数据包
|
||||
func CreateRecordData(recordType byte, data []byte) []byte {
|
||||
recordHead := &RecordHead{}
|
||||
recordHead.Type = recordType
|
||||
recordHead.Tag = 0xF103
|
||||
recordHead.Size = uint16(len(data))
|
||||
|
||||
// 组包返回
|
||||
retBytes := make([]byte, 0)
|
||||
retBytes = append(retBytes, RecordHeadSerialize(recordHead)[0:]...)
|
||||
retBytes = append(retBytes, data[0:]...)
|
||||
return retBytes
|
||||
}
|
||||
|
||||
// GetRecordDataByLength 根据长度获取RecordData
|
||||
func GetRecordDataByLength(recordType byte, len uint16) []byte {
|
||||
recordHead := &RecordHead{}
|
||||
recordHead.Type = recordType
|
||||
recordHead.Tag = 0xF103
|
||||
recordHead.Size = len
|
||||
return RecordHeadSerialize(recordHead)
|
||||
}
|
||||
|
||||
// CreateHandShakeClientHelloData 创建ClientHello数据包
|
||||
func CreateHandShakeClientHelloData(mmInfo *MMInfo) []byte {
|
||||
clientHello := &ClientHello{}
|
||||
// Version
|
||||
clientHello.Version = 0xF103
|
||||
// CipherSuiteList
|
||||
clientHello.CipherSuiteList = make([]*CipherSuite, 2)
|
||||
cipherSuite1 := &CipherSuite{}
|
||||
cipherSuite1.SuiteCode = 0xC02B
|
||||
clientHello.CipherSuiteList[0] = cipherSuite1
|
||||
cipherSuite2 := &CipherSuite{}
|
||||
cipherSuite2.SuiteCode = 0xA8
|
||||
clientHello.CipherSuiteList[1] = cipherSuite2
|
||||
// RandomBytes
|
||||
clientHello.RandomBytes = baseutils.RandomBytes(32)
|
||||
// ClientGmtTime
|
||||
clientHello.ClientGmtTime = (uint32)(time.Now().UnixNano() / 1000000000)
|
||||
// ExtensionList
|
||||
extensionList := make([]*Extension, 0)
|
||||
pskCount := len(mmInfo.ShortPskList)
|
||||
if pskCount > 1 {
|
||||
// 握手是用最后一个
|
||||
extensionList = append(extensionList, CreatePreSharedKeyExtensionData(mmInfo.ShortPskList[pskCount-1]))
|
||||
}
|
||||
|
||||
// 随机生成两队ECDHKey
|
||||
extensionList = append(extensionList, CreateClientKeyShareExtensionData(mmInfo.ClientEcdhKeys))
|
||||
clientHello.ExtensionList = extensionList
|
||||
|
||||
return ClientHelloSerialize(clientHello)
|
||||
}
|
||||
|
||||
// CreatePreSharedKeyExtensionData 创建CreatePreSharedKeyExtension数据
|
||||
func CreatePreSharedKeyExtensionData(psk *Psk) *Extension {
|
||||
preSharedKeyExtension := &PreSharedKeyExtension{}
|
||||
preSharedKeyExtension.PskList = make([]*Psk, 1)
|
||||
// 选取前面协商的最后一个Psk
|
||||
preSharedKeyExtension.PskList[0] = psk
|
||||
// 序列化
|
||||
retExtension := PreSharedKeyExtensionSerialize(preSharedKeyExtension)
|
||||
return retExtension
|
||||
}
|
||||
|
||||
// CreateClientKeyShareExtensionData 创建ClientKeyShareExtension数据
|
||||
func CreateClientKeyShareExtensionData(clientEcdhKeys *ClientEcdhKeys) *Extension {
|
||||
// retExtension
|
||||
clientKeyShareExtension := &ClientKeyShareExtension{}
|
||||
|
||||
// ClientKeyOfferList
|
||||
clientKeyShareExtension.ClientKeyOfferList = make([]*ClientKeyOffer, 2)
|
||||
// 随机第一个EcdhKey
|
||||
clientKeyShareExtension.ClientKeyOfferList[0] = CreateClientKeyOfferData(1, clientEcdhKeys.PubKeyBuf1)
|
||||
// 随机第一个EcdhKey
|
||||
clientKeyShareExtension.ClientKeyOfferList[1] = CreateClientKeyOfferData(2, clientEcdhKeys.PubKeyBuf2)
|
||||
// CertificateVersion
|
||||
clientKeyShareExtension.CertificateVersion = 1
|
||||
|
||||
// 返回序列化的ClientKeyShareExtension
|
||||
return ClientKeyShareExtensionSerialize(clientKeyShareExtension)
|
||||
}
|
||||
|
||||
// CreateClientKeyOfferData 创建CreateClientKeyOffer数据
|
||||
func CreateClientKeyOfferData(version uint32, publicKey []byte) *ClientKeyOffer {
|
||||
clientKeyOffser := &ClientKeyOffer{}
|
||||
clientKeyOffser.PublicValue = publicKey
|
||||
clientKeyOffser.Version = version
|
||||
|
||||
return clientKeyOffser
|
||||
}
|
||||
|
||||
// CreateClientHelloData 创建ClientHello数据包
|
||||
func CreateClientHelloData(mmInfo *MMInfo) (*ClientHello, error) {
|
||||
clientHello := &ClientHello{}
|
||||
// Version
|
||||
clientHello.Version = 0xF103
|
||||
// CipherSuiteList
|
||||
clientHello.CipherSuiteList = make([]*CipherSuite, 1)
|
||||
cipherSuite := &CipherSuite{}
|
||||
cipherSuite.SuiteCode = 0xA8
|
||||
clientHello.CipherSuiteList[0] = cipherSuite
|
||||
// RandomBytes
|
||||
clientHello.RandomBytes = baseutils.RandomBytes(32)
|
||||
// ClientGmtTime
|
||||
clientHello.ClientGmtTime = (uint32)(time.Now().UnixNano() / 1000000000)
|
||||
// ExtensionList
|
||||
extensionList := make([]*Extension, 0)
|
||||
pskCount := len(mmInfo.ShortPskList)
|
||||
if pskCount <= 0 {
|
||||
return nil, errors.New("CreateClientHelloData error: mmInfo.PskList empty")
|
||||
}
|
||||
extensionList = append(extensionList, CreatePreSharedKeyExtensionData(mmInfo.ShortPskList[0]))
|
||||
clientHello.ExtensionList = extensionList
|
||||
|
||||
return clientHello, nil
|
||||
}
|
||||
|
||||
// CreateEarlyEncryptDataExtension 创建EarlyEncryptDataExtension
|
||||
func CreateEarlyEncryptDataExtension() *Extension {
|
||||
retEarlyEncryptDataExtension := &EarlyEncryptDataExtension{}
|
||||
retEarlyEncryptDataExtension.ClientGmtTime = (uint32)(time.Now().UnixNano() / 1000000000)
|
||||
return EarlyEncryptDataExtensionSerialize(retEarlyEncryptDataExtension)
|
||||
}
|
||||
|
||||
// CreateEncryptedExtensions 创建EncryptedExtensions
|
||||
func CreateEncryptedExtensions() *EncryptedExtensions {
|
||||
retEncryptedExtensions := &EncryptedExtensions{}
|
||||
|
||||
// ExtensionList
|
||||
retEncryptedExtensions.ExtensionList = make([]*Extension, 1)
|
||||
retEncryptedExtensions.ExtensionList[0] = CreateEarlyEncryptDataExtension()
|
||||
|
||||
return retEncryptedExtensions
|
||||
}
|
||||
|
||||
// CreateFinished 创建Finish包
|
||||
func CreateFinished(verifyData []byte) *Finished {
|
||||
retFinished := &Finished{}
|
||||
retFinished.VerifyData = verifyData
|
||||
return retFinished
|
||||
}
|
||||
|
||||
// GetAlertData 获取Alert数据
|
||||
func GetAlertData() []byte {
|
||||
return []byte{0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x01}
|
||||
}
|
||||
246
clientsdk/mmtls/mmstructures.go
Normal file
246
clientsdk/mmtls/mmstructures.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto"
|
||||
"math/big"
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
// AesGcmParam AesGcm加密解密参数
|
||||
type AesGcmParam struct {
|
||||
AesKey []byte
|
||||
Nonce []byte
|
||||
}
|
||||
|
||||
// ClientEcdhKeys 客户端随机的两个EcdhKey私钥
|
||||
type ClientEcdhKeys struct {
|
||||
PriKey1 crypto.PrivateKey
|
||||
PubKeyBuf1 []byte
|
||||
PriKey2 crypto.PrivateKey
|
||||
PubKeyBuf2 []byte
|
||||
}
|
||||
|
||||
// HkdfKey28 HkdfKey28
|
||||
type HkdfKey28 struct {
|
||||
AesKey []byte
|
||||
Nonce []byte
|
||||
}
|
||||
|
||||
// HkdfKey56 HkdfKey56
|
||||
type HkdfKey56 struct {
|
||||
EncodeAesKey []byte
|
||||
EncodeNonce []byte
|
||||
DecodeAesKey []byte
|
||||
DecodeNonce []byte
|
||||
}
|
||||
|
||||
// MMInfo MMInfo
|
||||
type MMInfo struct {
|
||||
// 短链接 属性
|
||||
// mmtls 协议host 例如:hkextshort.weixin.qq.com,这个需要保存这数据库
|
||||
ShortHost string
|
||||
// mmtls路径 -- 例如:/mmtls/12345678(随机8位16进制字符串),每次握手都随机一个
|
||||
ShortURL string
|
||||
// 短链接会话票据(服务端返回, 第一次握手不设置), 下一次握手选择其中一个发给服务器, 需要保存到数据库
|
||||
ShortPskList []*Psk
|
||||
// 握手扩展出来的用于后续加密的Key
|
||||
PskAccessKey []byte
|
||||
|
||||
// 长链接 属性
|
||||
LongHost string
|
||||
LONGPort uint32
|
||||
|
||||
// Deprecated:
|
||||
LONGClientSeq uint32 `json:"-"` // 不持久化
|
||||
// Deprecated:
|
||||
LONGServerSeq uint32 `json:"-"` // 不持久化
|
||||
// Deprecated:
|
||||
Conn net.Conn `json:"-"` // 不持久化
|
||||
reader *bufio.Reader
|
||||
|
||||
LongHdkfKey *HkdfKey56
|
||||
// ClientEcdhKeys
|
||||
ClientEcdhKeys *ClientEcdhKeys
|
||||
// 代理
|
||||
Dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// EcdsaSignature 服务端传过来的校验数据
|
||||
type EcdsaSignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
// CipherSuiteInfo CipherSuiteInfo
|
||||
type CipherSuiteInfo struct {
|
||||
SuiteCode uint16
|
||||
Clipher1 string
|
||||
Clipher2 string
|
||||
Clipher3 string
|
||||
Clipher4 string
|
||||
Clipher5 string
|
||||
Length1 uint32
|
||||
Length2 uint32
|
||||
Length3 uint32
|
||||
}
|
||||
|
||||
// CipherSuite CipherSuite
|
||||
type CipherSuite struct {
|
||||
SuiteCode uint16
|
||||
SuiteInfo *CipherSuiteInfo
|
||||
}
|
||||
|
||||
// ClientKeyOffer ClientKeyOffer
|
||||
type ClientKeyOffer struct {
|
||||
Version uint32
|
||||
PublicValue []byte
|
||||
}
|
||||
|
||||
// CertificateVerify CertificateVerify
|
||||
type CertificateVerify struct {
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
// ClientKeyShareExtension ClientKeyShareExtension
|
||||
type ClientKeyShareExtension struct {
|
||||
ClientKeyOfferList []*ClientKeyOffer
|
||||
CertificateVersion uint32
|
||||
}
|
||||
|
||||
// EarlyEncryptDataExtension EarlyEncryptDataExtension
|
||||
type EarlyEncryptDataExtension struct {
|
||||
ClientGmtTime uint32
|
||||
}
|
||||
|
||||
// PreSharedKeyExtension PreSharedKeyExtension
|
||||
type PreSharedKeyExtension struct {
|
||||
PskList []*Psk
|
||||
}
|
||||
|
||||
// ServerKeyShareExtension ServerKeyShareExtension
|
||||
type ServerKeyShareExtension struct {
|
||||
KeyOfferNameGroup uint32
|
||||
PublicValue []byte
|
||||
}
|
||||
|
||||
// Extension Extension
|
||||
type Extension struct {
|
||||
ExtensionType uint16
|
||||
ExtensionData []byte
|
||||
}
|
||||
|
||||
// EncryptedExtensions EncryptedExtensions
|
||||
type EncryptedExtensions struct {
|
||||
ExtensionList []*Extension
|
||||
}
|
||||
|
||||
// ClientHello ClientHello
|
||||
type ClientHello struct {
|
||||
Version uint16
|
||||
CipherSuiteList []*CipherSuite
|
||||
RandomBytes []byte
|
||||
ClientGmtTime uint32
|
||||
ExtensionList []*Extension
|
||||
}
|
||||
|
||||
// ServerHello ServerHello
|
||||
type ServerHello struct {
|
||||
Version uint16
|
||||
CipherSuite *CipherSuite
|
||||
RandomBytes []byte
|
||||
ExtensionList []*Extension
|
||||
}
|
||||
|
||||
// Psk Psk
|
||||
type Psk struct {
|
||||
Type byte
|
||||
TicketKLifeTimeHint uint32
|
||||
MacValue []byte
|
||||
KeyVersion uint32
|
||||
Iv []byte
|
||||
EncryptedTicket []byte
|
||||
}
|
||||
|
||||
// ClientPsk CLientPsk
|
||||
type ClientPsk struct {
|
||||
Psk *Psk
|
||||
PskExpiredTime uint64
|
||||
PreSharedKey []byte
|
||||
}
|
||||
|
||||
// Finished Finished
|
||||
type Finished struct {
|
||||
VerifyData []byte
|
||||
}
|
||||
|
||||
// HTTPHandler HttpHandler
|
||||
type HTTPHandler struct {
|
||||
URL string
|
||||
Host string
|
||||
MMPkg []byte
|
||||
}
|
||||
|
||||
// KeyPair ECDH信息
|
||||
type KeyPair struct {
|
||||
Version uint32
|
||||
Nid uint32
|
||||
PublicKey []byte
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
// NewSessionTicket NewSessionTicket
|
||||
type NewSessionTicket struct {
|
||||
PskList []*Psk
|
||||
}
|
||||
|
||||
// PskTicket PskTicket
|
||||
type PskTicket struct {
|
||||
Version byte
|
||||
MMTlsVersion uint16
|
||||
CipherSuite *CipherSuite
|
||||
KeyVersion uint32
|
||||
TicketKLifeTimeHint uint32
|
||||
PreSharedKey []byte
|
||||
MacKey []byte
|
||||
ClientGmtTime uint32
|
||||
ServerGmtTime uint32
|
||||
EcdhVersion uint32
|
||||
Valid byte
|
||||
}
|
||||
|
||||
// RecordHead RecordHead
|
||||
type RecordHead struct {
|
||||
Type byte
|
||||
Tag uint16
|
||||
Size uint16
|
||||
}
|
||||
|
||||
// Alert Alert
|
||||
type Alert struct {
|
||||
AlertLevel byte
|
||||
AlertType uint16
|
||||
FallBackURL []byte
|
||||
SignatureURL []byte
|
||||
}
|
||||
|
||||
// PackItem 包数量
|
||||
type PackItem struct {
|
||||
RecordHead []byte
|
||||
PackData []byte
|
||||
}
|
||||
|
||||
// LongPackHeaderInfo 长链接请求包头部信息
|
||||
type LongPackHeaderInfo struct {
|
||||
HeaderLen uint16
|
||||
Version uint16
|
||||
Operation uint32
|
||||
SequenceNumber uint32
|
||||
}
|
||||
|
||||
// LongRecvInfo 长链接接收信息
|
||||
type LongRecvInfo struct {
|
||||
HeaderInfo *LongPackHeaderInfo
|
||||
RespData []byte
|
||||
}
|
||||
33
clientsdk/mmtls/mmtlslog.go
Normal file
33
clientsdk/mmtls/mmtlslog.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package mmtls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"xiawan/wx/clientsdk/baseutils"
|
||||
)
|
||||
|
||||
// ShowMMTLSExtensions 打印Extensions
|
||||
func ShowMMTLSExtensions(extensionList []*Extension) {
|
||||
fmt.Println("--------------- ShowMMTLSExtensions in ---------------")
|
||||
extensionLength := len(extensionList)
|
||||
for index := 0; index < extensionLength; index++ {
|
||||
// PreSharedKeyExtensionType
|
||||
if extensionList[index].ExtensionType == PreSharedKeyExtensionType {
|
||||
tmpExtension, _ := PreSharedKeyExtensionDeSerialize(extensionList[index].ExtensionData)
|
||||
baseutils.ShowObjectValue(tmpExtension)
|
||||
}
|
||||
|
||||
// ClientKeyShareType
|
||||
if extensionList[index].ExtensionType == ClientKeyShareType {
|
||||
tmpExtension, _ := ClientKeyShareExtensionDeSerialize(extensionList[index].ExtensionData)
|
||||
baseutils.ShowObjectValue(tmpExtension)
|
||||
}
|
||||
|
||||
// ServerKeyShareType
|
||||
if extensionList[index].ExtensionType == ServerKeyShareType {
|
||||
tmpExtension, _ := ServerKeyShareExtensionDeSerialize(extensionList[index].ExtensionData)
|
||||
baseutils.ShowObjectValue(tmpExtension)
|
||||
}
|
||||
}
|
||||
fmt.Println("--------------- ShowMMTLSExtensions out ---------------")
|
||||
}
|
||||
82
clientsdk/mmtls/mmtypedefine.go
Normal file
82
clientsdk/mmtls/mmtypedefine.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package mmtls
|
||||
|
||||
const (
|
||||
// ClientHandShakeType ClientHandShakeType
|
||||
ClientHandShakeType byte = 25
|
||||
// ServerHandShakeType ServerHandShakeType
|
||||
ServerHandShakeType byte = 22
|
||||
// BodyType BodyType
|
||||
BodyType byte = 23
|
||||
// AlertType AlertType
|
||||
AlertType byte = 21
|
||||
|
||||
// ClientHelloType ClientHelloType
|
||||
ClientHelloType byte = 1
|
||||
// ServerHelloType ServerHelloType
|
||||
ServerHelloType byte = 2
|
||||
// NewSessionTicketType NewSessionTicketType
|
||||
NewSessionTicketType byte = 4
|
||||
// EncryptedExtensionsType EncryptedExtensionsType
|
||||
EncryptedExtensionsType byte = 8
|
||||
// CertificateVerifyType CertificateVerifyType
|
||||
CertificateVerifyType byte = 15
|
||||
// FinishedType FinishedType
|
||||
FinishedType byte = 20
|
||||
|
||||
// PreSharedKeyExtensionType PreSharedKeyExtensionType
|
||||
PreSharedKeyExtensionType uint16 = 15
|
||||
// ClientKeyShareType ClientKeyShareType
|
||||
ClientKeyShareType uint16 = 16
|
||||
// ServerKeyShareType ServerKeyShareType
|
||||
ServerKeyShareType uint16 = 17
|
||||
// EarlyEncryptDataType EarlyEncryptDataType
|
||||
EarlyEncryptDataType uint16 = 18
|
||||
|
||||
// MaxCipherSuiteSize MaxCipherSuiteSize
|
||||
MaxCipherSuiteSize uint32 = 2
|
||||
// FixedRandomSize FixedRandomSize
|
||||
FixedRandomSize uint32 = 32
|
||||
// MaxNewSessionTicketPskSize MaxNewSessionTicketPskSize
|
||||
MaxNewSessionTicketPskSize uint32 = 2
|
||||
// MaxSignatureSize MaxSignatureSize
|
||||
MaxSignatureSize uint32 = 2048
|
||||
// MaxFinishedVerifyDataSize MaxFinishedVerifyDataSize
|
||||
MaxFinishedVerifyDataSize uint32 = 2048
|
||||
// MaxDataPackSize MaxDataPackSize
|
||||
MaxDataPackSize uint32 = 0x8000000
|
||||
|
||||
// MaxExtensionSize MaxExtensionSize
|
||||
MaxExtensionSize uint32 = 256
|
||||
// MaxKeyOfferSize MaxKeyOfferSize
|
||||
MaxKeyOfferSize uint32 = 256
|
||||
// MaxPublicValueSize MaxPublicValueSize
|
||||
MaxPublicValueSize uint32 = 256
|
||||
|
||||
// FixedRecordHeadSize FixedRecordHeadSize
|
||||
FixedRecordHeadSize uint32 = 5
|
||||
|
||||
// MMLongVersion 长链接版本号
|
||||
MMLongVersion uint16 = 1
|
||||
|
||||
// MMLongOperationSmartHeartBeat 心跳包
|
||||
MMLongOperationSmartHeartBeat uint32 = 0x06
|
||||
// MMLongOperationSmartHeartBeatBackUp 心跳包请求-备用
|
||||
MMLongOperationSmartHeartBeatBackUp uint32 = 0x0c
|
||||
// MMLongOperationServerPush 服务端推送消息
|
||||
MMLongOperationServerPush uint32 = 0x7a
|
||||
// MMLongOperationGetOnlineInfo 发起GetOnlineInfo请求
|
||||
MMLongOperationGetOnlineInfo uint32 = 0xcd
|
||||
// MMLongOperationCheckQrcode 检测二维码状态请求
|
||||
MMLongOperationCheckQrcode uint32 = 0xe9
|
||||
// MMLongOperationRequest 发起请求
|
||||
MMLongOperationRequest uint32 = 0xed
|
||||
// MMLongOperationHeartBeat 发送心跳包请求
|
||||
MMLongOperationHeartBeat uint32 = 0xee
|
||||
//MMLongOperationSystemPush 系统推送
|
||||
MMLongOperationSystemPush uint32 = 0x18
|
||||
|
||||
// MMLongSystemPushTypeSync 系统推送:需要同步
|
||||
MMLongSystemPushTypeSync int32 = 2
|
||||
// MMLongSystemPushTypeLogout 系统推送:被退出登陆
|
||||
MMLongSystemPushTypeLogout int32 = -1
|
||||
)
|
||||
Reference in New Issue
Block a user