从字符串创建RSA公钥

19

我使用1024 RSA生成了此测试公钥,然后在另一个编码平台上对其进行了DER和Base64编码。我将密钥复制到Android/Eclipse中的字符串中,并尝试使用KeyFactory将其转换为公钥。无论我尝试什么,它都会给我一个InvalidKeySpecException异常。非常感谢任何建议。

     private void prepKeys() {
         String AppKeyPub = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5" +
"5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ" +
"OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";

        // create the key factory          
            try {
                KeyFactory kFactory = KeyFactory.getInstance("RSA");  
                // decode base64 of your key
                byte yourKey[] =  Base64.decode(AppKeyPub,0);
                // generate the public key
                X509EncodedKeySpec spec =  new X509EncodedKeySpec(yourKey);
                PublicKey publicKey = (PublicKey) kFactory.generatePublic(spec);

            System.out.println("Public Key: " + publicKey);  

            } catch (Exception e) {
                // TODO Auto-generated catch block  
                e.printStackTrace(); 
            }

         }

X509EncodedKeySpec期望以SubjectPublicKeyInfo格式(请参见RFC 5280)表示的密钥。您的密钥采用更简单的RSAPublicKey格式,Java不支持该格式。解决方案:将您的密钥编码为正确的Java支持格式。 - President James K. Polk
好的,我不确定我能否以那种方式在另一个平台上生成密钥,但我非常感谢这些信息,我会尝试一下。谢谢。 - raximus
2个回答

24
您拥有的密钥是以PKCS#1格式而不是Java接受的SubjectPublicKeyInfo结构。 PKCS#1仅对RSA参数进行编码,缺少算法标识符等内容。 SubjectPublicKeyInfo在内部使用PKCS#1-至少对于RSA公钥如此。
由于PKCS#1公钥位于SubjectPublicKeyInfo结构的末尾,因此可以简单地在字节前面添加前缀,使其成为RSA SubjectPublicKeyInfo。这种解决方案更容易执行,无需额外的库(如Bouncy Castle)。因此,如果您需要不使用外部库,则可以查看我的答案here
或者,可以编写一个简单的BER解码器来将该结构解码为两个BigInteger值。结构本身并不是很复杂,但是BER / DER长度编码需要一些习惯。
但是,您也可以使用Bouncy Castle(轻量级API)来解决您的问题:
String publicKeyB64 = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5"
        + "5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ"
        + "OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// ok, you may need to use the Base64 decoder of bouncy or Android instead
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey generatedPublic = kf.generatePublic(keySpec);
System.out.printf("Modulus: %X%n", modulus);
System.out.printf("Public exponent: %d ... 17? Why?%n", publicExponent); // 17? OK.
System.out.printf("See, Java class result: %s, is RSAPublicKey: %b%n", generatedPublic.getClass().getName(), generatedPublic instanceof RSAPublicKey);

正如您所看到的,它实际上只需要一个类作为接口,尽管当然还支持整个Bouncy Castle中ASN.1 / BER解码器的功能。


请注意,可能需要将Base 64解码器更改为 Android特定的解码器(android.util.Base64。此代码已在等效Java运行时上进行了测试。


如果Bouncy Castle不起作用,你可以使用Spongy Castle,但在这种情况下可能没有必要。 - Maarten Bodewes
我刚刚花了一个小时在Eclipse中使Bouncy Castle工作,并测试了一些代码,试图将RSA密钥转换为SubjectPublicKeyInfo格式。我尝试了这段代码,它确实创建了一个密钥。我不知道它是否能够与我的其他平台配合使用,但这是一个开始。谢谢。 - raximus
1
我只需要更改Base64代码行就可以使它工作。byte[] decoded = Base64.decode(AppKeyPub,0); - raximus
嗨,我复制了那段代码并应用到我的公钥上。当我使用bouncycastle和公钥时,出现了以下错误:Exception in thread "main" java.lang.IllegalArgumentException: Bad sequence size: 9。你有什么解决方法吗? - Lee Dat
不要从那个描述中推断。最好创建一个单独的问题,包括代码和以 base64 或十六进制表示的测试密钥材料。也许你的密钥编码有问题,但这只是猜测。 - Maarten Bodewes

-2

对于那些不想使用Bouncy Castle的人

public class RSAKeySeperation {


public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException {
    String publicKeyB64 = "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBV8xakN/wOsB6qHpyMigk/5PrSxxd6tKTJsyMIq5f9npzZue0mI4H2o8toYImtRk6VHhcldo0t7UwsQXmFMk7D"
            + "i3C53Xwfk7yEFSkXGpdtp/7fbqNnjVoJl/EPcgoDsTPrHYF/HgtmbhzuYvYeY1zpV0d2uYpFxAuqkE9FreuuH0iI8xODFe5NzRevXH116elwdCGINeAecHKgiWe"
            + "bGpRPml0lagrfi0qoQvNScmi/WIN2nFcI3sQFCq3HNYDBKDhO0AEKPB2FjvoEheJJwTs5URCYsJglYyxEUon3w6KuhVa+hzYJUAgNTCsrAhQCUlX4+5LOGlwI5gonm1DYvJJZAgMBAAEB";
    byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
    X509EncodedKeySpec spec =
            new X509EncodedKeySpec(decoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    RSAPublicKey generatePublic = (RSAPublicKey) kf.generatePublic(spec);
    BigInteger modulus = generatePublic.getModulus();
    System.out.println(modulus);
    BigInteger exponent = generatePublic.getPublicExponent();
    System.out.println(exponent);
}

}

这需要SubjectPublicKeyInfo,而正如我在我的回答中解释的那样,这是不正确的。 - Maarten Bodewes
不知道@MaartenBodewes在指什么,但这段代码对我来说是有效的,生成的公钥能够解码JWT令牌。 - vincent mathew
2
@vincentmathew 这个问题中的密钥与这个答案中的密钥不同。如果它对您有用,您将拥有一个PKCS#1编码的公钥,而不是SubjectPublicKeyInfo。两者都是ASN.1编码的,因此它们可能在表面上看起来相似(例如以“MII”开头),但SubjectPublicKeyInfo结构会更大。当然,这个答案中的代码确实有效,只是不适用于这个问题。 - Maarten Bodewes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接