Perl与Scala跨语言RSA公私钥签名验证不兼容问题咨询
RSA跨语言签名验证失败问题解决方案
核心问题原因
- 密钥格式不兼容:Linux下
ssh-keygen默认生成的私钥为OpenSSH格式、公钥为SSH专有公钥格式,而Scala依赖的Java Security API默认仅支持PKCS8格式私钥、X.509/SPKI格式公钥,直接读取原生ssh生成的密钥文件会解析失败或内容不匹配。 - 签名流程不一致:两侧的哈希算法、填充模式、payload字节编码、换行符处理逻辑如果有差异,也会导致签名校验不通过。
- 代码逻辑错误:你提供的Scala代码中,校验逻辑存在低级错误,
verify方法需要传入解码后的签名字节数组,而非原始payload字节数组。
具体解决步骤
1. 统一密钥格式
通过OpenSSH/OpenSSL命令将原生生成的密钥转换为两侧都支持的通用格式:
- 私钥转换(OpenSSH格式转PKCS8 DER格式):
openssl pkcs8 -topk8 -inform PEM -outform DER -in ~/.ssh/id_rsa -out id_rsa_pkcs8.der -nocrypt
- 公钥转换(SSH格式转X.509 SPKI DER格式):
ssh-keygen -f ~/.ssh/id_rsa.pub -e -m pkcs8 > id_rsa_pub_spki.der
转换后的der格式密钥可直接被Scala的X509EncodedKeySpec和PKCS8EncodedKeySpec读取解析。
2. 统一签名校验流程
- 算法对齐:两侧统一使用
SHA256withRSA算法,填充模式默认用PKCS#1 v1.5,不要私自修改参数。 - payload处理对齐:读取原始payload时不要用
getLines.mkString拼接(会丢失换行符),直接读取全量字节;转字节编码时明确指定UTF-8,避免系统默认编码差异:
// 正确的payload读取方式 val payloadBytes = Files.readAllBytes(Paths.get("file"))
- Base64编码对齐:两侧都使用标准无换行的Base64编码规则,Perl侧编码时不要做行截断。
3. 修正Scala校验逻辑
你的代码中校验部分写错了,正确的校验逻辑示例:
import java.nio.file.Files import java.nio.file.Paths import java.security.KeyFactory import java.security.Signature import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec import java.util.Base64 // 1. 读取转换后的公钥 val pubKeyBytes = Files.readAllBytes(Paths.get("id_rsa_pub_spki.der")) val pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pubKeyBytes)) // 2. 读取原始payload val payloadBytes = Files.readAllBytes(Paths.get("file")) // 3. 读取并解码Base64签名 val signatureBase64 = Files.readString(Paths.get("file.hash")).trim val signatureBytes = Base64.getDecoder.decode(signatureBase64) // 4. 执行校验 val sign = Signature.getInstance("SHA256withRSA") sign.initVerify(pubKey) sign.update(payloadBytes) val verifyResult = sign.verify(signatureBytes) if (verifyResult) println("签名验证通过") else println("签名验证失败")
内容的提问来源于stack exchange,提问作者user3450546
相关产品推荐
相关产品推荐

