1. 程式人生 > 實用技巧 >C#與Java的RSA中的X509EncodedKeySpec、PKCS8EncodedKeySpec

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
                               }
                           }
                       }