Java 何时使用 X509EncodedKeySpec 与 RSAPublicKeySpec?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24223275/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 10:54:59  来源:igfitidea点击:

When to use X509EncodedKeySpec vs RSAPublicKeySpec?

javaencryptionpublic-key-encryption

提问by user3203425

I have a certificate in a text file, its contents look like:

我有一个文本文件中的证书,其内容如下所示:

-----BEGIN PUBLIC KEY-----
xxxx
xxxx
xxxx
-----END PUBLIC KEY-----

I believe this is a pem encoded certificate? So I want to load it now, I'm trying the following:

我相信这是一个pem编码的证书?所以我现在想加载它,我正在尝试以下操作:

X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
    readFileToByteArray("keyfile"));

but I get an InvalidKeySpecException.

但我得到一个 InvalidKeySpecException。

If I load the file, cut off the begin/end header/footer, then base64 decode the 'xxxx' contents, I don't get any complaints:

如果我加载文件,切断开始/结束页眉/页脚,然后 base64 解码“xxxx”内容,我不会收到任何投诉:

String contents = readFileToString("keyfile");
contents = contents.replace("-----BEGIN PUBLIC KEY-----", "");
contents = contents.replace("-----END PUBLIC KEY-----", "");
byte[] prepared = Base64.decode(contents);

// ok.
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(prepared);

is this the right way to load the key file? I see there's also a RSAPublicKeySpecclass, which, based on the name, seems like something I'd be interested in here. But I believe it is only for generating certificates, not reading existing ones?

这是加载密钥文件的正确方法吗?我看到还有一个RSAPublicKeySpec类,根据名称,它似乎是我在这里感兴趣的东西。但我相信它仅用于生成证书,而不是读取现有证书?

Thanks

谢谢

采纳答案by President James K. Polk

There is a good summary of the common key formats here. As indicated by the javadocs for X509EncodedKeySpecthis class is designed to convert between the SubjectPublicKeyInfoASN.1 struct that is in the X.509 standard and Java public key formats. And since the first link indicated that a file of the form

此处对常用密钥格式进行了很好的总结。如X509EncodedKeySpec的 javadocs 所示,此类旨在在 X.509 标准中的SubjectPublicKeyInfoASN.1 结构和 Java 公钥格式之间进行转换。并且由于第一个链接表明该表单的文件

-----BEGIN PUBLIC KEY-----
xxxx
xxxx
-----END PUBLIC KEY-----

-----开始公钥-----
xxxx
xxxx
-----结束公钥-----

is indeed a SubjectPublicKeyInfo, you are correctly parsing the file. There is one final step you're missing, and that's to convert your X509EncodedKeySpec into a public key. That is the function of the KeyFactory class. To extend your example by two more lines, it would be

确实是一个SubjectPublicKeyInfo,您正在正确解析文件。您还缺少最后一步,那就是将您的 X509EncodedKeySpec 转换为公钥。这就是KeyFactory 类的功能。要将您的示例再扩展两行,它将是

KeyFactory kf = KeyFactory.getInstance("RSA"); // Assuming this is an RSA key
RSAPublicKey rsaPubKey = (RSAPublicKey) kf.generatePublic(publicKeySpec);

回答by user3786340

The answer is not straightforward. Following code works for me when loading RSA public key string (encoded in PEM format as mentioned above) to java.security.PublicKeyobject. Note: the headers and ends should be stripped beforehand.

