如何使用Java通过SSH密钥对字符串进行签名与验签
解决ssh-keygen生成的RSA私钥签名请求体的问题
我之前也踩过一模一样的坑!用ssh-keygen -t rsa生成的私钥虽然是PEM格式,但它属于PKCS#1标准的RSA私钥,和很多示例里常见的PKCS#8格式略有区别,不过主流加密库都能直接处理,不用额外转换格式。下面给你几个常用语言的实现示例:
Python 实现(用cryptography库)
首先确保安装了cryptography:
pip install cryptography
然后用这段代码读取私钥并签名:
from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.backends import default_backend import base64 # 读取ssh-keygen生成的私钥文件 with open("id_rsa", "rb") as key_file: private_key = serialization.load_pem_private_key( key_file.read(), password=None, # 如果生成私钥时设置了密码,这里传入密码的bytes,比如b"your-password" backend=default_backend() ) # 待签名的请求体(注意要和API要求的编码一致,通常是UTF-8) request_body = b"your-request-body-content" # 执行签名,这里的哈希算法和填充方式要和第三方API要求完全匹配 signature = private_key.sign( request_body, padding.PKCS1v15(), hashes.SHA256() ) # 把签名转成Base64字符串(大多数API要求这种格式传递签名) signature_base64 = base64.b64encode(signature).decode("utf-8") print(f"生成的签名:{signature_base64}")
Java 实现(用BouncyCastle)
Java原生JDK默认只支持PKCS#8格式的私钥,所以我们需要借助BouncyCastle库来直接解析PKCS#1格式的私钥:
- 先在pom.xml(Maven)里添加依赖:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency>
- 然后编写签名代码:
import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.Signature; import java.security.spec.PKCS1EncodedKeySpec; import java.util.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class SshRsaSigner { public static void main(String[] args) throws Exception { // 注册BouncyCastle提供者 java.security.Security.addProvider(new BouncyCastleProvider()); // 读取私钥文件内容 String privateKeyStr = new String(Files.readAllBytes(Paths.get("id_rsa")), StandardCharsets.UTF_8); // 去除PEM格式的头部、尾部和空白字符 privateKeyStr = privateKeyStr.replace("-----BEGIN RSA PRIVATE KEY-----", "") .replace("-----END RSA PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr); // 解析PKCS#1格式的私钥 KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC"); PKCS1EncodedKeySpec keySpec = new PKCS1EncodedKeySpec(privateKeyBytes); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); // 初始化签名器,算法要和API要求一致 Signature signature = Signature.getInstance("SHA256withRSA", "BC"); signature.initSign(privateKey); // 传入请求体内容 String requestBody = "your-request-body-content"; signature.update(requestBody.getBytes(StandardCharsets.UTF_8)); // 生成签名 byte[] signatureBytes = signature.sign(); // 转成Base64字符串 String signatureBase64 = Base64.getEncoder().encodeToString(signatureBytes); System.out.println("生成的签名:" + signatureBase64); } }
关键注意事项
- 一定要和第三方API确认签名算法(比如是SHA256withRSA还是SHA1withRSA)、填充方式(通常是PKCS1v15),这些参数必须完全匹配,否则签名验证会失败。
- 如果生成私钥时设置了密码,读取私钥时必须传入对应的密码,否则会解析失败。
- 大多数API要求签名结果以Base64编码的字符串形式传递,所以最后一步的转换不能少。
内容的提问来源于stack exchange,提问作者Melad Basilius
相关产品推荐
相关产品推荐

