无法在Java中解析RSA公钥。

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

Failed to parse RSA publicKey in Java

问题

以下是代码的翻译部分:

我正在使用以下代码块来解析任何RSA公钥进行加密

static byte[] encrypt(byte[] publicKey, byte[] inputData) throws Exception {
    PublicKey key = KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(publicKey));
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] encryptedBytes = cipher.doFinal(inputData);
    return encryptedBytes;
}

public static String getEncrypted(String data, String key) throws Exception {
    byte[] keyBytes = Base64.getDecoder().decode(key);
    return new String(Base64.getEncoder().encode(encrypt(keyBytes, data.getBytes())));
}

但对于以下RSA公钥:

MIIBCgKCAQEAs6YyGDXibkazM7QSeFBXjkAn5A8P87k+nuU6v5+zLJiD1KwkZ/SYnLwVSluOx19AzPHj07abDTJtthKtKpp2997UiV4CNUSzkZM1Eorf1+iLFhqeOiz9J5tYfFkKN5qPzwoPK4aFz35hQi7R1ORF9rFDPL+Ex79Tc+ABQF/CH5tn/NTXCNUYzLezg2Y1VOZGNhxd2LIv/29ZDxpJS8dD34H20HMMZCMGGolTXUIxVKI3cR0d1XzNCvAx3jcSkEUEPPH0lfusXqQOfCxJSIjorAzi5ucaWicvXYq6BNGulPqLoGBZnJ4HrFQF0oq1SU4i60VHqOgoiqMPQ+8cyjFBHQIDAQAB

在解析时我遇到了以下异常:

Caused by: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
	at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
	at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
	at sun.security.x509.X509Key.decode(X509Key.java:397)
	at sun.security.x509.X509Key.decode(X509Key.java:402)
	at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:86)
	at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:298)
	at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:201)

有任何想法是什么出了问题以及如何创建一个通用的RSA公钥解析代码吗?

英文:

I am using the following code block to parse any RSA public key to encrypt.

    static byte[] encrypt(byte[] publicKey, byte[] inputData) throws Exception {

    PublicKey key = KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(publicKey));

    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, key);

    byte[] encryptedBytes = cipher.doFinal(inputData);

    return encryptedBytes;
}

public static String getEncrypted(String data, String key) throws Exception {
    byte[] keyBytes = Base64.getDecoder().decode(key);
    return new String(Base64.getEncoder().encode(encrypt(keyBytes, data.getBytes())));
}

But for the following RSA public Key

MIIBCgKCAQEAs6YyGDXibkazM7QSeFBXjkAn5A8P87k+nuU6v5+zLJiD1KwkZ/SYnLwVSluOx19AzPHj07abDTJtthKtKpp2997UiV4CNUSzkZM1Eorf1+iLFhqeOiz9J5tYfFkKN5qPzwoPK4aFz35hQi7R1ORF9rFDPL+Ex79Tc+ABQF/CH5tn/NTXCNUYzLezg2Y1VOZGNhxd2LIv/29ZDxpJS8dD34H20HMMZCMGGolTXUIxVKI3cR0d1XzNCvAx3jcSkEUEPPH0lfusXqQOfCxJSIjorAzi5ucaWicvXYq6BNGulPqLoGBZnJ4HrFQF0oq1SU4i60VHqOgoiqMPQ+8cyjFBHQIDAQAB

while parsing I am getting the following exception

Caused by: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
	at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
	at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
	at sun.security.x509.X509Key.decode(X509Key.java:397)
	at sun.security.x509.X509Key.decode(X509Key.java:402)
	at sun.security.rsa.RSAPublicKeyImpl.&lt;init&gt;(RSAPublicKeyImpl.java:86)
	at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:298)
	at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:201)

Any idea what is failing and how to make a generic RSA public key parsing code.

答案1

得分: 2

以下是翻译好的内容:

import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.opensl.PEMParser;
import java.io.IOException;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

