Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何从未格式化字符串中获取RSA公钥_Java_Rsa - Fatal编程技术网

Java 如何从未格式化字符串中获取RSA公钥

Java 如何从未格式化字符串中获取RSA公钥,java,rsa,Java,Rsa,我在属性文件中有一个未格式化的公钥,这意味着它只包含公钥hexa值: K_PUB_CCE = 3082010902820100A515281FAC9ABAA8E966DC1B6EC0F1C431674B4E7BCB718955A34211D5CC6BA53F2C93F67C030A970D4E41341949E6BC3F9336287EEA21702FE663C83D4BAFEE2AAA2EEE7A6AFDC423A159D420E42ABDE2792080249C5D6E25367F8043

我在属性文件中有一个未格式化的公钥,这意味着它只包含公钥hexa值:

K_PUB_CCE =  3082010902820100A515281FAC9ABAA8E966DC1B6EC0F1C431674B4E7BCB718955A34211D5CC6BA53F2C93F67C030A970D4E41341949E6BC3F9336287EEA21702FE663C83D4BAFEE2AAA2EEE7A6AFDC423A159D420E42ABDE2792080249C5D6E25367F804333665CAFB79FD5A59D70B9F69159F4EDAD2DA4B434F41D0EC5217808E7D91FF547C83774E4BDE813302E16377156E52CAF02D1E68371D536AA0E7E32DE484FF4863538DCBC69E8D3E3C1F3CECEA9861DA5516A06D3208F6363B86CF66641BE18C4F41ABD7F1B5CDC9BD964914515DDC58F32F49437BD7E89431C8F484BBCEBA86A5FFF74E01D12E6D1D7EBBF6E5DCD7A9134BF27185F4BD347B23007FF366C0009E14F0203010001
如你所见,它有538个六边形长。我想要实现的是获得一个具有此值的java.security.PublicKey

但在使用这种方法时:

private PublicKey createPublicKey(String stringPublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] bytesKey= Hex.decodeHex(stringPublicKey.toCharArray());
    X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(bytesKey);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return  keyFactory.generatePublic(encodedKeySpec);
}
我得到以下例外情况:

java.security.InvalidKeyException: IOException: algid parse error, not a sequence

我做错了什么?是否还有其他我应该使用的密钥规范类?

将公钥存储为base64字符串,然后将其转换回是相当麻烦的,因为格式也可能因平台而异。使用密钥库文件会容易得多

如果这不是你的选择,还有另一种方法。您需要输入密钥模数和使用的指数。指数通常为65537,但也有一些方法可以确定,请参阅参考资料。这对我很有用:

public static void main(String[] args) {
    try{
        // example key generated via: $ openssl genrsa -out tmp-key.pem 2048
        // modulus and exponent extracted via: $ openssl rsa -text -in tmp-key.pem
        // (modulus 514 hex long)
        String pubModulus = "00e45679a14c6cbd4646bbe90b3c820eb19fe8366822ce2e4beed9d158b9e8863bdc4e3f0fbad31144ef193493dff8619a5aaa7504541c5d97c20cd29c87435eb2e4fbbab8e3b02d82353016c91e971e8d497e1699eeb77a833833617369333c4d0d93cd6f1a9e6090fafd4cbf00b1e0fc6478003ed6e762fb921446c84f0f281117e692a5f76e4b75cbb1252436b3268893195d25344cc3e5d5a52560243d62e5ce8a7bd72a89fce5fbf009435901e274c3cca5eab0f2b2057683ed6e3ed851723adbabb4028a7900ddc46d8c894097c07ab071f6af8fc1c520681e0abd7685f4851d360a7c6d425373da806d356a517ae764093e6999d2cc9305f46a7e1744ed";
        String pubExp = "65537"; // most common exponent is 65537 which encodes to AQAB

        PublicKey key = createPublicKey(pubModulus, pubExp);
        byte[] data = encrypt("Secret Message", key);
        System.out.println(""+data.length); // prints "256" in this example code
    } catch(Exception e) {
        e.printStackTrace();
    }
}

public static PublicKey createPublicKey(String stringPublicKey, String exponentString) throws NoSuchAlgorithmException, InvalidKeySpecException {
    try{
        // for key modulus and exponent value as base64 encoded string from key file
        //BigInteger keyInt = new BigInteger(Base64.getDecoder().decode(stringPublicKey.getBytes("UTF-8")));
        //BigInteger exponentInt = new BigInteger(Base64.getDecoder().decode(exponentString.getBytes("UTF-8")));

        // for key modulus and exponent values as hex and decimal string respectively
        BigInteger keyInt = new BigInteger(stringPublicKey,16); // hex base
        BigInteger exponentInt = new BigInteger(exponentString,10); // decimal base

        RSAPublicKeySpec keySpeck = new RSAPublicKeySpec(keyInt, exponentInt);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return  keyFactory.generatePublic(keySpeck);
    } catch(Exception e) {
        e.printStackTrace();
    }

    return null;
}

