2012-01-07 57 views
0

有些东西与我编写的以下AES类进行加密和解密时很有趣。当我拷贝AES对象时,我选择加密纯文本,然后立即尝试解密刚刚加密的文本,但并未完全解密(每次都以不同方式执行)。不正确加密和解​​密

例如我用一个简单的JSP初始化它是这样的:

<%@page import="com.myclass.util.AES"%> 
<% 
     String hexMessage = "0xe800a86d90d2074fbf339aa70b6d0f62f047db15ef04c86b488a1dda3c6c4f2f2bbb444a8c709bbb4c29c7ff1f1e"; 
     String keyText = "12345678abcdefgh";//*/ 

     AES e = new AES(); 
     //e.setKey(keyText); 
     String plaintext = "This should decode & encode!"; 
     String ciphertext = e.encrypt(plaintext); 
     out.println(ciphertext); 
     out.println("<BR>"); 
     out.println(e.decrypt(ciphertext));  
%> 

输出上的每个页面负载变化: 一次:

0x663D64E6A0AE455AB3D25D5AF2F77C72202627EBA068E6DEBE5F22C31 
This should decoÁdìmèåV4ÉkÓ 

另:

0x5F5CF31961505F01EA9D5B7D7BFC656BD3117725D2EA041183F48 
This s2??XêêÈ&ÀܧF?ÒDÒ­? 

等:

0xC7178A34C59F74E5D68F7CE5ED655B670A0B4E715101B4DDC2122460E8 
Tà@¼R×ËÖ?_U?xÎÚ?Ba?b4r!©F 

种我创建的类如下:

package com.myclass.util; 

import java.io.UnsupportedEncodingException; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.NoSuchProviderException; 
import java.security.Security; 
import java.util.regex.Pattern; 
import javax.crypto.*; 
import javax.crypto.spec.*;  

    public class AES { 
     private static String provider = "AES/CTR/NoPadding"; 
     private static String providerkey = "AES"; 
     private static int size = 128; 
     private SecretKeySpec key; 
     private Cipher cipher; 
     private byte[] ivBytes = new byte[size/8]; 
     private IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); 


     public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException{ 
      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
      KeyGenerator kgen = KeyGenerator.getInstance(providerkey); 
      kgen.init(size); // 192 and 256 bits may not be available 
      SecretKey skey = kgen.generateKey(); 
      byte[] raw = skey.getEncoded(); 
      key = new SecretKeySpec(raw, providerkey); 
      cipher = Cipher.getInstance(provider); 
      for(int x = 0; x < (size/8); x++) 
       ivBytes[x] = 00; 
      ivSpec = new IvParameterSpec(ivBytes); 
     } 

     public void setKey(String keyText){ 
      byte[] bText = new byte[size/8]; 
      bText = keyText.getBytes(); 
      key = new SecretKeySpec(bText, providerkey); 
     } 

     public void setIV(String ivText){ 
      setIV(ivText.getBytes()); 
     } 

     public void setIV(byte[] ivByte){ 
      byte[] bText = new byte[size/8]; 
      bText = ivByte; 
      ivBytes = bText; 
      ivSpec = new IvParameterSpec(ivBytes); 
     } 

     public String encrypt(String message) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{ 
      cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); 
      byte[] encrypted = cipher.doFinal(message.getBytes()); 
      return byteArrayToHexString(encrypted); 
     } 
     public String decrypt(String hexCiphertext) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException{ 
      cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); 
      byte[] dec = hexStringToByteArray(hexCiphertext); 
      byte[] decrypted = cipher.doFinal(dec); 
      return new String(decrypted); 
     } 

     private static String byteArrayToHexString(byte [] raw) { 
      String hex = "0x"; 
      String s = new String(raw); 
      for(int x = 0; x < s.length(); x++){ 
       char[] t = s.substring(x, x + 1).toCharArray(); 
       hex += Integer.toHexString((int) t[0]).toUpperCase(); 
      } 
      return hex; 
     } 

     private static byte[] hexStringToByteArray(String hex) { 
      Pattern replace = Pattern.compile("^0x"); 
      String s = replace.matcher(hex).replaceAll(""); 

      byte[] b = new byte[s.length()/2]; 
      for (int i = 0; i < b.length; i++){ 
       int index = i * 2; 
       int v = Integer.parseInt(s.substring(index, index + 2), 16); 
       b[i] = (byte)v; 
      } 
      return b; 
     } 


    } 