public class MainSO {
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        System.out.println("https://stackoverflow.com/questions/63454577/failed-to-parse-rsa-publickey-in-java");
        Security.addProvider(new BouncyCastleProvider());
        String rsaPublicKeyHeader = "-----BEGIN RSA PUBLIC KEY-----\n";
        String rsaPublicKeyFooter = "\n-----END RSA PUBLIC KEY-----";
        String rsaPublicKeyString = "MIIBCgKCAQEAs6YyGDXibkazM7QSeFBXjkAn5A8P87k+nuU6v5+zLJiD1KwkZ/SYnLwVSluOx19AzPHj07abDTJtthKtKpp2997UiV4CNUSzkZM1Eorf1+iLFhqeOiz9J5tYfFkKN5qPzwoPK4aFz35hQi7R1ORF9rFDPL+Ex79Tc+ABQF/CH5tn/NTXCNUYzLezg2Y1VOZGNhxd2LIv/29ZDxpJS8dD34H20HMMZCMGGolTXUIxVKI3cR0d1XzNCvAx3jcSkEUEPPH0lfusXqQOfCxJSIjorAzi5ucaWicvXYq6BNGulPqLoGBZnJ4HrFQF0oq1SU4i60VHqOgoiqMPQ+8cyjFBHQIDAQAB";
        PEMParser pemParser = new PEMParser(new StringReader(rsaPublicKeyHeader +
                rsaPublicKeyString + rsaPublicKeyFooter));
        SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) pemParser.readObject();
        byte[] publicKey = subjectPublicKeyInfo.getEncoded();
        // original code starts here
        PublicKey key = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKey));
        // check key
        System.out.println("key: " + key);
    }
}
英文:

Your public key (here just the Base64-encoded part) seems to be a 'RSA Public Key'
and not a 'Public Key'. The later is the format that Java is been able to work with.

To read the 'RSA Public Key' you need the Bouncy Castle library and additional 7 lines of code. As you have the key without the 'wrapping' header and footer I add the two
line manually.

Please keep in mind that the following code has no proper exception handling and is for educational purposes only.

result:

key: Sun RSA public key, 2048 bits
  params: null
  modulus: 22678610734153400983507431374302231631648011897672768754638644005690558018788055145838420912495825001883497816406549666369767766949853573723573636289962789479998547620264293389522975840594912755684410510779642046063268111520844008640320545114702934792800828432077361704284837605938354936920018742130341245366517474980128047515437565419306561322350155414838564407700303406271838590880852369997316303577351737008942081641382006211591786506015023574950120763293965668830106827392978781367691242570394862298000041087969687942746452359360224223895623579995775734139473237799095359767270215802792812274542667250920043135261
  public exponent: 65537

code:

import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import java.io.IOException;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

public class MainSO {
    public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        System.out.println(&quot;https://stackoverflow.com/questions/63454577/failed-to-parse-rsa-publickey-in-java&quot;);
        Security.addProvider(new BouncyCastleProvider());
        String rsaPublicKeyHeader = &quot;-----BEGIN RSA PUBLIC KEY-----\n&quot;;
        String rsaPublicKeyFooter = &quot;\n-----END RSA PUBLIC KEY-----&quot;;
        String rsaPublicKeyString = &quot;MIIBCgKCAQEAs6YyGDXibkazM7QSeFBXjkAn5A8P87k+nuU6v5+zLJiD1KwkZ/SYnLwVSluOx19AzPHj07abDTJtthKtKpp2997UiV4CNUSzkZM1Eorf1+iLFhqeOiz9J5tYfFkKN5qPzwoPK4aFz35hQi7R1ORF9rFDPL+Ex79Tc+ABQF/CH5tn/NTXCNUYzLezg2Y1VOZGNhxd2LIv/29ZDxpJS8dD34H20HMMZCMGGolTXUIxVKI3cR0d1XzNCvAx3jcSkEUEPPH0lfusXqQOfCxJSIjorAzi5ucaWicvXYq6BNGulPqLoGBZnJ4HrFQF0oq1SU4i60VHqOgoiqMPQ+8cyjFBHQIDAQAB&quot;;
        PEMParser pemParser = new PEMParser(new StringReader(rsaPublicKeyHeader +
                rsaPublicKeyString + rsaPublicKeyFooter));
        SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) pemParser.readObject();
        byte[] publicKey = subjectPublicKeyInfo.getEncoded();
        // original code starts here
        PublicKey key = KeyFactory.getInstance(&quot;RSA&quot;).generatePublic(new X509EncodedKeySpec(publicKey));
        // check key
        System.out.println(&quot;key: &quot; + key);
    }
}