答案并不简单。将 RSA 公钥字符串(以上述 PEM 格式编码)加载到java.security.PublicKey对象时,以下代码对我有用。注意:头部和末端应事先剥离。

    public PublicKey convertToPublicKey(String publicKeyString) {
    try {
        byte[] decodedPublicKey = Base64.decode(publicKeyString, Base64.DEFAULT);
        org.spongycastle.asn1.ASN1InputStream in = new org.spongycastle.asn1.ASN1InputStream(decodedPublicKey);
        org.spongycastle.asn1.ASN1Primitive obj = in.readObject();
        org.spongycastle.asn1.pkcs.RSAPublicKey keyStruct = org.spongycastle.asn1.pkcs.RSAPublicKey.getInstance(obj);
        java.security.spec.RSAPublicKeySpec keySpec = new java.security.spec.RSAPublicKeySpec(keyStruct.getModulus(), keyStruct.getPublicExponent());
        java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

Then, the java.security.PublicKeyinstance can be used to encrypt your string as follows:

然后,该java.security.PublicKey实例可用于加密您的字符串,如下所示:

Cipher rsaCipher = Cipher.getInstance("RSA/None/PKCS1Padding", "SC");
rsaCipher.init(Cipher.ENCRYPT_MODE, apiPublicKey);
byte[] ENCRYPTED_YOUR_STRING = rsaCipher.doFinal(YOUR_STRING);

The bytes can then be converted to string by: Base64.encodeToString(ENCRYPTED_YOUR_STRING, Base64.DEFAULT)

然后可以通过以下方式将字节转换为字符串: Base64.encodeToString(ENCRYPTED_YOUR_STRING, Base64.DEFAULT)

The solution is tested on Android API 25 Platform and spongycastle 1.56

该解决方案在 Android API 25 平台和 spongycastle 1.56 上进行了测试

回答by Ali Bagheri

Use this class

使用这个类

Note: use RSA only by less then 129 byte string.

注意:只能通过小于 129 字节的字符串使用 RSA。

public class RSA
{
private PublicKey internalPublicKey;
private PrivateKey internalPrivateKey;
private KeyPairGenerator kpg = null;
private int SIZE = 4096;

public RSA(int size)
{
    try
    {
        SIZE = size;
        kpg = KeyPairGenerator.getInstance("RSA");
        init();
    }
    catch(Exception e){}
}

public RSA()
{
    this(1024);
}

private void init()
{
    kpg.initialize(SIZE, new SecureRandom());

    KeyPair kp = kpg.genKeyPair();
    internalPublicKey = kp.getPublic();
    internalPrivateKey = kp.getPrivate();
}

public int getSize()
{
    return SIZE;
}

public PublicKey getPublic()
{
    return internalPublicKey;
}

public PrivateKey getPrivate()
{
    return internalPrivateKey;
}

public String getPublicModule()
{
    String s = internalPublicKey.toString();
    return s.substring(s.indexOf("modulus")+8, s.indexOf(",publicExponent"));
}

public String getPublicExponent()
{
    String s = internalPublicKey.toString();
    return s.substring(s.indexOf("publicExponent")+15, s.lastIndexOf("}"));
}

public String getPrivateExponent()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("privateExponent")+16, s.indexOf(",primeP"));
}

public String getPrivatePrimP()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeP=")+7, s.indexOf(",primeQ"));
}

public String getPrivatePrimQ()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeQ=")+7, s.indexOf(",primeExponentP"));
}

public String getPrivatePrimExponentP()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeExponentP=")+15, s.indexOf(",primeExponentQ"));
}

public String getPrivatePrimExponentQ()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("primeExponentQ=")+15, s.indexOf(",crtCoefficient"));
}

public String getPrivateCrtCoefficient()
{
    String s = internalPrivateKey.toString();
    return s.substring(s.indexOf("crtCoefficient=")+15, s.lastIndexOf(","));
}

public byte[] getPublicKey()
{
    return internalPublicKey.getEncoded();
}

public byte[] getPrivateKey()
{
    return internalPrivateKey.getEncoded();
}

public String getPublicKeyAsString()
{
    return Base64.encodeToString(internalPublicKey.getEncoded(), Base64.DEFAULT);
}

public String getPrivateKeyAsString()
{
    return Base64.encodeToString(internalPrivateKey.getEncoded(), Base64.DEFAULT);
}

