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

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM