1、當有客戶端連接成功時,就在服務器端生成一個RSA密鑰對
2、服務器將公鑰發送給客戶端
3、客戶端生成一個對稱加密的密鑰
4、客戶端用公鑰對對稱加密的密鑰加密,發送給服務器端
5、服務器用私鑰對密文進行解密,拿到對稱加密的密鑰
6、雙方使用對稱加密的方式進行通信
server.go
func main() { //服務器地址信息 udpaddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8888") if err != nil { panic(err) } conn, err1 := net.ListenUDP("udp", udpaddr) if err1 != nil { panic(err) } defer conn.Close() count := 0 length := 0 data := make([]byte, 4096) var aesKey []byte var privateKey *rsa.PrivateKey //通信 for { count++ //接受客戶端發送來的消息 n, raddr, err := conn.ReadFromUDP(data) length = n if count == 1 { //1、生成密鑰對,指定密鑰私鑰長度為1024 privateKey, err = rsa.GenerateKey(rand.Reader, 1024) if err != nil { panic(err) } //2、取出公鑰 publickey := privateKey.PublicKey //3、x509格式化公鑰 pubder, err := x509.MarshalPKIXPublicKey(&publickey) if err != nil { panic(err) } //4、pem block block := pem.Block{Type: "rsa publickkey", Bytes: pubder, } //5、pem編碼得到base64編碼 var buf bytes.Buffer pem.Encode(&buf, &block) //將編碼好的公鑰發送給客戶端 _, err = conn.WriteToUDP(buf.Bytes(), raddr) if err != nil { panic(err) } } if count == 2 { //接受客戶端發送來的對稱密鑰,由於客戶端發送來的數據是經過16進制編碼,因此要解碼 text, err := hex.DecodeString(string(data[:length])) if err != nil { panic(err) } if err != nil { panic(err) } //私鑰解密,拿到對稱加密的密鑰 aesKey, err = rsa.DecryptPKCS1v15(rand.Reader, privateKey, text) fmt.Println("對稱加密的密鑰:", aesKey) conn.WriteToUDP([]byte("對稱密鑰接受完畢"), raddr) } //往后就是對稱加密通信 if count > 2 { //創建aes接口 block, err := aes.NewCipher(aesKey) if err != nil { panic(err) } //創建一個使用CTR分組的接口 stream := cipher.NewCTR(block, aesKey) //解碼拿到密文 cipherText, err := hex.DecodeString(string(data[:length])) if err != nil { panic(err) } plainText := make([]byte, len(cipherText)) stream.XORKeyStream(plainText, cipherText) fmt.Println("接受並解析的數據:", string(plainText)) conn.WriteToUDP([]byte("加密數據接受完畢"), raddr) } } }
client.go
func main() { conn, err := net.Dial("udp", "127.0.0.1:8888") if err != nil { panic(err) } defer conn.Close() count := 0 var publicKey *rsa.PublicKey data := make([]byte, 4096) //以下三行產生對稱加密的密鑰 hvalue := md5.Sum([]byte("1234abcd")) //得到16字節二進制哈希值 hexText := hex.EncodeToString(hvalue[:]) //把二進制轉換為16進制字符串 長度為16*2 aesKey := []byte(hexText)[:aes.BlockSize] //把32字節長的密鑰階段為16字節 for { count++ if count == 1 { //向服務器say hello conn.Write([]byte("hello i am client")) //阻塞。。。 //收到服務器發送來的公鑰 n, err := conn.Read(data) if err != nil { panic(err) } fmt.Println(string(data[:n])) //pem解碼 block, _ := pem.Decode(data[:n]) //x509解碼 pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic(err) } //類型斷言,拿到公鑰 publicKey = pubInterface.(*rsa.PublicKey) } if count == 2 { //使用公鑰加密aeskey //通過公鑰加密的密文是二進制,在網絡通信時不要發送二進制,因為有不可見字符 //最好給他編碼,base64或者16進制編碼, cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, aesKey) if err != nil { panic(err) } //發送給服務器 conn.Write([]byte(hex.EncodeToString(cipherText))) } //開始使用對稱加密通信 if count > 2 { block, err := aes.NewCipher(aesKey) if err != nil { panic(err) } //創建ctr模式接口 text := []byte("[對稱加密通信]你吃了嗎?") stream := cipher.NewCTR(block, aesKey) stream.XORKeyStream(text, text) conn.Write([]byte(hex.EncodeToString(text))) } if count > 1 { //阻塞 n, _ := conn.Read(data) fmt.Println("接受到的數據:", string(data[:n])) } time.Sleep(time.Second) } }