public static byte[] encrypt(String message, PublicKey key) {
    try{
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] data = cipher.doFinal(message.getBytes());
        return data;
    } catch(Exception e) {
        e.printStackTrace();
    }

    return new byte[1];
}
编辑:

对不起,我刚才看到你用的是十六进制字符串。原始RSA格式应该是一些base64编码值。因为您已经有十六进制值,所以模数的BigInteger创建应该不同。请参阅源代码编辑。 同样如下面的评论所述,十六进制字符串或Base64格式不会出现平台问题。这里唯一需要注意的是,当使用getBytes从String编码到Base64时,指定一种格式,如UTF-8。如果未指定格式,将使用平台默认值

参考资料:


将公钥存储为base64字符串,然后再将其转换回是相当麻烦的,因为格式也可能因平台而异。使用密钥库文件会容易得多

如果这不是你的选择,还有另一种方法。您需要输入密钥模数和使用的指数。指数通常为65537,但也有一些方法可以确定,请参阅参考资料。这对我很有用:

public static void main(String[] args) {
    try{
        // example key generated via: $ openssl genrsa -out tmp-key.pem 2048
        // modulus and exponent extracted via: $ openssl rsa -text -in tmp-key.pem
        // (modulus 514 hex long)
        String pubModulus = "00e45679a14c6cbd4646bbe90b3c820eb19fe8366822ce2e4beed9d158b9e8863bdc4e3f0fbad31144ef193493dff8619a5aaa7504541c5d97c20cd29c87435eb2e4fbbab8e3b02d82353016c91e971e8d497e1699eeb77a833833617369333c4d0d93cd6f1a9e6090fafd4cbf00b1e0fc6478003ed6e762fb921446c84f0f281117e692a5f76e4b75cbb1252436b3268893195d25344cc3e5d5a52560243d62e5ce8a7bd72a89fce5fbf009435901e274c3cca5eab0f2b2057683ed6e3ed851723adbabb4028a7900ddc46d8c894097c07ab071f6af8fc1c520681e0abd7685f4851d360a7c6d425373da806d356a517ae764093e6999d2cc9305f46a7e1744ed";
        String pubExp = "65537"; // most common exponent is 65537 which encodes to AQAB

        PublicKey key = createPublicKey(pubModulus, pubExp);
        byte[] data = encrypt("Secret Message", key);
        System.out.println(""+data.length); // prints "256" in this example code
    } catch(Exception e) {
        e.printStackTrace();
    }
}

public static PublicKey createPublicKey(String stringPublicKey, String exponentString) throws NoSuchAlgorithmException, InvalidKeySpecException {
    try{
        // for key modulus and exponent value as base64 encoded string from key file
        //BigInteger keyInt = new BigInteger(Base64.getDecoder().decode(stringPublicKey.getBytes("UTF-8")));
        //BigInteger exponentInt = new BigInteger(Base64.getDecoder().decode(exponentString.getBytes("UTF-8")));

        // for key modulus and exponent values as hex and decimal string respectively
        BigInteger keyInt = new BigInteger(stringPublicKey,16); // hex base
        BigInteger exponentInt = new BigInteger(exponentString,10); // decimal base

        RSAPublicKeySpec keySpeck = new RSAPublicKeySpec(keyInt, exponentInt);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return  keyFactory.generatePublic(keySpeck);
    } catch(Exception e) {
        e.printStackTrace();
    }

    return null;
}

public static byte[] encrypt(String message, PublicKey key) {
    try{
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] data = cipher.doFinal(message.getBytes());
        return data;
    } catch(Exception e) {
        e.printStackTrace();
    }

    return new byte[1];
}
编辑:

对不起,我刚才看到你用的是十六进制字符串。原始RSA格式应该是一些base64编码值。因为您已经有十六进制值,所以模数的BigInteger创建应该不同。请参阅源代码编辑。 同样如下面的评论所述,十六进制字符串或Base64格式不会出现平台问题。这里唯一需要注意的是,当使用getBytes从String编码到Base64时,指定一种格式,如UTF-8。如果未指定格式,将使用平台默认值

参考资料:

您所拥有的几乎是一个数组的DER编码的十六进制编码。这与X509EncodedKeySpec类处理的SubjectPublicKeyInfo不同。我这么说几乎是因为编码有点错误。模数被编码为负整数,这是错误的

处理这个问题的最简单的方法是用十六进制字符串手工分解模数和指数,如下面的小示例所示

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;

public class ProcessPKCS1RSAPublicKey {