基于变化的结果,如果事情是越来越乱了与IV不知何故,我不知道,但我真的不明白为什么...

[编辑]看起来它不是四,如果我解密仍然变化的硬编码。如果我对密钥进行了硬编码,它会停止变化,但仍不能正确解密文本:-(。

--------------------- == =================== ---------------------

添加我创建的最终解决方案根据owlstead的代码和建议,它具有以下功能:
1)在初始化时有一个随机密钥和iv。
2)允许您将密钥或iv指定为常规字符串或十六进制编码的字符串。
3)自动截断或null填补任何给定的关键或四,使其适当的长度。

注意:项目#3可以被视为非常不安全,因为它可以让你做一些愚蠢的事情。为了我的目的,我需要它,但请谨慎使用。如果你为键填充一个短字符串,你的内容不会很安全。

--------------------- ===================== --- ------------------

package com.myclass.util; 

import java.io.UnsupportedEncodingException; 
import java.nio.charset.Charset; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.NoSuchProviderException; 
import java.security.spec.InvalidParameterSpecException; 
import java.util.regex.Pattern; 
import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.KeyGenerator; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 

    public class AES { 
     private static Charset PLAIN_TEXT_ENCODING = Charset.forName("UTF-8"); 
     private static String CIPHER_TRANSFORMATION = "AES/CTR/NoPadding"; 
     private static String KEY_TYPE = "AES"; 
     private static int KEY_SIZE_BITS = 128; 

     private SecretKey key; 
     private Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); 
     private byte[] ivBytes = new byte[KEY_SIZE_BITS/8]; 

    public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidParameterSpecException, InvalidKeyException, InvalidAlgorithmParameterException{ 
     KeyGenerator kgen = KeyGenerator.getInstance(KEY_TYPE); 
     kgen.init(KEY_SIZE_BITS); 
     key = kgen.generateKey(); 
     cipher.init(Cipher.ENCRYPT_MODE, key); 
     ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); 
    } 

    public String getIVAsHex(){ 
     return byteArrayToHexString(ivBytes); 
    } 

    public String getKeyAsHex(){ 
     return byteArrayToHexString(key.getEncoded()); 
    } 

    public void setStringToKey(String keyText){ 
     setKey(keyText.getBytes()); 
    } 

    public void setHexToKey(String hexKey){ 
     setKey(hexStringToByteArray(hexKey)); 
    } 

    private void setKey(byte[] bArray){ 
     byte[] bText = new byte[KEY_SIZE_BITS/8]; 
     int end = Math.min(KEY_SIZE_BITS/8, bArray.length); 
     System.arraycopy(bArray, 0, bText, 0, end); 
     key = new SecretKeySpec(bText, KEY_TYPE); 
    } 

    public void setStringToIV(String ivText){ 
     setIV(ivText.getBytes()); 
    } 

    public void setHexToIV(String hexIV){ 
     setIV(hexStringToByteArray(hexIV)); 
    } 

    private void setIV(byte[] bArray){ 
     byte[] bText = new byte[KEY_SIZE_BITS/8]; 
     int end = Math.min(KEY_SIZE_BITS/8, bArray.length); 
     System.arraycopy(bArray, 0, bText, 0, end); 
     ivBytes = bText; 
    } 

    public String encrypt(String message) throws InvalidKeyException, 
      IllegalBlockSizeException, BadPaddingException, 
      InvalidAlgorithmParameterException { 
     cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivBytes)); 
     byte[] encrypted = cipher.doFinal(message.getBytes(PLAIN_TEXT_ENCODING)); 
     return byteArrayToHexString(encrypted); 
    } 

    public String decrypt(String hexCiphertext) 
      throws IllegalBlockSizeException, BadPaddingException, 
      InvalidKeyException, InvalidAlgorithmParameterException, 
      UnsupportedEncodingException { 
     cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivBytes)); 
     byte[] dec = hexStringToByteArray(hexCiphertext); 
     byte[] decrypted = cipher.doFinal(dec); 
     return new String(decrypted, PLAIN_TEXT_ENCODING); 
    } 

    private static String byteArrayToHexString(byte[] raw) { 
     StringBuilder sb = new StringBuilder(2 + raw.length * 2); 
     sb.append("0x"); 
     for (int i = 0; i < raw.length; i++) { 
      sb.append(String.format("%02X", Integer.valueOf(raw[i] & 0xFF))); 
     } 
     return sb.toString(); 
    } 

    private static byte[] hexStringToByteArray(String hex) { 
     Pattern replace = Pattern.compile("^0x"); 
     String s = replace.matcher(hex).replaceAll(""); 

     byte[] b = new byte[s.length()/2]; 
     for (int i = 0; i < b.length; i++){ 
      int index = i * 2; 
      int v = Integer.parseInt(s.substring(index, index + 2), 16); 
      b[i] = (byte)v; 
     } 
     return b; 
    } 

}

