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

如何将Java生成的ECDSA公钥转换为SSH2公钥?

实现Java ECPublicKey到SSH2格式公钥的转换

已生成ECDSA密钥对,现需将其中的ECPublicKey转换为SSH2格式公钥。生成密钥对的代码如下:

final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA");
ECGenParameterSpec params = new ECGenParameterSpec("secp384r1");
keyGen.initialize(params);
final KeyPair keyPair = keyGen.generateKeyPair();

// 需将公钥格式化为SSH2格式
final ECPublicKey ecPublicKey = (ECPublicKey) keyPair.getPublic();

writeJavaECPublicKeyToSSH2(ecPublicKey); // 如何实现这个方法?

实现思路

SSH2格式的ECDSA公钥遵循RFC 5656规范,核心结构分为三部分:

  • 密钥算法标识(如ecdsa-sha2-nistp384)
  • 椭圆曲线参数标识(如nistp384)
  • 非压缩格式的公钥点DER编码数据

完整实现代码

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.util.Base64;

public void writeJavaECPublicKeyToSSH2(ECPublicKey ecPublicKey) throws IOException {
    // 1. 映射Java曲线名称到SSH标准名称
    ECParameterSpec params = ecPublicKey.getParams();
    String curveName = getSshCurveName(params.getName());
    String algorithm = "ecdsa-sha2-" + curveName;

    // 2. 序列化非压缩格式的公钥点:0x04 + X坐标字节 + Y坐标字节
    ECPoint ecPoint = ecPublicKey.getW();
    BigInteger x = ecPoint.getAffineX();
    BigInteger y = ecPoint.getAffineY();
    
    int keySizeBytes = params.getCurve().getField().getFieldSize() / 8;
    byte[] xBytes = padToFixedLength(x.toByteArray(), keySizeBytes);
    byte[] yBytes = padToFixedLength(y.toByteArray(), keySizeBytes);
    
    ByteArrayOutputStream pointOut = new ByteArrayOutputStream();
    pointOut.write(0x04); // 非压缩格式标记
    pointOut.write(xBytes);
    pointOut.write(yBytes);
    byte[] publicKeyPoint = pointOut.toByteArray();

    // 3. 按SSH2格式组装数据:每个字段先写4字节大端长度,再写字节内容
    ByteArrayOutputStream sshOutput = new ByteArrayOutputStream();
    writeString(sshOutput, algorithm);
    writeString(sshOutput, curveName);
    writeString(sshOutput, publicKeyPoint);

    // 4. 生成最终SSH2公钥字符串
    String base64Encoded = Base64.getEncoder().encodeToString(sshOutput.toByteArray());
    String ssh2PublicKey = algorithm + " " + base64Encoded;
    
    // 示例:打印公钥,可替换为写入文件等操作
    System.out.println(ssh2PublicKey);
}

// 补全BigInteger字节数组到固定长度(补前导0)
private byte[] padToFixedLength(byte[] input, int length) {
    byte[] result = new byte[length];
    int start = length - input.length;
    if (start < 0) {
        throw new IllegalArgumentException("输入字节数组长度超过目标长度");
    }
    System.arraycopy(input, 0, result, start, input.length);
    return result;
}

// 写入SSH格式字符串:先写4字节大端长度,再写字节内容
private void writeString(ByteArrayOutputStream out, String value) throws IOException {
    byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
    writeInt(out, bytes.length);
    out.write(bytes);
}

// 写入4字节大端整数
private void writeInt(ByteArrayOutputStream out, int value) throws IOException {
    out.write((value >> 24) & 0xFF);
    out.write((value >> 16) & 0xFF);
    out.write((value >> 8) & 0xFF);
    out.write(value & 0xFF);
}

// 映射Java曲线名称到SSH标准名称
private String getSshCurveName(String javaCurveName) {
    return switch (javaCurveName) {
        case "secp256r1" -> "nistp256";
        case "secp384r1" -> "nistp384";
        case "secp521r1" -> "nistp521";
        default -> throw new IllegalArgumentException("不支持的椭圆曲线: " + javaCurveName);
    };
}

关键说明

  • 曲线名称映射:Java中使用的secp384r1对应SSH标准的nistp384,必须转换才能被SSH客户端识别。
  • 公钥点格式:SSH强制要求使用非压缩格式的公钥点,必须以0x04开头,X/Y坐标需补前导0保证固定长度。
  • SSH2格式组装:所有字段都遵循「4字节大端长度+内容」的结构,最后整体Base64编码并拼接算法前缀,得到标准SSH2公钥字符串。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 18:25:30