2016-06-07 93 views
2

我試圖生成一個AES密鑰,使用RSA對其進行加密和解密。 它種類的作品,除了在解密數據和使用Base64編碼之後,我在我的實際字符串(base64編碼的AES密鑰)之前得到一堆「A」字母。我猜這些是字節中的零。使用RSA解密字符串在開始時返回附加的零

「RSA/ECB/NoPadding」參數是強制性的。我究竟做錯了什麼 ?我需要它返回原始字符串/字節。

package szyfrator; 

import java.io.BufferedInputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.security.KeyFactory; 
import java.security.NoSuchAlgorithmException; 
import java.security.PrivateKey; 
import java.security.PublicKey; 
import java.security.spec.PKCS8EncodedKeySpec; 
import java.security.spec.X509EncodedKeySpec; 

import javax.crypto.Cipher; 
import javax.crypto.KeyGenerator; 
import javax.crypto.SecretKey; 

import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; 
import org.apache.commons.compress.utils.IOUtils; 
import org.apache.tools.bzip2.CBZip2OutputStream; 

import com.google.common.hash.HashCode; 
import com.google.common.hash.Hashing; 
import com.google.common.io.Files; 
import com.sun.org.apache.xml.internal.security.utils.Base64; 

public class Cryptography { 

    private static byte[] aesKey; 
    private static String base64AESKey; 
    private static byte[] encryptedAESKey; 
    private static String base64AESEncryptedKey; 
    private static byte[] aesKeyTransformed; 

    public static void main(String args[]){ 

     Cryptography.generateAESkey(); 
     Cryptography.encryptAESKey(new File("G:\\HASHBABYHASH\\public.txt")); 
     Cryptography.decryptAESKey(new File("G:\\HASHBABYHASH\\private.txt")); 

     System.out.println("String: " + Base64.encode(Cryptography.getAesKey()) + "\r\n"); 
     System.out.println("Encrypted string: " + Cryptography.getBase64EncryptedKey() + "\r\n"); 
     System.out.println("Decrypted String: " + Base64.encode(Cryptography.getAesKeyTransformed()) + "\r\n"); 

    } 

    public static void generateAESkey(){ 

     try { 
      KeyGenerator keyGen = KeyGenerator.getInstance("AES"); 

      keyGen.init(256); 
      SecretKey secretKey = keyGen.generateKey(); 

      byte[] keyBytes = secretKey.getEncoded(); 
      base64AESKey = Base64.encode(keyBytes); 

      aesKey = keyBytes; 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void encryptAESKey(File publicKeyFile){ 

     try {  
      FileInputStream input = new FileInputStream(publicKeyFile); 

      byte[] decoded = Base64.decode(IOUtils.toByteArray(input));  

      X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(decoded); 
      KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
      PublicKey publicKey = keyFactory.generatePublic(publicSpec); 

      Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); 
      cipher.init(Cipher.ENCRYPT_MODE, publicKey); 

      encryptedAESKey = cipher.doFinal(aesKey); 
      base64AESEncryptedKey = Base64.encode(encryptedAESKey); 

      input.close(); 
     }catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void decryptAESKey(File privateKeyFile){ 

     try { 
      FileInputStream input = new FileInputStream(privateKeyFile); 

      byte[] decoded = Base64.decode(IOUtils.toByteArray(input)); 

      PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded); 
      KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 
      PrivateKey privateKey = keyFactory.generatePrivate(keySpec); 

      Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); 
      cipher.init(Cipher.DECRYPT_MODE, privateKey); 

      aesKeyTransformed = cipher.doFinal(encryptedAESKey); 
      input.close(); 
     }catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

下面是結果:

String: xVwH7Nbz84emVoH0J31sRHC+B669T9wCUVlTDhYgXiI= 

Encrypted string: INTA8rx46hX6bZbDIl4iiWsUGO4ywCW0Aee1reqQ3wR5X7He5ztLHvyZoa0WZmUGYbYwprNGffRI 
OVJFxczMHkxUfHU1WWCTzcfNylD+sWObIYrbyc13aZi9OL/r1GXuaGtkIgTJyqv0QPHfIri7iaH3 
Lr/F4EIcyphJM3E2reQ= 

Decrypted String: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxVwH7Nbz84emVoH0J31sRHC+ 
B669T9wCUVlTDhYgXiI= 
+0

「PKCS8EncodedKeySpec」是解密所需的keyspec?應該是X509吧? – venture

回答

1

在RSA一些數據被編碼成大量並且在計算。 NoPadding(unpadded或教科書RSA)意味着您完全對消息的正確編碼負責。所有的計算都是在大模數下完成的(現在應該至少是2048位)。由於Java採用大端數字,因此您的消息會自動編碼爲最低有效字節,但解密將以相同大小的模數返回解碼消息,因爲無法知道前導零字節是否有意或無意。

爲了使此計算正確和安全有必要應用填充。舊式的PKCS#1 v1.5填充現在不被認爲是安全的,但它只有11個字節的開銷(只有2048/8-11 = 245字節可以用2048位的密鑰加密)。較新的PKCS#1 v2.1填充(OAEP)被認爲是安全的,應該在這裏使用。如果使用SHA-1,它的開銷爲42字節。

「RSA/ECB/NoPadding」參數是強制性的。

這是非常糟糕的,因爲它是非常不安全的:Which attacks are possible against raw/textbook RSA?

如果你不願意簡單地把密碼字符串更改爲Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");,你必須自己刪除前導零。問題當然是這個「零填充」模式是不明確的,如果明文以0x00字節開始,你將無法區分它與填充字節,並且必須將其刪除,從而破壞你的明文。 如果明文與您的情況一樣是AES密鑰,則有0.3%的機會以0x00字節開頭,從而破壞密鑰。您必須確保密鑰實際上是正確的,並且如果長度不正確,則填充零字節。

這裏是你如何刪除前導零個字節:

byte[] unpadZeros(byte[] in) { 
    int i = 0; 
    while(in[i] == 0) i++; 
    return Arrays.copyOfRange(in, i, in.length); 
} 

如果你知道你解密AES密鑰,那麼它可能使去填充沒有產生錯誤的數據:

byte[] unpadZerosToGetAesKey(byte[] in) { 
    int i = 0; 
    while(in[i] == 0) i++; 
    int len = in.length - i; 
    if (len <= 16) len = 16; 
    else if (len <= 24) len = 24; 
    else len = 32; 
    return Arrays.copyOfRange(in, in.length - len, in.length); 
} 
+0

關於這些參數的有趣(或悲傷)的事情是,他們是由我的國家稅務局強加的=)。第二種方法是exacly我需要的,謝謝。 – c00ckiez