java.security.spec.InvalidKeySpecException: java.io.IOException: 意外的内容结束标记

java.security.spec.InvalidKeySpecException: java.io.IOException: unexpected end-of-contents marker

我正在尝试将 .pub 文件的内容转换为 PublicKey,然后将 PublicKey 转换回 String 以确定是否转换正在工作并且不会更改过程中的密钥。

id_rsa.pub:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0zszKhcZTC8xJidUszmRn4Tr/FxPs04wpCzEstebfTW7Bvqgtt+OdvxoNyYM0LAEnxEF4XhAWcsX7VJJqstZLpDqlKDXFr2d0aVIjksCpZt+ftVRwYHRoERhEOP/UmPFb5rKIkhQbED2kTWg11mW9soc6BhwB3THn/Cyo3t1u2vWjEySgPhKeA3Xzh+5eqV7CUD8V6S7OAT7T9ijf7sRV0R8rwHgTLWJ8+dETnY3L3N0fEaNuaayeNblHqrL53/1+tsBBUF3bAS+1GE6oniSeM/yhtfzf2x+O5MDlVVMbOCC/v+FnfIIEKLA+v1xDSAha7C5cHh82TxToWXsbjqGD me@mail

Converter.java

public static final synchronized PublicKey base64ToPublicKey(final String algorithm, final String base64) throws GeneralSecurityException, IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] sigBytes2 = decoder.decodeBuffer(base64);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(sigBytes2);
        KeyFactory keyFact = KeyFactory.getInstance(algorithm, "BC");
        return keyFact.generatePublic(x509KeySpec);
    }

    public static final synchronized String publicKeyToBase64(final PublicKey publicKey) throws GeneralSecurityException, IOException {
        byte[] publicKeyBytes = publicKey.getEncoded();
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(publicKeyBytes);
    }

当我运行:

PublicKey test1 = base64ToPublicKey("RSA", "AAAAB3NzaC1yc2EAAAADAQABAAABAQC0zszKhcZTC8xJidUszmRn4Tr/FxPs04wpCzEstebfTW7Bvqgtt+OdvxoNyYM0LAEnxEF4XhAWcsX7VJJqstZLpDqlKDXFr2d0aVIjksCpZt+ftVRwYHRoERhEOP/UmPFb5rKIkhQbED2kTWg11mW9soc6BhwB3THn/Cyo3t1u2vWjEySgPhKeA3Xzh+5eqV7CUD8V6S7OAT7T9ijf7sRV0R8rwHgTLWJ8+dETnY3L3N0fEaNuaayeNblHqrL53/1+tsBBUF3bAS+1GE6oniSeM/yhtfzf2x+O5MDlVVMbOCC/v+FnfIIEKLA+v1xDSAha7C5cHh82TxToWXsbjqGD");

我回来了:

java.security.spec.InvalidKeySpecException: java.io.IOException: unexpected end-of-contents marker
    at org.bouncycastle.jce.provider.JDKKeyFactory.engineGeneratePublic(Unknown Source)
    at org.bouncycastle.jce.provider.JDKKeyFactory$RSA.engineGeneratePublic(Unknown Source)
    at java.security.KeyFactory.generatePublic(KeyFactory.java:328)
    at base64ToPublicKey(Converter.java:216)
    at main(Converter.java:283)

OpenSSH public 密钥文件id_*.pub 也是 known_hostsauthorized_keys 中的条目)SSH2 使用 OpenSSH -SSH 特定格式的特定变体,请参阅 rfc4716 which is in turn based on the SSH2 wire format (as linked) rfc4253 6.6,它 不是 'X.509' 格式 Java 加密使用 。 (SSH1 的 OpenSSH 文件格式不同,但 SSH1 早已损坏,不应使用。)

要在 Java 中进行转换,请参阅 convert openSSH rsa key to javax.crypto.Cipher compatible format

更容易避免问题。

绕过 1: 如果你有相当新的 OpenSSH(6.0 没问题,对于更早的版本不确定),使用

ssh-keygen -e -m PKCS8 -f id_rsa.pub >pub.pem # change filename as needed

转换为 PEM 形式的 'X.509'(实际上是 SubjectPublicKeyInfo aka SPKI)。 (是的,他们确实使用名称 PKCS8 来表示 SPKI;it's crazy。)然后通过丢弃 BEGIN 和 END 行在 Java 中阅读此内容,将其间的所有内容(减去换行符)从 base64 解码为 byte[],然后像现在一样将其放入 X509EncodedKeySpec。或者,如果你有 OpenSSL,你可以转换为 DER 形式

openssl rsa -pubin -in pub.pem -out pub.der -outform der # any version
openssl pkey -pubin -in pub.pem -out pub.der -outform der # 1.0.0 up

然后将 DER 文件原封不动地读入 X509EncodedKeySpec.

绕过 2: 如果您有 私钥,并且它不是 OpenSSH 的 'new' 格式(自 6.5 起可选) (编辑)和 default 从 7.8 开始),并且您有 OpenSSL,使用 [=22] 之一获取 SPKI(Java-友好)格式的 public 密钥=]

openssl rsa -in id_rsa -pubout -out pub.pem # default PEM
openssl rsa -in id_rsa -pubout -out pub.der -outform der # DER
openssl pkey -in id_rsa -pubout -out pub.pem # default PEM, 1.0.0 up
openssl pkey -in id_rsa -pubout -out pub.der -outform der # DER, 1.0.0