【问题标题】:Generating Rsa public key from string从字符串生成 Rsa 公钥
【发布时间】:2019-12-14 08:06:45
【问题描述】:

这是我的服务器公钥:

UlNBMQAIAAADAAAAAAEAAAAAAAAAAAAAAQABupDgHg710Jh4QwJriIdrY+6yyZme0e0FSQ1EFO3JEmiT+DXRLgU7CcgqGqLhosqYV7Rm1/H0I0ugFBk4zisPbgIb3epyMCcjnTgknRYddtIl9jNS8Z91/1SJ5bY0FkIRFJLHSCEcu1Un09w+gpWRoeAhSc87EMGngUZixAcyoDeYsW0mM1IlH44flo76wXERadmtAd9KFN0nYTjMwkIDyc5B9I2zvSjOQ+p54+6dl6o6nOalCWJg8V137ZMwQCk/4Tmp4C52d0AzYjEeU9gQowkrm7DgjLthd4ew/kY5a/8JfsWMJhajDPgzBV5fFJMbNL1h0fmtG8QpH5OtWukDhQ==

和加密代码:

 byte[] publicBytes = Base64.decode(PUBLIC_KEY, Base64.DEFAULT);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            Cipher cipher = Cipher.getInstance("RSA"); //or try with "RSA"
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            encrypted = cipher.doFinal(txt.getBytes());
            encoded = Base64.encodeToString(encrypted, Base64.DEFAULT);

我在尝试加密时收到此错误:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException:错误:0D0680A8:asn1 编码 例程:ASN1_CHECK_TLEN:标签错误

这有什么问题?

【问题讨论】:

  • 这是硬编码。如何使用字符串公钥加密数据?

标签: java android encryption


【解决方案1】:

如前所述,这是一种用于公共 RSA 密钥的 Microsoft 专有格式,BCRYPT_RSAPUBLIC_BLOB

BCRYPT_RSAKEY_BLOB
PublicExponent[cbPublicExp]          // Big-endian.
Modulus[cbModulus]                   // Big-endian.

typedef struct _BCRYPT_RSAKEY_BLOB {
    ULONG Magic;                      // identifies the type, e.g. BCRYPT_RSAPUBLIC_MAGIC for an RSA public key
    ULONG BitLength;                  // size, in bits, of the key
    ULONG cbPublicExp;                // size, in bytes, of the exponent of the key
    ULONG cbModulus;                  // size, in bytes, of the modulus of the key
    ULONG cbPrime1;                   // size, in bytes, of the first prime number of the key. This is only used for private key BLOBs
    ULONG cbPrime2;                   // size, in bytes, of the second prime number of the key. This is only used for private key BLOBs
} BCRYPT_RSAKEY_BLOB;

BCRYPT_RSAKEY_BLOB 中的值采用小端格式,指数和模数采用大端格式。 BCRYPT_RSAPUBLIC_MAGIC 的值 8263647540x31415352 为大端,0x52534131 为小端。后者对应字符串RSA1的ASCII编码。

贴出的key是Base64解码后的十六进制格式:

525341310008000003000000000100000000000000000000010001ba90e01e0ef5d0987843026b88876b63eeb2c9999ed1ed05490d4414edc9126893f835d12e053b09c82a1aa2e1a2ca9857b466d7f1f4234ba0141938ce2b0f6e021bddea723027239d38249d161d76d225f63352f19f75ff5489e5b6341642111492c748211cbb5527d3dc3e829591a1e02149cf3b10c1a7814662c40732a03798b16d263352251f8e1f968efac1711169d9ad01df4a14dd276138ccc24203c9ce41f48db3bd28ce43ea79e3ee9d97aa3a9ce6a5096260f15d77ed933040293fe139a9e02e7677403362311e53d810a3092b9bb0e08cbb617787b0fe46396bff097ec58c2616a30cf833055e5f14931b34bd61d1f9ad1bc4291f93ad5ae90385

或分离成单独的组件:

little endian   big endian
52534131        0x31415352 
00080000        0x0800     = 2048 bits
03000000        0x  03     =    3 bytes 
00010000        0x0100     =  256 bytes
00000000    
00000000

big endian
010001
ba90e01e0ef5d0987843026b88876b63eeb2c9999ed1ed05490d4414edc9126893f835d12e053b09c82a1aa2e1a2ca9857b466d7f1f4234ba0141938ce2b0f6e021bddea723027239d38249d161d76d225f63352f19f75ff5489e5b6341642111492c748211cbb5527d3dc3e829591a1e02149cf3b10c1a7814662c40732a03798b16d263352251f8e1f968efac1711169d9ad01df4a14dd276138ccc24203c9ce41f48db3bd28ce43ea79e3ee9d97aa3a9ce6a5096260f15d77ed933040293fe139a9e02e7677403362311e53d810a3092b9bb0e08cbb617787b0fe46396bff097ec58c2616a30cf833055e5f14931b34bd61d1f9ad1bc4291f93ad5ae90385

最后两部分是指数:

0x010001 = 65537

和模数

0xba90e01e0ef5d0987843026b88876b63eeb2c9999ed1ed05490d4414edc9126893f835d12e053b09c82a1aa2e1a2ca9857b466d7f1f4234ba0141938ce2b0f6e021bddea723027239d38249d161d76d225f63352f19f75ff5489e5b6341642111492c748211cbb5527d3dc3e829591a1e02149cf3b10c1a7814662c40732a03798b16d263352251f8e1f968efac1711169d9ad01df4a14dd276138ccc24203c9ce41f48db3bd28ce43ea79e3ee9d97aa3a9ce6a5096260f15d77ed933040293fe139a9e02e7677403362311e53d810a3092b9bb0e08cbb617787b0fe46396bff097ec58c2616a30cf833055e5f14931b34bd61d1f9ad1bc4291f93ad5ae90385

在 Java 中,可以使用类 RSAPublicKeySpec 创建公钥。这个类有一个期望指数和模数的构造函数,参见例如herehere

更新:

正如@Maarten Bodewes 的评论中提到的,在将模数和指数创建为BigInteger 时必须考虑以下几点:模数和指数是正值。然而,Java 使用2's complement 来表示整数,因此第一个字节还包含有关符号的信息(即,如果最左边的字节大于0x79,则值为负)。考虑到这一点,如果最左边的字节大于0x79,则可以添加0x00 字节。或者,有相应的构造函数,例如BigInteger(String val, int radix),如果数据以十六进制字符串形式提供(符号在 string 参数中被考虑为可选 +/-),或者BigInteger(int signum, byte[] magnitude),如果数据以字节形式提供数组(int 参数中考虑了符号)。由于在当前情况下,数据可能以十六进制字符串的形式提供,因此以下是合适的(例如,对于模数):

BigInteger modulus = new BigInteger("ba90e01e...5ae90385", 16);

【讨论】:

  • 请注意,您需要将new BigInteger(1, byte[]) 用于无符号数字,例如RSA 使用的数字...