C#与Java的RSA中的X509EncodedKeySpec、PKCS8EncodedKeySpec

1、JAVA - RSA使用X509EncodedKeySpec、PKCS8EncodedKeySpec生成公钥和私钥

	private static final String KEY_ALGORITHM = "RSA";  
	private static final String PUBLIC_KEY ="publicKey";
	private static final String PRIVATE_KEY ="privateKey"; 
        public static void main(String[] args) throws Exception{
		Map<String,String> keyMap = genKey();
		RSAPublicKey publicKey = getPublicKey(keyMap.get(PUBLIC_KEY));
		RSAPrivateKey privateKey = getPrivateKey(keyMap.get(PRIVATE_KEY));
		String info ="明文123456";
		//加密
		byte[] bytes = encrypt(info.getBytes("utf-8"),publicKey);
		//解密
		bytes = decrypt(bytes, privateKey);
		System.out.println(new String(bytes,"utf-8"));

	}

	public static Map<String,String> genKey() throws NoSuchAlgorithmException{
		Map<String,String> keyMap = new HashMap<String,String>();
		KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		SecureRandom random = new SecureRandom();
		// random.setSeed(keyInfo.getBytes());
		// 初始加密,512位已被破解,用1024位,最好用2048位
		keygen.initialize(1024, random);
		// 取得密钥对
		KeyPair kp = keygen.generateKeyPair();
		RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
 		String privateKeyString = Base64.encode(privateKey.getEncoded());
		RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic(); 
		String publicKeyString = Base64.encode(publicKey.getEncoded());
		keyMap.put(PUBLIC_KEY, publicKeyString);
		keyMap.put(PRIVATE_KEY, privateKeyString);
		return keyMap;
	}

	public static RSAPublicKey getPublicKey(String publicKey) throws Exception{
		byte[] keyBytes = LBase64.decode(publicKey);
		X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		return (RSAPublicKey) keyFactory.generatePublic(spec);
	}

	public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception{
		byte[] keyBytes = LBase64.decode(privateKey);
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		return (RSAPrivateKey) keyFactory.generatePrivate(spec);
	}

2、C# - RSA使用X509EncodedKeySpec、PKCS8EncodedKeySpec生成公钥和私钥

/*********** Java代码 ***********/
//System.out.println(bytes2hex(publicKey.getModulus().toByteArray()));
//System.out.println(bytes2hex(publicKey.getPublicExponent().toByteArray()));

//设定RSA参数,用于指定modulus和exponent
var parameter = new RSAParameters();
parameter.Modulus = modulusBytes;       //modulusBytes是转化后的byte[]
parameter.Exponent = exponentBytes;
                                                     
//加密
var rsa = RSACryptoServiceProvider.Create("RSA");
rsa.ImportParameters(parameter);
byte[] result = rsa.EncryptValue(Encoding.UTF8.GetBytes("PASSWORD"));
                                                   
//把密文转化为HEX形式
string resultHex = BitConverter.ToString(result).Replace("-", "");

#if 0
//把3个变量转化为System.Numerics.BigInteger
var modulus = new BigInteger(modulusBytes);
var exponent = new BigInteger(exponentBytes);
var data = new BigInteger(Encoding.UTF8.GetBytes("PASSWORD"));
                                           
//做ModPow运算得到密文,也是BigInteger
var result = BigInteger.ModPow(data, exponent, modulus);
                                            
//把密文BigInteger对应的byte[]转化为HEX形式
string resultHex = BitConverter.ToString(result.ToByteArray()).Replace("-", "");
#endif

//把3个变量转化为System.Numerics.BigInteger
var modulus = new BigInteger(modulusBytes.Reverse().ToArray());
var exponent = new BigInteger(exponentBytes.Reverse().ToArray());
var data = new BigInteger(Encoding.UTF8.GetBytes("PASSWORD").Reverse().ToArray());
                                            
//做ModPow运算得到密文,也是BigInteger
var result = BigInteger.ModPow(d, e, m);
                                            
//把密文BigInteger对应的byte[]转化为HEX形式
string resultHex = BitConverter.ToString(result.ToByteArray().Reverse().ToArray()).Replace("-", "");

3、C# - JAVA - RSA使用X509EncodedKeySpec、PKCS8EncodedKeySpec生成公钥和私钥解析

  1. Java程序员很少想过getEncoded()是什么算法,它是如何把modulus与publicExponent封装到一个byte[]里去的。

  2. 而对于C#程序员来讲,RSA公钥加密必须要用到modulus与publicExponent,无论它们是XML形式还是byte[]形式,因此如何从publicKeyHex中解析这两个参数就成了第一个关键点。

  3. JavaDoc:

An Encoded Form

