公钥/私钥加密 Android-PHP

4
我正在尝试在 Android 端加密数据,并在 PHP 端解密数据。我在 PHP 中使用 phpseclib 生成公钥/私钥。
在生成密钥之后,我得到了PHP端的公钥:
-----BEGIN RSA PUBLIC KEY-----".
            "MIGJAoGBAKks62Itns2uU/dVZJ4kCkMinHgyeh/rdMD53a4Zu2a76OIJvdSZ8q4c".
            "YTWvPj0giefVtMc7tV4c6AAw04jyIfmCTvcQUlHI+sspHxXDlQTagNoxCuA29b5L".
            "9MKO6Ok0LwF9rGgTywC1heNEulZz9ISn9FQDazJT+Bd9cnNOrJRdAgMBAAE=".
            "-----END RSA PUBLIC KEY-----

然后我将其编码为base64,得到了这个base64编码的密钥
LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tTUlHSkFvR0JBS2tzNjJJdG5zMnVVL2RWWko0a0NrTWluSGd5ZWgvcmRNRDUzYTRadTJhNzZPSUp2ZFNaOHE0Y1lUV3ZQajBnaWVmVnRNYzd0VjRjNkFBdzA0anlJZm1DVHZjUVVsSEkrc3NwSHhYRGxRVGFnTm94Q3VBMjliNUw5TUtPNk9rMEx3RjlyR2dUeXdDMWhlTkV1bFp6OUlTbjlGUURhekpUK0JkOWNuTk9ySlJkQWdNQkFBRT0tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0t

我将它复制到 Android 端用于加密数据,但是出现了“InvalidKeySpecException”错误。
Android 端代码:
public static byte[] encrypt(String text) {

        byte[] encodedPublicKey= Base64.decode("LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tTUlHSkFvR0JBS2tzNjJJdG5zMnVVL2RWWko0a0NrTWluSGd5ZWgvcmRNRDUzYTRadTJhNzZPSUp2ZFNaOHE0Y1lUV3ZQajBnaWVmVnRNYzd0VjRjNkFBdzA0anlJZm1DVHZjUVVsSEkrc3NwSHhYRGxRVGFnTm94Q3VBMjliNUw5TUtPNk9rMEx3RjlyR2dUeXdDMWhlTkV1bFp6OUlTbjlGUURhekpUK0JkOWNuTk9ySlJkQWdNQkFBRT0tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0t", Base64.DEFAULT);



   PublicKey publicKey=null;
    KeyFactory keyFactory = null;
    try {
        keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
        publicKey = keyFactory.generatePublic(publicKeySpec);

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }


    byte[] cipherText = null;
    try {
        // get an RSA cipher object and print the provider
        final Cipher cipher = Cipher.getInstance("RSA");
        // encrypt the plain text using the public key
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        cipherText = cipher.doFinal(text.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cipherText;
}

2
根据文档,X509EncodedKeySpec期望一个ASN.1格式的密钥,而你正在传递一个PEM格式的密钥。 - adelphus
1
嘿,双重base64编码。这真的没有意义。你需要使用base64将二进制转换为文本;如果输入已经是文本,那么base64几乎没有用处,你同意吗?去掉头和尾看看是否有效。 - Maarten Bodewes
1
@MaartenBodewes:这不会起作用,因为格式是更简单的PKCS#1公钥,只有模数和指数。Java期望更复杂的SubjectPublicKeyInfo兽,也称为Java中的“X509EncodedKey”。 - President James K. Polk
1
可能你最好的选择是以 XML 签名格式从 PHPSeclib 中导出:$rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_XML);。然后提取模数和指数,对它们进行 Base64 解码并将它们放入一个 RSAPublicKeySpec 实例中。 - President James K. Polk
@JamesKPolk 我已经执行了以下代码:$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_XML); $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_XML); extract($rsa->createKey());,并获得了以下密钥: <RSAKeyValue> <Modulus>uRe18YPb3x8Moqe6E9M2f4GTCQNm5i7lm5MrvmmiW0hjC+SG0vxXOcWpDegwQIlfWfN3SUP‌​JUxn4YeaAqX4Z2Xx2N9elgsPUM6EYYMl+dforjsBpk0uc1EvncYahpg0si0I7MnXqLD6SmVM6OX2M8XvT‌​kKbYu2GvZ6zlPrMe3B8=</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue> 请问如何处理并在Android中使用它?能否提供相关代码? - Khaled Awad
@JamesKPolk 使用了您的评论查找了正确的类来回答我的问题,谢谢,减少了调试。这是解析ASN.1 / DER后的密钥 - Maarten Bodewes
1个回答