+0

Doug,你可以尝试清理一下这个问题,很多格式和文本都丢失了。如果遇到问题,请参阅stackoverflow的文档。我会在同一时间看一看。 – 2012-01-07 16:58:41

+1

作为一名java程序员,您需要更好地处理对象引用。 setIV()方法显示了很多混淆。 – 2012-01-07 17:04:20

+0

猫头鹰,它看起来像一个友好的管理员来清理它;-)。格雷格,如果你有任何好的资源,我全是耳朵。 – Doug 2012-01-07 17:12:59

回答

3

重写了,有评论在线。有趣的是,最大的错误是产生十六进制数,所以我重写了这种方法。这不是完美的,但我尽可能保持原来的来源。

import java.io.UnsupportedEncodingException; 
import java.nio.charset.Charset; 
import java.security.GeneralSecurityException; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.NoSuchProviderException; 
import java.security.Security; 
import java.util.regex.Pattern; 

import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 

import org.bouncycastle.jce.provider.BouncyCastleProvider; 

/* 
* Add state handling! Don't allow same key/iv for encrypting different cipher text! 
*/ 
public class AES { 

    private static Charset PLAIN_TEXT_ENCODING = Charset.forName("UTF-8"); 
    private static String CIPHER_TRANSFORMATION = "AES/CTR/NoPadding"; 
    private static String KEY_TYPE = "AES"; 
    // 192 and 256 bits may not be available 
    private static int KEY_SIZE_BITS = 128; 

    private Cipher cipher; 
    private SecretKey key; 
    private IvParameterSpec iv; 

