使用来自 java 的 public 密钥实现进行 RSA 解密
Go RSA decrypt using public key implementation from java
提供者有此示例 JAVA 代码使用 public 密钥解密 RSA。
public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet >(2048/8)) {
cache = cipher.doFinal(encryptedData, offSet,(2048/8));
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i *(2048/8);
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
我曾尝试在 Go 中编写等价物,但运气不佳。
base64DecodeBytesKey, err := base64.StdEncoding.DecodeString(os.Getenv("PUBKEY"))
if err != nil {
Log(logRef, " error reading pubkey", err)
}
pubKey, err := x509.ParsePKCS1PrivateKey(base64DecodeBytesKey)
c := new(big.Int)
m := new(big.Int)
m.SetBytes(data)
e := big.NewInt(int64(pubKey.E))
c.Exp(m, e, pubKey.N)
out := c.Bytes()
skip := 0
Log(" payload size:--> ", len(data))
for i := 2; i < len(out); i++ {
if i+1 >= len(out) {
break
}
if out[i] == 0xff && out[i+1] == 0 {
skip = i + 2
break
}
}
return out[skip:]
以上是第一次尝试,但失败得很厉害。
rsa.DecryptPKCS1v15
确实需要私钥,提供商坚持认为没有必要。
encryptedBlockBytes, err := rsa.DecryptPKCS1v15(
rand.Reader,
NO_PRIVATE_KEY_PROVIDED,
payloadBytes[start:finish])
有没有办法使用 Go crypt 库从 RSA 验证 PSS 中获取解密的有效负载?
从Java代码看padding不是很清楚,因为只指定了算法,没有指定padding。在这种情况下,填充依赖于提供者,例如SunJCE 提供商的 PKCS#1 v1.5。
假设 PKCS#1 v1.5,组合 Cipher.ENCRYPT_MODE
/私钥和 Cipher.DECRYPT_MODE
/public 密钥应用 RSASSA-PKCS1-v1_5 作为填充。这在功能上与 signing/verifying 和 NonewithRSA
相同(除了 使用 NonewithRSA
验证另外检查数据与 相比是否相等使用 public 密钥解密).
NonewithRSA
表示数据未经过哈希处理且未添加摘要 ID。该算法实际上旨在对已经散列的数据进行签名(在摘要 ID 被添加到前面之后)。它绝不意味着签署未散列的数据,即 Java 代码滥用了该算法。
由于 RSA 限制了消息大小(密钥大小减去填充所需的 space),因此只能对适当的短消息进行签名。因此,大小超过允许大小的未散列数据会强制执行多个签名。这就是Java代码中多次解密的原因。
使用散列数据不仅出于实际原因(签署长消息)有用,而且出于安全原因也是必要的,s。例如here and here.
要在 Go 中实现 Java 代码的功能,与 Java 代码一样,Go 中的 signing/verifying 需要一个低级实现。
另一种可能性是实现解密 (m = c^e) 并自己删除填充,对于 RSASSA-PKCS1-v1_5 来说,它仅由一系列 0xFF 值组成,左边为 0x0001,右边为 0x00。
下面我考虑第二种方法。下面的 Go 代码执行以下操作:
- 导入 public 密钥
- Base64解码密文
- 将密文拆分为单独的签名块(这里 3 个块包含相同的数据:敏捷的棕色狐狸跳过懒狗)
- 解密每个签名块(m = c ^ e)
- 连接解密的签名块
package main
import (
"fmt"
"math/big"
"encoding/pem"
"crypto/x509"
"crypto/rsa"
"encoding/base64"
"bytes"
"io"
)
func main() {
pubKeyPem := `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZ67dtUTLxoXnNEzRBFB
mwukEJGC+y69cGgpNbtElQj3m4Aft/7cu9qYbTNguTSnCDt7uovZNb21u1vpZwKH
yVgFEGO4SA8RNnjhJt2D7z8RDMWX3saody7jo9TKlrPABLZGo2o8vadW8Dly/v+I
d0YDheCkVCoCEeUjQ8koXZhTwhYkGPu+vkdiqX5cUaiVTu1uzt591aO5Vw/hV4DI
hFKnOTnYXnpXiwRwtPyYoGTa64yWfi2t0bv99qz0BgDjQjD0civCe8LRXGGhyB1U
1aHjDDGEnulTYJyEqCzNGwBpzEHUjqIOXElFjt55AFGpCHAuyuoXoP3gQvoSj6RC
sQIDAQAB
-----END PUBLIC KEY-----`
// Import public key
pubKey := ImportSPKIPublicKeyPEM(pubKeyPem);
// Base64 decode ciphertext
ciphertextBytes, _ := base64.StdEncoding.DecodeString("ajQbkszbZ97YZaPSRBab9vj0DDLm9tTrQwSZ+ucPj+cYSmw06KLCtRH3SPn3b2DqSd1revLXqxMtSzFmjRvZ5F8y3nzdP8NJaRplOigbPFhKZTv7xBVK5ATEmLukgtI7f+d3KdmGUG+cyTkfxIrMBvB3BIS5oTiMNmC9pqLaWcDVF9qpuxnwEMQJbeO9nTklpdv+F8BrchHmeUkKRrMJBoPbbcfq9Hi4bHiFyxPWhwB66d/AryCKsFRhaX6hSkTL+0NvuhVhv98wdo3juv2Il50XKOCbfc8kUG628TcSK6n31piLF9cntSVTB/L/pVfcAxEwx4hcUhLuqmk6EZIJvGo0G5LM22fe2GWj0kQWm/b49Awy5vbU60MEmfrnD4/nGEpsNOiiwrUR90j5929g6knda3ry16sTLUsxZo0b2eRfMt583T/DSWkaZTooGzxYSmU7+8QVSuQExJi7pILSO3/ndynZhlBvnMk5H8SKzAbwdwSEuaE4jDZgvaai2lnA1RfaqbsZ8BDECW3jvZ05JaXb/hfAa3IR5nlJCkazCQaD223H6vR4uGx4hcsT1ocAeunfwK8girBUYWl+oUpEy/tDb7oVYb/fMHaN47r9iJedFyjgm33PJFButvE3Eiup99aYixfXJ7UlUwfy/6VX3AMRMMeIXFIS7qppOhGSCbxqNBuSzNtn3thlo9JEFpv2+PQMMub21OtDBJn65w+P5xhKbDToosK1EfdI+fdvYOpJ3Wt68terEy1LMWaNG9nkXzLefN0/w0lpGmU6KBs8WEplO/vEFUrkBMSYu6SC0jt/53cp2YZQb5zJOR/EiswG8HcEhLmhOIw2YL2motpZwNUX2qm7GfAQxAlt472dOSWl2/4XwGtyEeZ5SQpGswkGg9ttx+r0eLhseIXLE9aHAHrp38CvIIqwVGFpfqFKRMv7Q2+6FWG/3zB2jeO6/YiXnRco4Jt9zyRQbrbxNxIrqffWmIsX1ye1JVMH8v+lV9wDETDHiFxSEu6qaToRkgm8")
// Split ciphertext into signature chunks a 2048/8 bytes and decrypt each chunk
reader := bytes.NewReader(ciphertextBytes)
var writer bytes.Buffer
ciphertextBytesChunk := make([]byte, 2048/8)
for {
n, _ := io.ReadFull(reader, ciphertextBytesChunk)
if (n == 0) {
break
}
decryptChunk(ciphertextBytesChunk, &writer, pubKey)
}
// Concatenate decrypted signature chunks
decryptedData := writer.String()
fmt.Println(decryptedData)
}
func ImportSPKIPublicKeyPEM(spkiPEM string) (*rsa.PublicKey) {
body, _ := pem.Decode([]byte(spkiPEM ))
publicKey, _ := x509.ParsePKIXPublicKey(body.Bytes)
if publicKey, ok := publicKey.(*rsa.PublicKey); ok {
return publicKey
} else {
return nil
}
}
func decryptChunk(ciphertextBytesChunk []byte , writer *bytes.Buffer, pubKey *rsa.PublicKey ){
// Decrypt each signature chunk
ciphertextInt := new(big.Int)
ciphertextInt.SetBytes(ciphertextBytesChunk)
decryptedPaddedInt := decrypt(new(big.Int), pubKey, ciphertextInt)
// Remove padding
decryptedPaddedBytes := make([]byte, pubKey.Size())
decryptedPaddedInt.FillBytes(decryptedPaddedBytes)
start := bytes.Index(decryptedPaddedBytes[1:], []byte{0}) + 1 // // 0001FF...FF00<data>: Find index after 2nd 0x00
decryptedBytes := decryptedPaddedBytes[start:]
// Write decrypted signature chunk
writer.Write(decryptedBytes)
}
func decrypt(c *big.Int, pub *rsa.PublicKey, m *big.Int) *big.Int {
// Textbook RSA
e := big.NewInt(int64(pub.E))
c.Exp(m, e, pub.N)
return c
}
输出:
The quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dog
请注意,代码只是一个示例实现,特别是不包括异常处理。
测试:
下面的Java代码
String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZ67dtUTLxoXnNEzRBFBmwukEJGC+y69cGgpNbtElQj3m4Aft/7cu9qYbTNguTSnCDt7uovZNb21u1vpZwKHyVgFEGO4SA8RNnjhJt2D7z8RDMWX3saody7jo9TKlrPABLZGo2o8vadW8Dly/v+Id0YDheCkVCoCEeUjQ8koXZhTwhYkGPu+vkdiqX5cUaiVTu1uzt591aO5Vw/hV4DIhFKnOTnYXnpXiwRwtPyYoGTa64yWfi2t0bv99qz0BgDjQjD0civCe8LRXGGhyB1U1aHjDDGEnulTYJyEqCzNGwBpzEHUjqIOXElFjt55AFGpCHAuyuoXoP3gQvoSj6RCsQIDAQAB";
byte[] ciphertext = Base64.getDecoder().decode("ajQbkszbZ97YZaPSRBab9vj0DDLm9tTrQwSZ+ucPj+cYSmw06KLCtRH3SPn3b2DqSd1revLXqxMtSzFmjRvZ5F8y3nzdP8NJaRplOigbPFhKZTv7xBVK5ATEmLukgtI7f+d3KdmGUG+cyTkfxIrMBvB3BIS5oTiMNmC9pqLaWcDVF9qpuxnwEMQJbeO9nTklpdv+F8BrchHmeUkKRrMJBoPbbcfq9Hi4bHiFyxPWhwB66d/AryCKsFRhaX6hSkTL+0NvuhVhv98wdo3juv2Il50XKOCbfc8kUG628TcSK6n31piLF9cntSVTB/L/pVfcAxEwx4hcUhLuqmk6EZIJvGo0G5LM22fe2GWj0kQWm/b49Awy5vbU60MEmfrnD4/nGEpsNOiiwrUR90j5929g6knda3ry16sTLUsxZo0b2eRfMt583T/DSWkaZTooGzxYSmU7+8QVSuQExJi7pILSO3/ndynZhlBvnMk5H8SKzAbwdwSEuaE4jDZgvaai2lnA1RfaqbsZ8BDECW3jvZ05JaXb/hfAa3IR5nlJCkazCQaD223H6vR4uGx4hcsT1ocAeunfwK8girBUYWl+oUpEy/tDb7oVYb/fMHaN47r9iJedFyjgm33PJFButvE3Eiup99aYixfXJ7UlUwfy/6VX3AMRMMeIXFIS7qppOhGSCbxqNBuSzNtn3thlo9JEFpv2+PQMMub21OtDBJn65w+P5xhKbDToosK1EfdI+fdvYOpJ3Wt68terEy1LMWaNG9nkXzLefN0/w0lpGmU6KBs8WEplO/vEFUrkBMSYu6SC0jt/53cp2YZQb5zJOR/EiswG8HcEhLmhOIw2YL2motpZwNUX2qm7GfAQxAlt472dOSWl2/4XwGtyEeZ5SQpGswkGg9ttx+r0eLhseIXLE9aHAHrp38CvIIqwVGFpfqFKRMv7Q2+6FWG/3zB2jeO6/YiXnRco4Jt9zyRQbrbxNxIrqffWmIsX1ye1JVMH8v+lV9wDETDHiFxSEu6qaToRkgm8");
byte[] decrypted = decryptByPublicKey(ciphertext, publicKey);
System.out.println(new String(decrypted, StandardCharsets.UTF_8));
使用您发布的方法给出相同的结果。
提供者有此示例 JAVA 代码使用 public 密钥解密 RSA。
public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
throws Exception {
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet >(2048/8)) {
cache = cipher.doFinal(encryptedData, offSet,(2048/8));
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i *(2048/8);
}
byte[] decryptedData = out.toByteArray();
out.close();
return decryptedData;
}
我曾尝试在 Go 中编写等价物,但运气不佳。
base64DecodeBytesKey, err := base64.StdEncoding.DecodeString(os.Getenv("PUBKEY"))
if err != nil {
Log(logRef, " error reading pubkey", err)
}
pubKey, err := x509.ParsePKCS1PrivateKey(base64DecodeBytesKey)
c := new(big.Int)
m := new(big.Int)
m.SetBytes(data)
e := big.NewInt(int64(pubKey.E))
c.Exp(m, e, pubKey.N)
out := c.Bytes()
skip := 0
Log(" payload size:--> ", len(data))
for i := 2; i < len(out); i++ {
if i+1 >= len(out) {
break
}
if out[i] == 0xff && out[i+1] == 0 {
skip = i + 2
break
}
}
return out[skip:]
以上是第一次尝试,但失败得很厉害。
rsa.DecryptPKCS1v15
确实需要私钥,提供商坚持认为没有必要。
encryptedBlockBytes, err := rsa.DecryptPKCS1v15(
rand.Reader,
NO_PRIVATE_KEY_PROVIDED,
payloadBytes[start:finish])
有没有办法使用 Go crypt 库从 RSA 验证 PSS 中获取解密的有效负载?
从Java代码看padding不是很清楚,因为只指定了算法,没有指定padding。在这种情况下,填充依赖于提供者,例如SunJCE 提供商的 PKCS#1 v1.5。
假设 PKCS#1 v1.5,组合 Cipher.ENCRYPT_MODE
/私钥和 Cipher.DECRYPT_MODE
/public 密钥应用 RSASSA-PKCS1-v1_5 作为填充。这在功能上与 signing/verifying 和 NonewithRSA
相同(除了 使用 NonewithRSA
验证另外检查数据与 相比是否相等使用 public 密钥解密).
NonewithRSA
表示数据未经过哈希处理且未添加摘要 ID。该算法实际上旨在对已经散列的数据进行签名(在摘要 ID 被添加到前面之后)。它绝不意味着签署未散列的数据,即 Java 代码滥用了该算法。
由于 RSA 限制了消息大小(密钥大小减去填充所需的 space),因此只能对适当的短消息进行签名。因此,大小超过允许大小的未散列数据会强制执行多个签名。这就是Java代码中多次解密的原因。
使用散列数据不仅出于实际原因(签署长消息)有用,而且出于安全原因也是必要的,s。例如here and here.
要在 Go 中实现 Java 代码的功能,与 Java 代码一样,Go 中的 signing/verifying 需要一个低级实现。
另一种可能性是实现解密 (m = c^e) 并自己删除填充,对于 RSASSA-PKCS1-v1_5 来说,它仅由一系列 0xFF 值组成,左边为 0x0001,右边为 0x00。
下面我考虑第二种方法。下面的 Go 代码执行以下操作:
- 导入 public 密钥
- Base64解码密文
- 将密文拆分为单独的签名块(这里 3 个块包含相同的数据:敏捷的棕色狐狸跳过懒狗)
- 解密每个签名块(m = c ^ e)
- 连接解密的签名块
package main
import (
"fmt"
"math/big"
"encoding/pem"
"crypto/x509"
"crypto/rsa"
"encoding/base64"
"bytes"
"io"
)
func main() {
pubKeyPem := `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZ67dtUTLxoXnNEzRBFB
mwukEJGC+y69cGgpNbtElQj3m4Aft/7cu9qYbTNguTSnCDt7uovZNb21u1vpZwKH
yVgFEGO4SA8RNnjhJt2D7z8RDMWX3saody7jo9TKlrPABLZGo2o8vadW8Dly/v+I
d0YDheCkVCoCEeUjQ8koXZhTwhYkGPu+vkdiqX5cUaiVTu1uzt591aO5Vw/hV4DI
hFKnOTnYXnpXiwRwtPyYoGTa64yWfi2t0bv99qz0BgDjQjD0civCe8LRXGGhyB1U
1aHjDDGEnulTYJyEqCzNGwBpzEHUjqIOXElFjt55AFGpCHAuyuoXoP3gQvoSj6RC
sQIDAQAB
-----END PUBLIC KEY-----`
// Import public key
pubKey := ImportSPKIPublicKeyPEM(pubKeyPem);
// Base64 decode ciphertext
ciphertextBytes, _ := base64.StdEncoding.DecodeString("ajQbkszbZ97YZaPSRBab9vj0DDLm9tTrQwSZ+ucPj+cYSmw06KLCtRH3SPn3b2DqSd1revLXqxMtSzFmjRvZ5F8y3nzdP8NJaRplOigbPFhKZTv7xBVK5ATEmLukgtI7f+d3KdmGUG+cyTkfxIrMBvB3BIS5oTiMNmC9pqLaWcDVF9qpuxnwEMQJbeO9nTklpdv+F8BrchHmeUkKRrMJBoPbbcfq9Hi4bHiFyxPWhwB66d/AryCKsFRhaX6hSkTL+0NvuhVhv98wdo3juv2Il50XKOCbfc8kUG628TcSK6n31piLF9cntSVTB/L/pVfcAxEwx4hcUhLuqmk6EZIJvGo0G5LM22fe2GWj0kQWm/b49Awy5vbU60MEmfrnD4/nGEpsNOiiwrUR90j5929g6knda3ry16sTLUsxZo0b2eRfMt583T/DSWkaZTooGzxYSmU7+8QVSuQExJi7pILSO3/ndynZhlBvnMk5H8SKzAbwdwSEuaE4jDZgvaai2lnA1RfaqbsZ8BDECW3jvZ05JaXb/hfAa3IR5nlJCkazCQaD223H6vR4uGx4hcsT1ocAeunfwK8girBUYWl+oUpEy/tDb7oVYb/fMHaN47r9iJedFyjgm33PJFButvE3Eiup99aYixfXJ7UlUwfy/6VX3AMRMMeIXFIS7qppOhGSCbxqNBuSzNtn3thlo9JEFpv2+PQMMub21OtDBJn65w+P5xhKbDToosK1EfdI+fdvYOpJ3Wt68terEy1LMWaNG9nkXzLefN0/w0lpGmU6KBs8WEplO/vEFUrkBMSYu6SC0jt/53cp2YZQb5zJOR/EiswG8HcEhLmhOIw2YL2motpZwNUX2qm7GfAQxAlt472dOSWl2/4XwGtyEeZ5SQpGswkGg9ttx+r0eLhseIXLE9aHAHrp38CvIIqwVGFpfqFKRMv7Q2+6FWG/3zB2jeO6/YiXnRco4Jt9zyRQbrbxNxIrqffWmIsX1ye1JVMH8v+lV9wDETDHiFxSEu6qaToRkgm8")
// Split ciphertext into signature chunks a 2048/8 bytes and decrypt each chunk
reader := bytes.NewReader(ciphertextBytes)
var writer bytes.Buffer
ciphertextBytesChunk := make([]byte, 2048/8)
for {
n, _ := io.ReadFull(reader, ciphertextBytesChunk)
if (n == 0) {
break
}
decryptChunk(ciphertextBytesChunk, &writer, pubKey)
}
// Concatenate decrypted signature chunks
decryptedData := writer.String()
fmt.Println(decryptedData)
}
func ImportSPKIPublicKeyPEM(spkiPEM string) (*rsa.PublicKey) {
body, _ := pem.Decode([]byte(spkiPEM ))
publicKey, _ := x509.ParsePKIXPublicKey(body.Bytes)
if publicKey, ok := publicKey.(*rsa.PublicKey); ok {
return publicKey
} else {
return nil
}
}
func decryptChunk(ciphertextBytesChunk []byte , writer *bytes.Buffer, pubKey *rsa.PublicKey ){
// Decrypt each signature chunk
ciphertextInt := new(big.Int)
ciphertextInt.SetBytes(ciphertextBytesChunk)
decryptedPaddedInt := decrypt(new(big.Int), pubKey, ciphertextInt)
// Remove padding
decryptedPaddedBytes := make([]byte, pubKey.Size())
decryptedPaddedInt.FillBytes(decryptedPaddedBytes)
start := bytes.Index(decryptedPaddedBytes[1:], []byte{0}) + 1 // // 0001FF...FF00<data>: Find index after 2nd 0x00
decryptedBytes := decryptedPaddedBytes[start:]
// Write decrypted signature chunk
writer.Write(decryptedBytes)
}
func decrypt(c *big.Int, pub *rsa.PublicKey, m *big.Int) *big.Int {
// Textbook RSA
e := big.NewInt(int64(pub.E))
c.Exp(m, e, pub.N)
return c
}
输出:
The quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dog
请注意,代码只是一个示例实现,特别是不包括异常处理。
测试:
下面的Java代码
String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZ67dtUTLxoXnNEzRBFBmwukEJGC+y69cGgpNbtElQj3m4Aft/7cu9qYbTNguTSnCDt7uovZNb21u1vpZwKHyVgFEGO4SA8RNnjhJt2D7z8RDMWX3saody7jo9TKlrPABLZGo2o8vadW8Dly/v+Id0YDheCkVCoCEeUjQ8koXZhTwhYkGPu+vkdiqX5cUaiVTu1uzt591aO5Vw/hV4DIhFKnOTnYXnpXiwRwtPyYoGTa64yWfi2t0bv99qz0BgDjQjD0civCe8LRXGGhyB1U1aHjDDGEnulTYJyEqCzNGwBpzEHUjqIOXElFjt55AFGpCHAuyuoXoP3gQvoSj6RCsQIDAQAB";
byte[] ciphertext = Base64.getDecoder().decode("ajQbkszbZ97YZaPSRBab9vj0DDLm9tTrQwSZ+ucPj+cYSmw06KLCtRH3SPn3b2DqSd1revLXqxMtSzFmjRvZ5F8y3nzdP8NJaRplOigbPFhKZTv7xBVK5ATEmLukgtI7f+d3KdmGUG+cyTkfxIrMBvB3BIS5oTiMNmC9pqLaWcDVF9qpuxnwEMQJbeO9nTklpdv+F8BrchHmeUkKRrMJBoPbbcfq9Hi4bHiFyxPWhwB66d/AryCKsFRhaX6hSkTL+0NvuhVhv98wdo3juv2Il50XKOCbfc8kUG628TcSK6n31piLF9cntSVTB/L/pVfcAxEwx4hcUhLuqmk6EZIJvGo0G5LM22fe2GWj0kQWm/b49Awy5vbU60MEmfrnD4/nGEpsNOiiwrUR90j5929g6knda3ry16sTLUsxZo0b2eRfMt583T/DSWkaZTooGzxYSmU7+8QVSuQExJi7pILSO3/ndynZhlBvnMk5H8SKzAbwdwSEuaE4jDZgvaai2lnA1RfaqbsZ8BDECW3jvZ05JaXb/hfAa3IR5nlJCkazCQaD223H6vR4uGx4hcsT1ocAeunfwK8girBUYWl+oUpEy/tDb7oVYb/fMHaN47r9iJedFyjgm33PJFButvE3Eiup99aYixfXJ7UlUwfy/6VX3AMRMMeIXFIS7qppOhGSCbxqNBuSzNtn3thlo9JEFpv2+PQMMub21OtDBJn65w+P5xhKbDToosK1EfdI+fdvYOpJ3Wt68terEy1LMWaNG9nkXzLefN0/w0lpGmU6KBs8WEplO/vEFUrkBMSYu6SC0jt/53cp2YZQb5zJOR/EiswG8HcEhLmhOIw2YL2motpZwNUX2qm7GfAQxAlt472dOSWl2/4XwGtyEeZ5SQpGswkGg9ttx+r0eLhseIXLE9aHAHrp38CvIIqwVGFpfqFKRMv7Q2+6FWG/3zB2jeO6/YiXnRco4Jt9zyRQbrbxNxIrqffWmIsX1ye1JVMH8v+lV9wDETDHiFxSEu6qaToRkgm8");
byte[] decrypted = decryptByPublicKey(ciphertext, publicKey);
System.out.println(new String(decrypted, StandardCharsets.UTF_8));
使用您发布的方法给出相同的结果。