2012-12-19 48 views
3

我使用的是私有RSA密鑰來加密使用默認Java RSA實現一個隨機的AES密鑰:重新實現RSA解密在Java中

Cipher cipher = Cipher.getInstance("RSA"); 
cipher.init(Cipher.ENCRYPT_MODE, privateKey); 
byte[] cipherBytes = cipher.doFinal(plainText.getBytes()); 

因爲我們需要一個公共密鑰,無論如何,這是一個方便的方法來僞裝密鑰並確保它已用我們的私鑰加密。解密同樣做:

Cipher cipher = Cipher.getInstance("RSA"); 
cipher.init(Cipher.DECRYPT_MODE, publicKey); 
byte[] plainBytes = cipher.doFinal(cipherBytes); 

這正常工作與Oracle的JDK,但與IBM的失敗,因爲IBM認爲使用加密私鑰是不是一個有效的用例。不幸的是,我必須同時支持兩個JDK,所以我試圖自己重新實現RSA解密。

這是我到目前爲止的代碼:

BigInteger big = new BigInteger(cipherBytes); 
big = big.modPow(pub.getPublicExponent(), pub.getModulus()); 
System.out.println(new String(big.toByteArray())); 

它幾乎工作,但似乎有一個填充問題。大多數情況下,我會在原始文本的前面加上一串點狀符號,但有時它只是隨機字節。

不幸的是,我無法確定默認使用哪種填充方案。有誰知道我的代碼中缺少什麼,或者至少可以提供使用哪種算法填充處理的提示?

下面是一個輸入和輸出值的例子,根據要求。我使用了512位密鑰來避免太大的數字。

Public modulus : 8117919732251191237549784557538073836207094968952416063837701691514861428726690140363567956265691836505266266364256892197254736023284927189008247933889303 
Public exponent: 65537 
Plaintext:  teststring 
Plaintext as BN: 549665952565679142563431 
Ciphertext as BN: 6304229782339071167863563708554898540621778162930150363326921290545577949349781053660336996882823758722402137580193903457839924005473545992074817339077456 
"Decrypted" BN: 409173825987017733751648712103449894027080255755383098685411421012016724550584319360408761540738019643860835515945008876151848132891805352276483731047 
Resultstring: ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇteststring 

爲了解決爲什麼我這樣做了討論:

公鑰是硬編碼到我的軟件。我使用私鑰來加密AES的另一個密鑰。因此,要使用AES實際解碼任何內容,首先需要AES密鑰。要得到這個密鑰,你必須先用公鑰解密它。由於無需嚴格操作就無法修改公鑰,因此只有使用私鑰加密的AES密鑰才能工作。您可能會以某種方式提取公鑰並解密AES密鑰,但這很詳細,只能讓您獲得AES密鑰來解密安全內容。還有一個使用私鑰計算的簽名,該密鑰也使用公鑰進行驗證。所以操縱是不可能的。

所以是的,從技術上說簽名是足夠的,因爲有方法來閱讀內容。但這些都是精心設計的,我不介意是否有人真的把所有的麻煩都拿走了,但我不想讓事情變得簡單。

+4

這不是一個有效的用例。有一件事必須是私人和其他公衆。當然,你可以放棄「私鑰」並保持「公鑰」的私密性,但這很愚蠢。 http://crypto.stackexchange.com/questions/2123/rsa-encryption-with-private-key-and-decryption-with-a-public-key –

+0

PKCS1可能是一個很好的起點http://en.wikipedia .org/wiki/PKCS1爲了理解填充算法,你也可以檢查PKCS5和PKCS7填充標準桿 –

+0

我檢查過PKCS填充,我不認爲它被使用。填充字節是填充字節的數量,因此必須有重複模式。不幸的是,情況並非如此。 – user1916974

回答

0

好吧,我已經通過閱讀RSA規範瞭解了它。要添加更多的安全性,填充,加密前加入和下面的「串」被創建:

0x00 + BT + Padding + 0x00 + Data 

塊類型(BT)表示的那種填充。在BT = 0x01的情況下,填充是0xff並且BT = 0x02,填充是隨機的但非零。連接的字符串然後被加密。

解密時,格式可以驗證,但只是讀取數據,必須刪除前導字節。直到數據前的0x00,它們都是非零的。因此,直到幷包括填充後的0x00的所有內容都可以被刪除。信息剩下的是什麼。

此代碼的工作現在:

// Decrypt 
byte[] decryptedBytes = (new BigInteger(1, cipherBytes)).modPow(pub.getPublicExponent(), pub.getModulus()).toByteArray(); 

// Extract msg 
int dataStart; 
for (dataStart = 0; decryptedBytes[msgStart] != 0; dataStart++); 
dataStart++; 

byte finalBytes[] = new byte[decryptedBytes.length - msgStart]; 
System.arraycopy(decryptedBytes, msgStart, finalBytes, 0, finalBytes.length); 

這也可以解釋的字符串「^」在我以前的嘗試。那些填充字節是BTff = 0x01的0xff。

我只需要解密,但爲了完整起見,這是加密的代碼:

int bitLength = 512; 
String plainText = "teststring"; 

// Convert to bytes 
byte plainBytes[] = plainText.getBytes(); 

byte encryptionBytes[] = new byte[bitLength/8]; 

encryptionBytes[0] = 0; // Leading 0 
encryptionBytes[1] = 1; // Block type 

// Padding String 
int paddingEnd = (bitLength/8) - plainBytes.length - 2; 
for (int i = 2; i < paddingEnd; i++) { 
    encryptionBytes[i] = (byte) 0xff; 
} 
encryptionBytes[paddingEnd + 1] = 0; 

// Actual data 
System.arraycopy(plainBytes, 0, encryptionBytes, paddingEnd + 1, plainBytes.length); 

// Encrypt 
byte[] cipherBytes = (new BigInteger(1, encryptionBytes)).modPow(priv.getPrivateExponent(), priv.getModulus()).toByteArray(); 

希望這有助於人:)

+0

該代碼不顯示加密。 –

0

什麼你正在嘗試做的看起來更像簽署而非加密。使用單獨的密鑰對進行簽名,因爲它與加密不完全相同。

+0

你的回答真的是一個評論。 –

1

公鑰用於加密和驗證簽名。私鑰用於解密和簽名。公鑰旨在成爲:公共。如果你正在做的事情,應該沒有理由隱藏公鑰。

+0

我不想隱藏公鑰。我想要像簽名一樣使用私鑰加密。我沒有在HMAC上使用真正的簽名,因爲只使用RSA,「簽名」的內容也被加密。我知道你可以用公鑰解密,但公鑰是硬編碼到軟件中的,所以你需要私鑰來加密任何東西以便被軟件正確解密。 – user1916974

+0

等一下,那麼您正在與使用公鑰解密的第三方軟件進行通信?如果是這樣,你應該預先說明。我也不清楚你爲什麼說使用RSA強制簽名的內容被加密,因爲這不是事實。發送明文和簽名是很常見的。 –