This is an external encoded form for the key used when a standard representation of the key is needed outside
the Java Virtual Machine, as when transmitting the key to some other party. The key is encoded according to
a standard format (such as X.509 SubjectPublicKeyInfo or PKCS#8), and is returned using the getEncoded method.
Note: The syntax of the ASN.1 type SubjectPublicKeyInfo is defined as follows:

SubjectPublicKeyInfo ::= SEQUENCE {
  algorithm AlgorithmIdentifier,
  subjectPublicKey BIT STRING 
}

AlgorithmIdentifier ::= SEQUENCE {
  algorithm OBJECT IDENTIFIER,
  parameters ANY DEFINED BY algorithm OPTIONAL 
}

For more information, see RFC 3280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile.

根据ASN.1标准进行保存的,涉及到了2种格式:X.509 SubjectPublicKeyInfo和PKCS#8, 具体是哪种可以从getFormat()的方法说明里可以找到答案:公钥使用的是X.509 SubjectPublicKeyInfo,私钥使用的是PKCS#8。 

JavaDoc里已经描述了SubjectPublicKeyInfo的结构如下:

SEQUENCE {
    SEQUENCE {          //algorithm,我们不用关心具体结构
    }
    BIT STRING {        //SubjectPublicKey,以BIT STRING存储
        SEQUENCE {
            INTEGER     //modulus
            INTEGER     //publicExponent
        }
    }
}

Java端使用的是2048位的标准RSA加密,给出的公钥HEX字符串如下:

30820122300D06092A864886F70D01010105000382010F003082010A02820101008C214751E6EA33378080F64BF55C0888D3EFA4DF08794318069DDFD14A3AB6468B20CD134819100FA20539785AECF595CF2333F7ADC48366F4ACBC41B1CED728B57417CF3B6CA4E7DDB9DA348F7D38158DD6F2FF3934AEB0A70732E2949505EF893A940404B1B5F4B69243E2877BBA90E5994EBFD61986F412DE4AD3E8331CE1D3D41ADAEF5C79D5B22E05C7F76FC748BC5FA42345D70EC3D1DE3DBD338C300C3750841E2E16E7B907E536FCA1A40D05DC9DFCDE4EB2E8575228309AD146486E6F21C386E90C36DEECB57F955CE68609204AFBD434F8A1BFB5D921C470EED82CCA8BFDA92A8EC668E9E9EB6F959CD535C8BCCFCB08A671983A27E8B03F5BF90D0203010001

对照着ASN.1规范,格式如下:

  二进制          解析
--------------------------------------------------------------
30  0011 0000       TAG:类型00=通用,1=结构体,10000=16=SEQUENCE
82  1000 0010       LEN:定长,长形式,后面2字节是长度
01                     长度 0x122=290字节,注意是大字节序
22
30  0011 0000       TAG:类型00=通用,1=结构体,10000=16=SEQUENCE
0D  0000 1101       LEN:定长,短形式,13字节
06                  VAL:13字节,我们不关心内容,跳过
09
.
.
.
05
00
03  0000 0011       TAG:类型00=通用类型,0=简单数据,00011=3=BIT STRING
82  1000 0010       LEN:定长,长形式,后面2字节是长度,注意是大字节序
01                     长度 0x10f=271字节
0F
00                  保留字
30  0011 0000       TAG:类型00=通用类型,1=结构体,10000=16=SEQUENCE
82  1000 0010       LEN:定长,长形式,后面2字节是长度,注意是大字节序
01                     长度 0x10a=266字节
0A
02  0000 0010       TAG:类型00,0=简单数据,00010=2=INTEGER
82  1000 0010       LEN:定长,长形式,后面2字节是长度,注意是大字节序
01                      长度=0x101,257字节
01
00                  VAL:257字节,是公钥中的modulus
8C
.
.
.
F9
0D
02  0000 0010       TAG:类型00,0=简单数据,00010=2=INTEGER
03  0000 0011       LEN:定长,短形式,3字节
01 00 01            VAL:3字节,是公钥中的publicExponent
再度整理后:
类型  长度位  实际长度
-----------------------------------------------
30    82     01 22     SEQUENCE {
30    0D     0D            SEQUENCE {
                           }
03    82     01 0F         BIT STRING {
30    82     01 0A             SEQUENCE {
02    82     01 01                 INTEGER
                                       00 8C 21 47 51 E6 EA 33 37 80 80 F6 4B F5 5C 08
                                       88 D3 EF A4 DF 08 79 43 18 06 9D DF D1 4A 3A B6
                                       .......
                                       35 C8 BC CF CB 08 A6 71 98 3A 27 E8 B0 3F 5B F9
                                       0D
02    03     03                    INTEGER
                                       01 00 01
                               }
                           }
                       }

  

原文地址:https://www.cnblogs.com/desultory-essay/p/13501738.html