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)
}
}
