【问题标题】:Creating RSA Public Key From String从字符串创建 RSA 公钥
【发布时间】:2016-01-30 06:09:42
【问题描述】:

我使用 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)。您的密钥采用 Java 不支持的更简单的 RSAPublicKey 格式。解决方案:以正确的 Java 支持格式对您的密钥进行编码。
  • 好吧,我不确定我是否可以通过这种方式在其他平台上生成密钥,但我当然很欣赏这些信息并会尝试一下。谢谢。

标签: java android cryptography rsa


【解决方案1】:

您拥有的密钥是 PKCS#1 格式,而不是 Java 接受的 SubjectPublicKeyInfo 结构。 PKCS#1 只是 RSA 参数的编码,缺少诸如算法标识符之类的东西。 SubjectPublicKeyInfo 在内部使用 PKCS#1 - 无论如何都用于 RSA 公钥。

由于 PKCS#1 公钥位于 SubjectPublicKeyInfo 结构的末尾,因此可以简单地为字节添加前缀,以便它们成为 RSA SubjectPublicKeyInfo。该解决方案更易于执行,无需额外的库,例如 Bouncy Castle。因此,如果您需要不使用外部库,那么您可以看看我的回答 here


或者,可以编写一个简单的 BER 解码器,将结构解码为两个 BigInteger 值。结构本身 is not that complicated 但 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 解码器更改为 the Android specific one (android.util.Base64)。此代码已在等效的 Java 运行时测试。

【讨论】:

  • 或者,如果 Bouncy Castle 不起作用,您可以使用 Spongy Castle,但在这种情况下可能不需要。
  • 我实际上花了一个小时让 Bouncy Castle 在 Eclipse 中工作,并且正在测试一些代码以尝试将 RSA 密钥转换为 SubjectPublicKeyInfo 格式。我尝试了这段代码,它确实创建了一个密钥。我不知道它是否可以与我的其他平台一起使用,但这是一个开始。谢谢。
  • 我只需要更改 Base 64 行即可使其正常工作。 byte[] decoded = Base64.decode(AppKeyPub,0);
  • 嗨,我复制了该代码并应用于我的公钥。使用带有公钥的 bouncycastle 时出现这样的错误:线程“main”中的异常 java.lang.IllegalArgumentException: Bad sequence size: 9. 你有什么想法要解决吗?
  • 不是来自那个描述。最好创建一个单独的问题,包括以 64 进制或十六进制表示的代码和测试密钥材料。可能是您的密钥编码错误,但这只是猜测。
【解决方案2】:

对于那些不想使用充气城堡的人

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,as I explained in my answer 不正确。
  • 不要提到@MaartenBodewes 所指的内容,但该代码确实对我有用,并且生成的公钥能够为我解码 JWT 令牌。
  • @vincentmathew 问题中的关键与此答案中的关键不同。如果它对您有用,您将拥有一个 PKCS#1 编码的公钥,而不是 SubjectPublicKeyInfo。两者都是 ASN.1 编码,因此它们可能表面上看起来彼此相似(例如以“MII”开头),但 SubjectPublicKeyInfo 结构会更大。当然,这个答案中的代码确实有效,只是不适用于这个问题。
  • 嗯,我的评论倒过来了,问题的关键是 PKCS#1,这适用于 SubjectPublicKeyInfo,它是 X.509 证书规范的一部分,因此是 X509EncodedKeySpec。跨度>
猜你喜欢
  • 2012-01-18
  • 1970-01-01
  • 2018-01-13
  • 2013-03-21
  • 2018-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-24
相关资源
最近更新 更多