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

如何从Java的RSAPrivateKey生成SSH兼容私钥?已尝试代码未生效

解决Java生成SSH兼容RSA私钥的问题

我太懂你现在的困扰了——直接把RSAPrivateKey.getEncoded()的结果Base64编码后拼接前缀后缀,生成的私钥SSH根本不认对吧?这是因为Java默认生成的私钥是PKCS#8格式的DER编码,而SSH期望的是PKCS#1格式的PEM私钥(也就是OpenSSL传统的RSA私钥格式),两者的结构完全不同,直接拼接肯定不行。

下面给你两种可行的解决方案,一种用第三方库(简单省心),一种纯JDK实现(无依赖):

方案一:使用BouncyCastle库(推荐)

BouncyCastle已经封装好了格式转换的逻辑,不用自己折腾ASN.1结构解析,非常方便。

步骤1:引入依赖

如果用Maven,在pom.xml里添加:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

步骤2:生成SSH兼容私钥的代码

import org.bouncycastle.openssl.PEMWriter;
import java.io.FileWriter;
import java.io.Writer;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;

public class SshRsaKeyGenerator {
    public static void main(String[] args) throws Exception {
        // 注册BouncyCastle加密提供者
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        // 生成2048位RSA密钥对
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(2048);
        KeyPair kp = kpg.genKeyPair();

        // 用PEMWriter直接输出SSH兼容的私钥
        try (Writer out = new PEMWriter(new FileWriter("private.key"))) {
            // 传入RSAPrivateKey,PEMWriter会自动处理成PKCS#1格式的PEM
            out.writeObject(kp.getPrivate());
        }
    }
}

这段代码生成的私钥就是SSH直接能识别的格式——PEMWriter会自动加上-----BEGIN RSA PRIVATE KEY-----和-----END RSA PRIVATE KEY-----前缀后缀,还会自动把Base64编码分割成每行64字符,完全符合SSH的要求。

方案二:纯JDK实现(无第三方依赖)

如果不能引入外部库,我们可以手动解析PKCS#8的DER编码,提取出PKCS#1的内容,再转换成PEM格式。

核心逻辑

Java的RSAPrivateKey.getEncoded()返回的是PKCS#8的DER字节流,结构大致是:

PKCS#8 PrivateKeyInfo ::= SEQUENCE {
  version Version,
  privateKeyAlgorithm AlgorithmIdentifier,
  privateKey OCTET STRING -- 这里面就是PKCS#1格式的RSA私钥
}

我们需要解析这个SEQUENCE,取出privateKey字段里的内容,再对其进行Base64编码,然后加上SSH要求的前缀后缀,分割成每行64字符。

完整代码

import java.io.FileWriter;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.util.Base64;

public class PureJdkSshRsaGenerator {
    public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
        // 生成2048位RSA密钥对
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(2048);
        KeyPair kp = kpg.genKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();

        // 从PKCS#8格式中提取PKCS#1格式的私钥字节
        byte[] pkcs8Bytes = privateKey.getEncoded();
        byte[] pkcs1Bytes = extractPkcs1FromPkcs8(pkcs8Bytes);

        // 转换成Base64编码并分割成每行64字符
        String base64Encoded = Base64.getEncoder().encodeToString(pkcs1Bytes);
        StringBuilder pemBuilder = new StringBuilder();
        pemBuilder.append("-----BEGIN RSA PRIVATE KEY-----\n");
        for (int i = 0; i < base64Encoded.length(); i += 64) {
            int end = Math.min(i + 64, base64Encoded.length());
            pemBuilder.append(base64Encoded.substring(i, end)).append("\n");
        }
        pemBuilder.append("-----END RSA PRIVATE KEY-----");

        // 写入文件
        try (FileWriter out = new FileWriter("private.key")) {
            out.write(pemBuilder.toString());
        }
    }

    // 手动解析PKCS#8 DER格式,提取PKCS#1内容
    private static byte[] extractPkcs1FromPkcs8(byte[] pkcs8Bytes) {
        int index = 0;
        // 跳过SEQUENCE标签和长度
        if (pkcs8Bytes[index++] != 0x30) throw new IllegalArgumentException("不是合法的PKCS#8格式");
        int length = readDerLength(pkcs8Bytes, index);
        index += getLengthOfLength(length);

        // 跳过version字段
        if (pkcs8Bytes[index++] != 0x02) throw new IllegalArgumentException("解析version字段失败");
        index += 2; // 长度1,值为0

        // 跳过privateKeyAlgorithm字段
        if (pkcs8Bytes[index++] != 0x30) throw new IllegalArgumentException("解析算法字段失败");
        length = readDerLength(pkcs8Bytes, index);
        index += getLengthOfLength(length) + length;

        // 读取privateKey OCTET STRING内容
        if (pkcs8Bytes[index++] != 0x04) throw new IllegalArgumentException("解析私钥内容失败");
        length = readDerLength(pkcs8Bytes, index);
        index += getLengthOfLength(length);

        // 提取PKCS#1格式的私钥字节
        byte[] pkcs1Bytes = new byte[length];
        System.arraycopy(pkcs8Bytes, index, pkcs1Bytes, 0, length);
        return pkcs1Bytes;
    }

    // 读取DER编码的长度字段
    private static int readDerLength(byte[] bytes, int index) {
        int firstByte = bytes[index] & 0xFF;
        if ((firstByte & 0x80) == 0) {
            return firstByte;
        }
        int lengthBytes = firstByte & 0x7F;
        int length = 0;
        for (int i = 0; i < lengthBytes; i++) {
            length = (length << 8) | (bytes[index + 1 + i] & 0xFF);
        }
        return length;
    }

    // 获取长度字段本身的字节数
    private static int getLengthOfLength(int length) {
        if (length < 0x80) {
            return 1;
        } else if (length <= 0xFF) {
            return 2;
        } else if (length <= 0xFFFF) {
            return 3;
        } else if (length <= 0xFFFFFF) {
            return 4;
        } else {
            return 5;
        }
    }
}

验证方法

生成私钥后,可以用SSH工具验证有效性:

# 从私钥生成对应的公钥
ssh-keygen -y -f private.key > public.key

对比这个公钥和Java生成的kp.getPublic()转换后的OpenSSH格式公钥,应该完全一致。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:56:09