答案2

得分: 2

你的问题与 https://stackoverflow.com/questions/31941413/invalidkeyspecexception-algid-parse-error-not-a-sequence 中的问题类似,尽管不完全相同。就像PKCS8是几乎任何算法中私钥的通用格式一样,通过包含标识算法的AlgorithmIdentifier和算法特定数据,Java使用了通用格式SubjectPublicKeyInfo(SPKI),由X.509/PKIX定义,用于公钥,Java称之为X509EncodedKeySpec,包含AlgId和算法特定数据。(有关详细信息,请参阅java.security.Key的javadoc;由于某种原因,我目前无法访问docs.oracle.com获取链接。)您只拥有由PKCS1 RSAPublicKey定义的算法特定数据。

您可以使用BouncyCastle手动解析和使用它(https://stackoverflow.com/questions/33425446/creating-rsa-public-key-from-string),或者将其转换为SPKI并使用(https://stackoverflow.com/questions/54238568/reading-a-pkcs1-or-spki-public-key-in-java-without-libraries)。或者(在使用BC或其他ASN.1库的情况下),您可以使用与#31941413中相同的方法,只是省略Integer版本(0),并将数据包装在DERBitString中,而不是DEROctetString中,或者使用我在https://stackoverflow.com/questions/53924326/rsa-should-i-use-x-509-or-pkcs-1 中展示的更简单、更直接的方法。

请注意,这不是“通用”的格式。您的格式与算法通用相反,这正是SPKI和PCKS8的目的。它也不是应用程序通用;除其他外,OpenSSH、PGP、Microsoft、PKCS11、JWK和XML都使用与此不同且不易与Java兼容的公钥格式。

英文:

Your problem is analagous to the problem in https://stackoverflow.com/questions/31941413/invalidkeyspecexception-algid-parse-error-not-a-sequence, though not the same. Just like PKCS8 is a generic format for private keys in (almost) any algorithm, by containing both an AlgorithmIdentifier that identifies the algorithm and algorithm-specific data, Java uses the generic format SubjectPublicKeyInfo (SPKI) defined by X.509/PKIX for public keys, which Java calls X509EncodedKeySpec, containing an AlgId plus algorithm-specific data. (See the javadoc for java.security.Key; for some reason I am currently unable to access docs.oracle.com to get a link.) You have only the algorithm-specific data defined by PKCS1 RSAPublicKey.

You can parse and use it 'manually' with BouncyCastle, or convert it to SPKI and use that. Or (given BC or another ASN.1 library) you could use the same approach as in #31941413 except omit the Integer version (0) and wrap the data in a DERBitString instead of a DEROctetString, or the simpler and more direct approach I show in https://stackoverflow.com/questions/53924326/rsa-should-i-use-x-509-or-pkcs-1 .

Note this is not 'generic'. Your format is the opposite of algorithm-generic, which as noted is the purpose of SPKI and PCKS8. It is also not applciation-generic; among other things OpenSSH, PGP, Microsoft, PKCS11, JWK, and XML all use publickey formats different from this and not easily compatible with Java.

huangapple
  • 本文由 发表于 2020年8月18日 00:05:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/63454577.html
匿名

发表评论

匿名网友

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

确定