1. 程式人生 > >Java與.Net環境下RSA加密解密互動不成功的問題解決【續】

Java與.Net環境下RSA加密解密互動不成功的問題解決【續】

在前面《一篇文章》中提到了 Java和.Net環境下RSA加解密不能互通的情況,最後通過重寫.Net下面RSA演算法來解決了問題;本文將就這個問題再深入一點,看是否能有其他方式可以解決;

首先來看下Java對Security領域的支援情況
Jdk提供了非常強大的API集合,以及對常用演算法、協議的通用實現;主要包括四個方面:
1、加解密,該部分對常用演算法及其加密標準提供了支援,體現為JCA【Java Cryptographic Architecture】和JCE【Java Cryptographic Extension】,通過這兩個元件,使用者可以實現自己的加解密演算法,並可以在JVM中進行插拔,使用非常靈活;比如業界著名的bouncycastle實現就是通過JCE來提供的;有一點需要注意:通過java.security進行插拔時用到的自定義jar包一定是要經過數字簽名的,否則jvm會拒絕執行;
2、PKI

,這部分主要是提供對PKI體系的支援,比如X.509證書、CRLs【Certificate Revocations Lists】、OCSP【On-Line Certificate Status Protocol】、LDAP【Lightweight Directory Access Protocol】等等;
3、安全通訊,這部分主要是提供對底層安全通訊協議的支援,比如SSL/TLS上層的HTTPS等,通過JSSE【Java Secure Socket Extension】、JGSS【Java Generic Security Services】、JSASL【Java Simple Authentication and Security Layer】等元件來實現;
4、鑑別和訪問控制
,這部分實現對多種鑑別機制實現SSO,以及對各類資源實現許可權控制,主要元件有:JAAS【Java Authentication and Authorization Service】、Policy……
具體的各元件的內容可以從Java SE Security首頁http://java.sun.com/javase/technologies/security/index.jsp檢視;以上內容體現在JDK中的package主要有:java.security.*、javax.crypto.*、javax.security.*;
其支援的各類標準演算法可以到http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html
檢視;

.Net對Security的支援相對來說要弱一些,他沒有分類這麼詳細,也沒有提供類似JCA/JCE這類可擴充套件元件,對各類標準演算法的支援也較少;他主要通過System.Security這個名稱空間來提供支援,而我們最常用到的應該是:
System.Security.Cryptography,該名稱空間提供加密服務,包括安全的資料編碼和解碼,以及許多其他操作,例如雜湊法、隨機數字生成和訊息身份驗證。
System.Security.Cryptography.X509Certificates,該名稱空間提供對Authenticode X.509 v.3 證書的公共語言執行庫實現。

回到上次我們遇到的問題,.Net環境下每次用RSA演算法加密都會得到不同的結果,這是因為每次都添加了一些隨機數,其實這些隨機數的生成也是遵循演算法標準的,更專業的說是隨機填充演算法,比如NoPadding、ISO10126Padding、OAEPPadding、PKCS1Padding、PKCS5Padding、SSL3Padding,而最常見的應該是OAEPPadding【Optimal Asymmetric Encryption Padding】、PKCS1Padding;.Net的實現用的是PKCS1Padding,而且只有這一種實現,也不能通過引數指定用哪種填充演算法;另外,通過RSACryptoServiceProvider.KeyExchangeAlgorithm檢視到的結果是RSA-PKCS1-KeyEx,由於無法看到原始碼,不知道最後的KeyEx代表什麼意思?
反觀Java的實現,主要通過Cipher.getInstance實現,傳入的引數是描述為產生某種輸出而在給定的輸入上執行的操作(或一組操作)的字串。必須包括加密演算法的名稱,後面可能跟有一個反饋模式和填充方案。這樣的實現就比較靈活,我們可以通過引數指定不同的反饋模式和填充方案;比如Cipher.getInstance("RSA/ECB/PKCS1Padding"),或者Cipher.getInstance("RSA")均可,但用其加密的效果也會不一樣;
用.Net的與Java對照下,應該KeyEx對應於反饋模式,但是我用Java中所有可能的反饋模式【NONE、CBC 、CFB/CFBx、CTR、CTS、ECB、OFB/OFBx、PCBC】來進行測試,發現還是沒有一種能夠實現雙方加解密互通!
上次一位網友說.Net/C++兩者可以互通,這是完全可能的,我不熟悉C++,但只要他和.Net採用相同的加密規則(包括加密演算法、反饋模式、填充方案)就可以互通,這個規則適合於所有的語言之間;

雖然雙方的加解密不能互通,但是數字簽名和驗證卻沒有問題,用預設實現類即可;因為數字簽名是不能採用填充演算法來進行加密的,這樣就失去了對原始資料簽名的意義了;

另外,還有一個小細節,.Net對X.509證書的支援是v3版本,而Java支援的卻是v1版本(用keytool生成的jks檔案都是對應v1版本的),雖然目前我在使用證書上沒有發現任何問題,但我想應該是我們沒有用到證書的一些細節地方;