错误 ‘非法的Base64字符’,尝试将字符串转换为PublicKey类

huangapple go评论79阅读模式
英文:

Error 'Illegal base64 character' while trying convert String to PublicKey class

问题

在使用以下命令生成一对密钥之后:

ssh-keygen -b 2048 -t rsa

我尝试在一个Java/Servlet项目中使用公钥(文件:'key_rsa.pub')。在Servlet类中,我像这样读取文件:

@WebServlet(name = "Login", urlPatterns = "/login")
public class Login extends HttpServlet {
    ...
    InputStream publicKeyInputStream = getClass().getClassLoader().getResourceAsStream("key_rsa.pub");
    if (publicKeyInputStream == null) {
        out.println("fail - no public key");
        out.flush();
        return;
    }
    String publicKeyString = new String(publicKeyInputStream.readAllBytes(), StandardCharsets.UTF_8);
    try {
        PublicKey publicKey = util.RSAKeyGenerator.getPublicKey(publicKeyString);
    ...
    } catch (Exception e) {
    ...
    }
}

RSAKeyGenerator.getPublic 方法看起来像这样:

public class RSAKeyGenerator {
    public static final String RSA_ALGORITHM = "RSA";
    
    public static final int KEY_SIZE = 2048;
    
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    }
    
    public static PublicKey getPublicKey(String publicKeyString) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }
    
    public static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }
}

当我执行应用程序时,方法中的这一行:

byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);

出现错误'Illegal base64 character'。我在这里做错了什么?

key_rsa.pub 文件内容

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCko/IRJIBoIN6fe3k8mCzsereXnQlTSwHutyMqZh7/JqbWJA31036I0YZUuOJ0d4WAMDWktWrfn7dkAlt+dqQtAlyzPUrqOc/euQ0iZPYhrd8Gn9jn+8WAQyt9L3LemgQ1ZWnXgNUk5Z1Y1vcbp8GsRWWLrxR7uvtbonU7QDJv5vEFnleObPqKeqobbg1iUoaXYkw0IAqhCTNW3ZvfWQic4FeHeNNVtT8XUrGyNPZkSgQ8QHsvSvNiLJ2bROhBD4AUZKBN/XRvNs+OSJM7agGroznHGxpwj7jZ0V+MH1HvHt95+4SnFAQJVfjza3lUPoMIOphq24V6hLzCr17lPECj kleber@kleber-desktop

更新

我遵循了@WJS的建议,现在我的getPublicKey方法如下所示:

public static PublicKey getPublicKey(String publicKeyString) throws Exception {
    publicKeyString = publicKeyString.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","");
    byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
    return keyFactory.generatePublic(keySpec);
}

我认为这样可以删除字符串开头和结尾的额外字符,但现在我在return keyFactory.generatePublic(keySpec); 行中遇到错误 java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format

英文:

After generate a pair of keys with the command:

ssh-keygen -b 2048 -t rsa

I am trying use the public key (file: 'key_rsa.pub') in a java/servlet project. In the servlet class, I read the file like this:

@WebServlet(name = "Login", urlPatterns = "/login")
public class Login extends HttpServlet {
...
        InputStream publicKeyInputStream = getClass().getClassLoader().getResourceAsStream("key_rsa.pub");
        if (publicKeyInputStream == null) {
            out.println("fail - no public key");
            out.flush();
            return;
        }
        String publicKeyString = new String(publicKeyInputStream.readAllBytes(), StandardCharsets.UTF_8);
        try {
            PublicKey publicKey = util.RSAKeyGenerator.getPublicKey(publicKeyString);
...
        } catch (Exception e) {
...
        }
}

the method 'RSAKeyGenerator.getPublic' looks like that:

public class RSAKeyGenerator {
    public static final String RSA_ALGORITHM = "RSA";

    public static final int KEY_SIZE = 2048;

    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        return keyPairGenerator.generateKeyPair();
    }

    public static PublicKey getPublicKey(String publicKeyString) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }

    public static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }
}

wheh I execute the application, the line 'byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);' in the method is giving the error 'Illegal base64 character'. What I am done wrong here?

arquivo key_rsa.pub

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCko/IRJIBoIN6fe3k8mCzsereXnQlTSwHutyMqZh7/JqbWJA31036I0YZUuOJ0d4WAMDWktWrfn7dkAlt+dqQtAlyzPUrqOc/euQ0iZPYhrd8Gn9jn+8WAQyt9L3LemgQ1ZWnXgNUk5Z1Y1vcbp8GsRWWLrxR7uvtbonU7QDJv5vEFnleObPqKeqobbg1iUoaXYkw0IAqhCTNW3ZvfWQic4FeHeNNVtT8XUrGyNPZkSgQ8QHsvSvNiLJ2bROhBD4AUZKBN/XRvNs+OSJM7agGroznHGxpwj7jZ0V+MH1HvHt95+4SnFAQJVfjza3lUPoMIOphq24V6hLzCr17lPECj kleber@kleber-desktop

Update

I follow the sugestion from @WJS below, and now my getPublicKey method looks like that:

public static PublicKey getPublicKey(String publicKeyString) throws Exception {
    publicKeyString = publicKeyString.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","");
    byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
    return keyFactory.generatePublic(keySpec);
}