public byte[] getEncrypt(String plain)
{
    try
    {
        //Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, internalPublicKey);

        return cipher.doFinal(plain.getBytes("UTF-8"));
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: getEncrypt(String x), ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public String getStringEncrypt(String plain)
{
    return new String(getEncrypt(plain), Charset.forName("UTF-8"));
}

public byte[] getDecrypt(byte[] encryptedBytes)
{
    try
    {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, internalPrivateKey);

        return cipher.doFinal(encryptedBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: getDecrypt(byte[] x), ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public byte[] getDecrypt(String encrypted)
{
    return getDecrypt(encrypted.getBytes());
}

public String getStringDecrypt(byte[] encryptedBytes)
{
    return new String(getDecrypt(encryptedBytes), Charset.forName("UTF-8"));
}

public String getStringDecrypt(String encrypted)
{
    return new String(getDecrypt(encrypted), Charset.forName("UTF-8"));
}

public static byte[] getEncrypt(String plain, String modulus, String exponent)
{
    try
    {
        BigInteger modBigInteger = new BigInteger(modulus, 16);
        BigInteger exBigInteger = new BigInteger(exponent, 16);
        RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);

        KeyFactory factory = KeyFactory.getInstance("RSA");
        PublicKey pk = factory.generatePublic(spec);

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pk);

        return cipher.doFinal(plain.getBytes("UTF-8"));
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: getEncrypt, ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static String getStringEncrypt(final String plain, String modulus, String exponent)
{
    return Base64.encodeToString(getEncrypt(plain, modulus, exponent), Base64.DEFAULT);
}

public static byte[] getDecrypt(byte[] encryptedBytes, String modulus, String publicExpo, String privateExpo, String primP, String primQ, String ePrimP, String ePrimQ, String cof)
{
    try
    {
        BigInteger module = new BigInteger(modulus, 16);
        BigInteger expo1 = new BigInteger(publicExpo, 16);
        BigInteger expo2 = new BigInteger(privateExpo, 16);
        BigInteger prim_P = new BigInteger(primP, 16);
        BigInteger prim_Q = new BigInteger(primQ, 16);
        BigInteger prim_EP = new BigInteger(ePrimP, 16);
        BigInteger prim_EQ = new BigInteger(ePrimQ, 16);
        BigInteger coefficient = new BigInteger(cof, 16);
        /*BigInteger module = new BigInteger(1, Base64.encode(modulus.getBytes(), Base64.DEFAULT));
        BigInteger expo1 = new BigInteger(1, Base64.encode(publicExpo.getBytes(), Base64.DEFAULT));
        BigInteger expo2 = new BigInteger(1, Base64.encode(privateExpo.getBytes(), Base64.DEFAULT));
        BigInteger prim_P = new BigInteger(1, Base64.encode(primP.getBytes(), Base64.DEFAULT));
        BigInteger prim_Q = new BigInteger(1, Base64.encode(primQ.getBytes(), Base64.DEFAULT));
        BigInteger prim_EP = new BigInteger(1, Base64.encode(ePrimP.getBytes(), Base64.DEFAULT));
        BigInteger prim_EQ = new BigInteger(1, Base64.encode(ePrimQ.getBytes(), Base64.DEFAULT));
        BigInteger coefficient = new BigInteger(1, Base64.encode(cof.getBytes(), Base64.DEFAULT));*/

        RSAPrivateCrtKeySpec spec = new RSAPrivateCrtKeySpec(module, expo1, expo2, prim_P, prim_Q, prim_EP, prim_EQ, coefficient);

        KeyFactory factory = KeyFactory.getInstance("RSA");
        PrivateKey pk = factory.generatePrivate(spec);

        Cipher cipher1 = Cipher.getInstance("RSA");
        cipher1.init(Cipher.DECRYPT_MODE, pk);

        //return cipher1.doFinal(Base64.decode(encryptedBytes, Base64.DEFAULT));
        return cipher1.doFinal(encryptedBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static String getStringDecrypt(byte[] encryptedBytes, String modulus, String publicExpo, String privateExpo, String primP, String primQ, String ePrimP, String ePrimQ, String cof)
{
    return Converter.byteToString_UTF8(getDecrypt(encryptedBytes, modulus, publicExpo, privateExpo, primP, primQ, ePrimP, ePrimQ, cof));
}

public static byte[] getDecrypt(final byte[] encryptedBytes, byte[] privateKey)
{
    try
    {
        KeyFactory keyFac = KeyFactory.getInstance("RSA");
        KeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        PrivateKey pk = keyFac.generatePrivate(keySpec);

        Cipher cipher1 = Cipher.getInstance("RSA");
        cipher1.init(Cipher.DECRYPT_MODE, pk);
        return cipher1.doFinal(encryptedBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static String getStringDecrypt(final byte[] encryptedBytes, byte[] privateKey)
{
    return Converter.byteToString_UTF8(getDecrypt(encryptedBytes, privateKey));
}

public static String sign(String plainText, PrivateKey privateKey)
{
    try
    {
        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(privateKey);
        privateSignature.update(plainText.getBytes());

        byte[] signature = privateSignature.sign();

        return Base64.encodeToString(signature, Base64.DEFAULT);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: sign(), ", Constants.DEFAULT_ALERT_STATE);
    }

    return null;
}

public static boolean verify(String plainText, String signature, PublicKey publicKey)
{
    Signature publicSignature;

    try
    {
        publicSignature = Signature.getInstance("SHA256withRSA");
        publicSignature.initVerify(publicKey);
        publicSignature.update(plainText.getBytes());

        byte[] signatureBytes = Base64.decode(signature, Base64.DEFAULT);

        return publicSignature.verify(signatureBytes);
    }
    catch(Exception e)
    {
        ThreadHelper.exceptionAlert(e, Constants.TAG_FOR_LOG, "Err: verify(), ", Constants.DEFAULT_ALERT_STATE);
    }

    return false;
}

}

}