2
以下代码解析PEM格式,将其输入BouncyCastle的PKCS#1 RSAPublicKey类解析器中,然后使用模数和指数构建JCE RSAPublicKey。其余部分为错误处理、密钥本身和一个主要方法来展示其工作。

对于Android,则可能需要使用Spongy Castle。

package nl.owlstead.stackoverflow;

import java.io.IOException;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

public final class RSAPublicKeyFromOpenSSL_PKCS1_PEM {
    private static final String PEM = "-----BEGIN RSA PUBLIC KEY-----\r\n"
            + "MIGJAoGBAKks62Itns2uU/dVZJ4kCkMinHgyeh/rdMD53a4Zu2a76OIJvdSZ8q4c\r\n"
            + "YTWvPj0giefVtMc7tV4c6AAw04jyIfmCTvcQUlHI+sspHxXDlQTagNoxCuA29b5L\r\n"
            + "9MKO6Ok0LwF9rGgTywC1heNEulZz9ISn9FQDazJT+Bd9cnNOrJRdAgMBAAE=\r\n"
            + "-----END RSA PUBLIC KEY-----\r\n";

    public static RSAPublicKey parsePEM(final String pem)
            throws IllegalArgumentException {

        // --- read PEM object
        final PemObject readPemObject;
        try (final PemReader reader = new PemReader(new StringReader(PEM))) {
            readPemObject = reader.readPemObject();
        } catch (final IOException e) {
            throw new IllegalArgumentException("Not a PEM object", e);
        }
        if (!readPemObject.getType().equalsIgnoreCase("RSA PUBLIC KEY")) {
            throw new IllegalArgumentException("Not a public key");
        }
        final byte[] pemContent = readPemObject.getContent();

        // --- create Bouncy Castle PKCS#1 public key
        final org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey;
        try {
            pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey
                    .getInstance(pemContent);
        } catch (final Exception e) {
            throw new IllegalArgumentException(
                    "Could not parse BER PKCS#1 public key structure", e);
        }

        // --- convert to JCE RSAPublicKey
        final RSAPublicKeySpec spec = new RSAPublicKeySpec(
                pkcs1PublicKey.getModulus(), pkcs1PublicKey.getPublicExponent());
        final KeyFactory rsaKeyFact;
        try {
            rsaKeyFact = KeyFactory.getInstance("RSA");
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException("RSA KeyFactory should be available", e);
        }
        try {
            return (RSAPublicKey) rsaKeyFact.generatePublic(spec);
        } catch (InvalidKeySpecException e) {
            throw new IllegalArgumentException(
                    "Invalid RSA public key, modulus and/or exponent invalid", e);
        }
    }

    public static void main(final String ... args) throws Exception {
        final RSAPublicKey publicKey = parsePEM(PEM);
        System.out.println(publicKey);
    }
}

您还可以使用正确的DER结构前缀来创建“SubjectPublicKey”,并使用“X509PublicKeySpec”解析它,但这仅适用于您事先知道确切的密钥大小,因此比这更脆弱的代码。 - Maarten Bodewes
这个回答解决了你的问题吗,Khaled? - Maarten Bodewes

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