    static { 
     // only needed if the platform does not contain CTR encryption by default 
     if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 
      // only needed for some platforms I presume 
      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
     } 
    } 

    public AES() throws NoSuchAlgorithmException, NoSuchPaddingException, 
      NoSuchProviderException { 
     // not much use without a getter 
//  final KeyGenerator kgen = KeyGenerator.getInstance(KEY_TYPE); 
//  kgen.init(KEY_SIZE_BITS); 
//  key = kgen.generateKey(); 
     cipher = Cipher.getInstance(CIPHER_TRANSFORMATION); 
    } 

    public void setKeyHex(String keyText) { 

     byte[] bText = hexStringToByteArray(keyText); 
     if (bText.length * Byte.SIZE != KEY_SIZE_BITS) { 
      throw new IllegalArgumentException(
        "Wrong key size, expecting " + KEY_SIZE_BITS/Byte.SIZE + " bytes in hex"); 
     } 
     key = new SecretKeySpec(bText, KEY_TYPE); 
    } 

    public void setIVHex(String ivText) { 
     byte[] bText = hexStringToByteArray(ivText); 
     if (bText.length != cipher.getBlockSize()) { 
      throw new IllegalArgumentException(
        "Wrong IV size, expecting " + cipher.getBlockSize() + " bytes in hex"); 
     } 
     iv = new IvParameterSpec(bText); 
    } 

    public String encrypt(String message) throws InvalidKeyException, 
      IllegalBlockSizeException, BadPaddingException, 
      InvalidAlgorithmParameterException { 
     cipher.init(Cipher.ENCRYPT_MODE, key, iv); 
     byte[] encrypted = cipher.doFinal(message.getBytes(PLAIN_TEXT_ENCODING)); 
     return byteArrayToHexString(encrypted); 
    } 

    public String decrypt(String hexCiphertext) 
      throws IllegalBlockSizeException, BadPaddingException, 
      InvalidKeyException, InvalidAlgorithmParameterException, 
      UnsupportedEncodingException { 
     cipher.init(Cipher.DECRYPT_MODE, key, iv); 
     byte[] dec = hexStringToByteArray(hexCiphertext); 
     byte[] decrypted = cipher.doFinal(dec); 
     return new String(decrypted, PLAIN_TEXT_ENCODING); 
    } 

    private static String byteArrayToHexString(byte[] raw) { 
     StringBuilder sb = new StringBuilder(2 + raw.length * 2); 
     sb.append("0x"); 
     for (int i = 0; i < raw.length; i++) { 
      sb.append(String.format("%02X", Integer.valueOf(raw[i] & 0xFF))); 
     } 
     return sb.toString(); 
    } 

    // better add some input validation 
    private static byte[] hexStringToByteArray(String hex) { 
     Pattern replace = Pattern.compile("^0x"); 
     String s = replace.matcher(hex).replaceAll(""); 

     byte[] b = new byte[s.length()/2]; 
     for (int i = 0; i < b.length; i++) { 
      int index = i * 2; 
      int v = Integer.parseInt(s.substring(index, index + 2), 16); 
      b[i] = (byte) v; 
     } 
     return b; 
    } 

    public static void main(String[] args) { 
     try { 
      String key = "0x000102030405060708090A0B0C0D0E0F"; 
      String iv = "0x000102030405060708090A0B0C0D0E0F"; 

      String text = "Owlsteads answer"; 
      AES aes = new AES(); 
      aes.setKeyHex(key); 
      aes.setIVHex(iv); 
      String cipherHex = aes.encrypt(text); 
      System.out.println(cipherHex); 
      String deciphered = aes.decrypt(cipherHex); 
      System.out.println(deciphered); 
     } catch (GeneralSecurityException e) { 
      throw new IllegalStateException("Something is rotten in the state of Denmark", e); 
     } catch (UnsupportedEncodingException e) { 
      // not always thrown even if decryption fails, add integrity check such as MAC 
      throw new IllegalStateException("Decryption and/or decoding plain text message failed", e); 
     } 
    } 
} 
+0

每次使用同一个密钥使用CTR加密时,请不要忘记使用不同的IV(NONCE)。使用密文将十六进制(随机)IV存储。如果您想要像以前那样生成随机密钥,请为其创建一个方法,并让它返回密钥的十六进制字符串。你可以为IV做同样的事情。目前使用不同的纯文本调用encrypt()两次是不安全的。 – 2012-01-07 18:00:36

+0

男人,我应该看看十六进制发生器!谢谢你的帮助! – Doug 2012-01-07 18:01:15

+0

感谢关于确保使用不同的IV的说明。我绝对明白这是如此重要。我最担心的是开始时的基础工作,但我会回去添加这些东西。 – Doug 2012-01-07 18:03:47

0

您的byteArrayToHexString方法已损坏。 Integer.toHexString不会添加填充零,但您的代码假定它是。破坏的转换为十六进制会破坏加密的字符串,并且从十六进制转换回原始字节字符串会进一步破坏它。