    public static void main(String[] args) throws Exception {
        String modulusHex = "A515281FAC9ABAA8E966DC1B6EC0F1C431674B4E7BCB718955A34211D5CC6BA53F2C93F67C030A970D4E41341949E6BC3F9336287EEA21702FE663C83D4BAFEE2AAA2EEE7A6AFDC423A159D420E42ABDE2792080249C5D6E25367F804333665CAFB79FD5A59D70B9F69159F4EDAD2DA4B434F41D0EC5217808E7D91FF547C83774E4BDE813302E16377156E52CAF02D1E68371D536AA0E7E32DE484FF4863538DCBC69E8D3E3C1F3CECEA9861DA5516A06D3208F6363B86CF66641BE18C4F41ABD7F1B5CDC9BD964914515DDC58F32F49437BD7E89431C8F484BBCEBA86A5FFF74E01D12E6D1D7EBBF6E5DCD7A9134BF27185F4BD347B23007FF366C0009E14F";
        BigInteger modulus = new BigInteger(modulusHex, 16);
        String exponentHex = "010001";
        BigInteger exponent = new BigInteger(exponentHex, 16);
        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, exponent);
        KeyFactory keyFac = KeyFactory.getInstance("RSA");
        RSAPublicKey rsaPub = (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
        System.out.println(rsaPub);
    }

}
您所拥有的几乎是一个数组的DER编码的十六进制编码。这与X509EncodedKeySpec类处理的SubjectPublicKeyInfo不同。我这么说几乎是因为编码有点错误。模数被编码为负整数,这是错误的

处理这个问题的最简单的方法是用十六进制字符串手工分解模数和指数,如下面的小示例所示

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;

public class ProcessPKCS1RSAPublicKey {

    public static void main(String[] args) throws Exception {
        String modulusHex = "A515281FAC9ABAA8E966DC1B6EC0F1C431674B4E7BCB718955A34211D5CC6BA53F2C93F67C030A970D4E41341949E6BC3F9336287EEA21702FE663C83D4BAFEE2AAA2EEE7A6AFDC423A159D420E42ABDE2792080249C5D6E25367F804333665CAFB79FD5A59D70B9F69159F4EDAD2DA4B434F41D0EC5217808E7D91FF547C83774E4BDE813302E16377156E52CAF02D1E68371D536AA0E7E32DE484FF4863538DCBC69E8D3E3C1F3CECEA9861DA5516A06D3208F6363B86CF66641BE18C4F41ABD7F1B5CDC9BD964914515DDC58F32F49437BD7E89431C8F484BBCEBA86A5FFF74E01D12E6D1D7EBBF6E5DCD7A9134BF27185F4BD347B23007FF366C0009E14F";
        BigInteger modulus = new BigInteger(modulusHex, 16);
        String exponentHex = "010001";
        BigInteger exponent = new BigInteger(exponentHex, 16);
        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(modulus, exponent);
        KeyFactory keyFac = KeyFactory.getInstance("RSA");
        RSAPublicKey rsaPub = (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
        System.out.println(rsaPub);
    }

}

为什么要将其保存在属性文件中?为什么不按照上帝的意愿建立密钥库?为什么要将其保存在属性文件中?为什么不按照上帝的意愿建造一个密钥库呢?因为它是一个六角钉,所以平台问题不会出现。也不知道base64是如何产生的。否则+1@EJP您完全正确-对于十六进制字符串没有问题。但我认为,当使用base64字符串时,必须正确处理解码,以避免平台问题。在代码示例中,我特别使用UTF-8作为getBytesUTF-8格式进行解码,因为否则getBytes没有参数,我们必须依赖平台设置,这可能会产生一些意外的结果。@fishi很好,不再抛出异常。尽管如此,当使用此公钥加密数据时,我得到了一个269字节长的加密值。通常,它应该返回一个256字节长的加密值。哪个参数会影响这个?@DanielCalderonMori您给定的示例密钥也会为我返回一个269字节的加密消息。你能正确地解密它吗?当我使用我自己创建的密钥时,我会得到一条256字节的加密消息。请参阅编辑的代码示例,我在其中解释了如何创建键以及如何查找模数/指数。您的密钥比我的示例密钥514hex长538hex,因此我猜您的hex值存储的信息比所引用的模要多。希望这有助于base-64字符在每个平台和编码上都是相同的,是吗?当然这就是他们被选中的原因?因为这是一个妖术刺,平台问题不会出现。也不知道base64是如何产生的。否则+1@EJP你完全是对的
十六进制字符串没有问题。但我认为,当使用base64字符串时,必须正确处理解码,以避免平台问题。在代码示例中,我特别使用UTF-8作为getBytesUTF-8格式进行解码,因为否则getBytes没有参数,我们必须依赖平台设置,这可能会产生一些意外的结果。@fishi很好,不再抛出异常。尽管如此,当使用此公钥加密数据时,我得到了一个269字节长的加密值。通常,它应该返回一个256字节长的加密值。哪个参数会影响这个?@DanielCalderonMori您给定的示例密钥也会为我返回一个269字节的加密消息。你能正确地解密它吗?当我使用我自己创建的密钥时,我会得到一条256字节的加密消息。请参阅编辑的代码示例,我在其中解释了如何创建键以及如何查找模数/指数。您的密钥比我的示例密钥514hex长538hex,因此我猜您的hex值存储的信息比所引用的模要多。希望这有助于base-64字符在每个平台和编码上都是相同的,是吗?当然这就是他们被选中的原因?