I think this works for remove the extra characters at the beginning and at the end of the string, but now I am getting the error java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format in the line return keyFactory.generatePublic(keySpec);

答案1

得分: 3

OpenSSH密钥格式(SSH2)使用不同的编码。

您必须将密钥转换为PEM或DER,或更改您的命令为:

ssh-keygen -b 2048 -t rsa -m PEM

英文:

The OpenSSH key format (SSH2) uses a different encoding.

You must convert the key to PEM or DER or change your command to:

ssh-keygen -b 2048 -t rsa -m PEM.

答案2

得分: 2

X509EncodedKeySpec() 需要一个以 Base64 编码的 ASN.1/DER 编码的 X.509/SPKI 格式的公钥,而 PKCS8EncodedKeySpec() 需要一个以 Base64 编码的 ASN.1/DER 编码的 PKCS#8 格式的私钥。

您的 ssh-keygen 语句生成的密钥是以 OPENSSH 格式的(如此答案中已经找到的),不能直接被所发布的 Java 代码处理。

您可以将私有的 OpenSSH 密钥转换为私有的 PKCS#8 密钥,如下所示(请注意,该文件将被覆盖)。转换后的密钥是不加密的(-N ""),正如所发布的 Java 代码所需:

ssh-keygen -p -N "" -m pkcs8 -f "<path to existing private OpenSSH key>" // 注意:原文件将被覆盖

这里重要的是 -m pkcs8 规定了 PKCS#8 作为目标格式。在另一个答案中使用的 -m pem 规定了 PKCS#1,这也不能被(直接)所发布的 Java 代码处理。

X.509/SPKI 格式的公钥可以从公共的 OpenSSH 密钥中确定,如下所示:

ssh-keygen -f "<path to existing public OpenSSH key>" -e -m pkcs8 > "<path to X.509 key to be generated>"

或者,也可以从生成的私有 PKCS#8 密钥中获得:

ssh-keygen -f "<path to existing private PKCS#8 key>" -i -m PKCS8 -e > "<path to X.509 key to be generated>"

作为转换的替代方案,私钥也可以直接以 PKCS#8 格式导出:

ssh-keygen -b 2048 -t rsa -N "" -m pkcs8 -f "<path to private PKCS#8 key to be generated>"

正如在其他答案中已经发布的,但具有所需的目标格式。

请注意,以这种方式生成的公钥仍然具有 OpenSSH 格式,必须根据上述描述显式转换。

生成的 PKCS#8 和 X.509/SPKI 密钥都以 PEM 编码,即它们由标头、页脚和Base64编码的主体组成,每64个字符后有换行符。如果删除标头、页脚和换行符,就会得到Base64编码的 ASN.1/DER 编码的密钥,可以被所发布的 Java 代码导入。

英文:

X509EncodedKeySpec() requires a Base64 encoded ASN.1/DER encoded public key in X.509/SPKI format, while PKCS8EncodedKeySpec() requires a Base64 encoded ASN.1/DER encoded private key in PKCS#8 format.

Your ssh-keygen statement generated the keys in OPENSSH format (as already found in this answer), which cannot be directly processed by the posted Java code.


You can convert the private OpenSSH key into a private PKCS#8 key as follows (note that the file will be overwritten). The converted key is unencrypted (-N "") as required by the posted Java code:

ssh-keygen -p -N "" -m pkcs8 -f "<path to existing private OpenSSH key>" // attention: original file is overwritten

Important here is the specifier -m pkcs8, which defines PKCS#8 as the target format. The specifier -m pem used in the other answer defines PKCS#1, which also cannot be (directly) processed by the posted Java code.


The public key in X.509/SPKI format can be determined from the public OpenSSH key as follows:

ssh-keygen -f "<path to existing public OpenSSH key>" -e -m pkcs8 > "<path to X.509 key to be generated>"

or alternatively from the generated private PKCS#8 key:

ssh-keygen -f "<path to existing private PKCS#8 key>" -i -m PKCS8 -e > "<path to X.509 key to be generated>"

As an alternative to a conversion, the private key can be exported directly in PKCS#8 format:

ssh-keygen -b 2048 -t rsa -N "" -m pkcs8 -f "<path to private PKCS#8 key to be generated>"

as already posted in the other answer but with the required target format.

Note that the public key generated this way still has the OpenSSH format and must be explicitly converted as described above.


The generated PKCS#8 and X.509/SPKI keys are PEM encoded, i.e. they consist of header, footer and Base64 encoded body with line breaks after 64 characters. If the header, footer and line breaks are removed, the Base64 encoded ASN.1/DER encoded keys are obtained, which can be imported by the posted Java code.

答案3

得分: 1

我相信你的字符串开头或结尾可能包含多余的字符。这对我有用。这确保只使用空格之间的字符串。

key = key.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","");
byte[] bytes = Base64.getDecoder().decode(s);
英文:

I believe you may have extraneous characters at the beginning or end of the String. This worked for me. This ensures that only the string between the blanks is used.

key = key.replaceAll("ssh-rsa\\s+", "").replaceAll("\\s.*","");
byte[]bytes = Base64.getDecoder().decode(s);

</details>



huangapple
  • 本文由 发表于 2023年7月14日 07:37:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76683871.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定