基于Golang的RSA加密通信:无法解密客户端消息问题
RSA通信解密失败问题修复
问题核心原因
- 客户端未加密发送消息:客户端直接将明文发送给服务器,但服务器尝试解密该明文,导致解密失败。
- 公钥PEM块类型不匹配:生成公钥时使用
x509.MarshalPKIXPublicKey(PKIX格式),但PEM块类型标注为RSA PUBLIC KEY,正确类型应为PUBLIC KEY,可能导致公钥加载异常。 - TCP传输方式错误:使用
ReadString('\n')处理加密消息,而加密后的二进制数据可能包含换行符,导致读取不完整或错误。 - 客户端接收逻辑混乱:接收服务器回复时先执行加密操作,再判断读取错误,逻辑顺序错误,且错误地加密服务器回复后打印。
修复后的代码
generatekeys.go(修复公钥PEM类型)
package main import ( "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" "os" ) func main() { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { fmt.Println("Error generating private key:", err) os.Exit(1) } publicKey := &privateKey.PublicKey savePEMKey("private.pem", privateKey) savePublicPEMKey("public.pem", publicKey) } func savePEMKey(filename string, key *rsa.PrivateKey) { outFile, err := os.Create(filename) if err != nil { fmt.Println("Error creating file:", err) os.Exit(1) } defer outFile.Close() var privateKey = &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), } pem.Encode(outFile, privateKey) } func savePublicPEMKey(filename string, pubkey *rsa.PublicKey) { pubASN1, err := x509.MarshalPKIXPublicKey(pubkey) if err != nil { fmt.Println("Error marshalling public key:", err) os.Exit(1) } // 修正PEM块类型为PUBLIC KEY,匹配PKIX格式 var pemkey = &pem.Block{ Type: "PUBLIC KEY", Bytes: pubASN1, } outFile, err := os.Create(filename) if err != nil { fmt.Println("Error creating file:", err) os.Exit(1) } defer outFile.Close() pem.Encode(outFile, pemkey) }
client.go(修复发送加密、接收逻辑)
package main import ( "bufio" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "log" "net" "os" ) func main() { conn, err := net.Dial("tcp", "127.0.0.1:8081") if err != nil { fmt.Println("Error connecting: ", err.Error()) return } defer conn.Close() go receive(conn) sc := bufio.NewScanner(os.Stdin) pubKey := getPublicKey() for sc.Scan() { if sc.Err() != nil { fmt.Println("scanner error!") break } txt := sc.Text() // 使用服务器公钥加密消息 encryptedMsg, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, []byte(txt)) if err != nil { fmt.Println("Error encrypting message:", err) break } // 先发送密文长度,再发送密文(解决TCP字节流粘包问题) _, err = conn.Write([]byte(fmt.Sprintf("%d\n", len(encryptedMsg)))) if err != nil { fmt.Println("Failed to send length to server!") break } _, err = conn.Write(encryptedMsg) if err != nil { fmt.Println("Failed to send encrypted data to server!") break } } } func receive(c net.Conn) { rd := bufio.NewReader(c) for { // 读取服务器回复的长度 lenStr, err := rd.ReadString('\n') if err != nil { fmt.Println("Failed to receive msg length from server!") break } var msgLen int _, err = fmt.Sscanf(lenStr, "%d", &msgLen) if err != nil { fmt.Println("Failed to parse msg length!") break } // 读取对应长度的加密回复 encryptedReply := make([]byte, msgLen) _, err = io.ReadFull(rd, encryptedReply) if err != nil { fmt.Println("Failed to receive encrypted reply!") break } // 用服务器公钥解密回复(实际场景建议客户端生成自身密钥对,服务器用客户端公钥加密) decryptedReply, err := rsa.DecryptPKCS1v15(rand.Reader, getClientPrivateKey(), encryptedReply) if err != nil { fmt.Println("Error decrypting server reply:", err) break } fmt.Println("Server replied:", string(decryptedReply)) } c.Close() } func loadPublicKey(filename string) (*rsa.PublicKey, error) { data, err := os.ReadFile(filename) if err != nil { return nil, err } block, _ := pem.Decode(data) if block == nil || block.Type != "PUBLIC KEY" { return nil, errors.New("failed to decode PEM block containing public key") } pkey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic(err) } rsaKey, ok := pkey.(*rsa.PublicKey) if !ok { log.Fatalf("got unexpected key type: %T", pkey) } return rsaKey, nil } func getPublicKey() *rsa.PublicKey { publicKey, err := loadPublicKey("public.pem") if err != nil { fmt.Println("Error loading public key:", err) os.Exit(1) } return publicKey } // 客户端自身私钥,需提前生成(可复用generatekeys.go逻辑生成private_client.pem) func getClientPrivateKey() *rsa.PrivateKey { data, err := os.ReadFile("private_client.pem") if err != nil { fmt.Println("Error loading client private key:", err) os.Exit(1) } block, _ := pem.Decode(data) if block == nil || block.Type != "RSA PRIVATE KEY" { fmt.Println("failed to decode PEM block containing client private key") os.Exit(1) } privKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { fmt.Println("Error parsing client private key:", err) os.Exit(1) } return privKey }
server.go(修复接收解密、广播逻辑)
package main import ( "bufio" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "net" "os" ) type server struct { clients []net.Conn } func (s *server) addClient(c net.Conn) { s.clients = append(s.clients, c) } func main() { fmt.Println("Launching server...") srv := &server{} ln, err := net.Listen("tcp", ":8081") if err != nil { fmt.Println("Error listening: ", err.Error()) os.Exit(1) } defer ln.Close() for { conn, err := ln.Accept() if err != nil { fmt.Println("Error accepting: ", err.Error()) continue } srv.addClient(conn) go srv.recvAndEcho(conn) } } func (s *server) recvAndEcho(c net.Conn) { defer c.Close() privKey := getPrivateKey() rd := bufio.NewReader(c) for { // 读取客户端发送的密文长度 lenStr, err := rd.ReadString('\n') if err != nil { fmt.Println("Error reading length:", err) // 移除断开连接的客户端 s.removeClient(c) return } var msgLen int _, err = fmt.Sscanf(lenStr, "%d", &msgLen) if err != nil { fmt.Println("Error parsing length:", err) return } // 读取对应长度的密文 encryptedMsg := make([]byte, msgLen) _, err = io.ReadFull(rd, encryptedMsg) if err != nil { fmt.Println("Error reading encrypted message:", err) s.removeClient(c) return } // 解密消息 decryptedMessage, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, encryptedMsg) if err != nil { fmt.Println("Error decrypting message:", err) return } fmt.Println("Received from client:", string(decryptedMessage)) // 广播解密后的消息(加密后广播,确保传输安全) s.broadcastMsg(string(decryptedMessage)) } } func (s *server) broadcastMsg(msg string) { // 复用服务器公钥加密回复(实际场景建议使用客户端各自的公钥) pubKey := &getPrivateKey().PublicKey for _, cl := range s.clients { encryptedReply, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, []byte(msg)) if err != nil { fmt.Println("Error encrypting reply:", err) continue } // 先发送长度,再发送密文 _, err = cl.Write([]byte(fmt.Sprintf("%d\n", len(encryptedReply)))) if err != nil { fmt.Println("Failed to send reply length!") continue } _, err = cl.Write(encryptedReply) if err != nil { fmt.Println("Failed to send encrypted reply!") continue } } } func (s *server) removeClient(c net.Conn) { for i, client := range s.clients { if client == c { s.clients = append(s.clients[:i], s.clients[i+1:]...) break } } } func loadPrivateKey(filename string) (*rsa.PrivateKey, error) { data, err := os.ReadFile(filename) if err != nil { return nil, err } block, _ := pem.Decode(data) if block == nil || block.Type != "RSA PRIVATE KEY" { return nil, errors.New("failed to decode PEM block containing private key") } return x509.ParsePKCS1PrivateKey(block.Bytes) } func getPrivateKey() *rsa.PrivateKey { privateKey, err := loadPrivateKey("private.pem") if err != nil { fmt.Println("Error loading private key:", err) os.Exit(1) } return privateKey }
关键修复点说明
- 客户端发送加密消息:客户端读取输入后,使用服务器公钥加密消息,先发送密文长度再发送密文,避免TCP字节流粘包问题。
- 服务器正确接收解密:服务器先读取密文长度,再读取对应长度的密文,然后用私钥解密。
- 修正公钥PEM类型:生成公钥时,PEM块类型改为
PUBLIC KEY,与PKIX格式匹配,确保公钥正常加载。 - 修复客户端接收逻辑:调整错误判断顺序,先读取服务器回复的长度和密文,再解密并打印。
- 客户端密钥对补充:如果需要解密服务器回复,客户端需生成自己的密钥对,服务器用客户端公钥加密回复,客户端用自己的私钥解密(代码中已预留相关逻辑)。
内容的提问来源于stack exchange,提问作者Biswajit Das
相关产品推荐
相关产品推荐

