You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Golang的RSA加密通信:无法解密客户端消息问题

RSA通信解密失败问题修复

问题核心原因

  1. 客户端未加密发送消息:客户端直接将明文发送给服务器,但服务器尝试解密该明文,导致解密失败。
  2. 公钥PEM块类型不匹配:生成公钥时使用x509.MarshalPKIXPublicKey(PKIX格式),但PEM块类型标注为RSA PUBLIC KEY,正确类型应为PUBLIC KEY,可能导致公钥加载异常。
  3. TCP传输方式错误:使用ReadString('\n')处理加密消息,而加密后的二进制数据可能包含换行符,导致读取不完整或错误。
  4. 客户端接收逻辑混乱:接收服务器回复时先执行加密操作,再判断读取错误,逻辑顺序错误,且错误地加密服务器回复后打印。

修复后的代码

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
}

关键修复点说明

  1. 客户端发送加密消息:客户端读取输入后,使用服务器公钥加密消息,先发送密文长度再发送密文,避免TCP字节流粘包问题。
  2. 服务器正确接收解密:服务器先读取密文长度,再读取对应长度的密文,然后用私钥解密。
  3. 修正公钥PEM类型:生成公钥时,PEM块类型改为PUBLIC KEY,与PKIX格式匹配,确保公钥正常加载。
  4. 修复客户端接收逻辑:调整错误判断顺序,先读取服务器回复的长度和密文,再解密并打印。
  5. 客户端密钥对补充:如果需要解密服务器回复,客户端需生成自己的密钥对,服务器用客户端公钥加密回复,客户端用自己的私钥解密(代码中已预留相关逻辑)。

内容的提问来源于stack exchange,提问作者Biswajit Das

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:29:55