2015-10-19 24 views
2

我有一个独立的Java程序,它从Mainframe_JCL触发。 Java程序有一个加密和解密字符串的代码。使用JDK 7使用AES 128时的问题

当我在我的本地运行该程序。加密后,当我解密时,该值是正确的(我得到了我提供的用于加密的字符串)。

但是,当这个由JCL执行这个在大型机上运行..我有越来越奇怪的解密值..种垃圾。

不知道最新的问题。任何帮助,将不胜感激。

我们使用以下JDK: IBM SDK针对z/OS,Java技术版,版本7

下面是用于加密和解密方法:

public static String encrypt(String text) throws UnsupportedEncodingException { 
     byte iv[] = new byte[16]; 
     byte[] encrypted = null; 
     BASE64Encoder enc = new BASE64Encoder(); 
     try {    
      SecureRandom random = new SecureRandom(); 
      random.nextBytes(iv); 
      IvParameterSpec ivspec = new IvParameterSpec(iv); 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      cipher.init(Cipher.ENCRYPT_MODE, getKeySpec(), ivspec); 
      encrypted = cipher.doFinal(text.getBytes()); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
     return URLEncoder.encode(enc.encode(iv)+enc.encode(encrypted), "UTF-8"); 
    } 

    public static String decrypt(String text) { 
     byte iv[] = new byte[16]; 
     String decrypted = ""; 
     byte[] splitText = null; 
     byte[] textToDecrypt = null; 
     BASE64Decoder dec = new BASE64Decoder(); 
     try { 
      text = URLDecoder.decode(text, "UTF-8"); 
      text = text.replaceAll(" ", "+"); 
      splitText = dec.decodeBuffer(text); 
      splitText.toString(); 
      for(int i=0;i<16;i++){ 
       iv[i]=splitText[i]; 
      } 
      textToDecrypt = new byte[splitText.length - 16]; 
      for(int i=16;i<splitText.length;i++){ 
       textToDecrypt[i-16]=splitText[i]; 
      } 
      IvParameterSpec ivspec = new IvParameterSpec(iv); 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      cipher.init(Cipher.DECRYPT_MODE, getKeySpec(), ivspec); 
      decrypted = new String(cipher.doFinal(textToDecrypt)); 
     } catch (UnsupportedEncodingException ex) { 
      ex.printStackTrace(); 
     }catch (Exception e){ 
      e.printStackTrace(); 
     } 
     return decrypted; 
    } 

static SecretKeySpec spec = null; 
public static SecretKeySpec getKeySpec() throws IOException, 
              NoSuchAlgorithmException { 
    if (spec == null) { 
     String keyFile = "aes_key.key"; 
     spec = null; 
     InputStream fis = null; 
     fis = Config.class.getClassLoader().getResourceAsStream(keyFile); 
     byte[] rawkey = new byte[16]; 
     fis.read(rawkey); 
     fis.close(); 
     spec = new SecretKeySpec(rawkey, "AES"); 
} 
return spec; 
} 
+0

你在同一台机器上进行加密和解密吗?你如何得到你的意见?读文件?你使用什么编码? –

+0

是的,我正在同一台机器上进行加密和解密。我从表列值中获取输入。它的一个varchar设置为Java String变量。使用的编码是BASE64Encoder。 – Anand

+0

你能提供一个不起作用的例子吗? – flo

回答

1

你还没有足够的供应信息来运行你的代码(所以我不会解决),但我看到,可能会损坏导致两件事情:

  1. 你,你应该使用编码前的Base64前缀16字节IV;目前你可能混合了IV和密文。
  2. 您应该为toBytesnew String定义字符编码;目前你可能有两个不同的平台字符集来满足。

最后,URL编码基地64可能不是非常有效,你最好只是更换+/字符by URL safe counterparts


于是我就在一个谜:

package nl.owlstead.stackoverflow; 

import static java.nio.charset.StandardCharsets.UTF_8; 

import java.io.IOException; 
import java.io.InputStream; 
import java.security.GeneralSecurityException; 
import java.security.SecureRandom; 
import java.util.Arrays; 
import java.util.Base64; 

import javax.crypto.Cipher; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 

public final class VeryStaticStringEncryption { 

    private static final String KEY_FILE = "aes_key.key"; 
    private static final SecretKey AES_KEY = retrieveSecretKey(KEY_FILE); 

    private VeryStaticStringEncryption() { 
     // avoid instantiation 
    } 

    public static String encrypt(final String text) { 
     try { 
      final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      final int n = cipher.getBlockSize(); 

      final SecureRandom random = new SecureRandom(); 
      final byte[] iv = new byte[n]; 
      random.nextBytes(iv); 
      final IvParameterSpec ivspec = new IvParameterSpec(iv); 

      cipher.init(Cipher.ENCRYPT_MODE, AES_KEY, ivspec); 

      final byte[] plaintext = text.getBytes(UTF_8); 
      byte[] ciphertext = Arrays.copyOf(iv, 
        n + cipher.getOutputSize(plaintext.length)); 
      final int ciphertextSize = cipher.doFinal(
        plaintext, 0, plaintext.length, 
        ciphertext, n); 

      // output size may be bigger, but not likely 
      if (n + ciphertextSize < ciphertext.length) { 
       ciphertext = Arrays.copyOf(ciphertext, n + ciphertextSize); 
      } 

      return Base64.getUrlEncoder().encodeToString(ciphertext); 
     } catch (final GeneralSecurityException e) { 
      throw new IllegalStateException("Could not encrypt string", e); 
     } 
    } 

    public static String decrypt(final String text) { 
     try { 
      // throws IllegalArgumentException if decoding fails 
      final byte[] ciphertext = Base64.getUrlDecoder().decode(text); 

      final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      final int n = cipher.getBlockSize(); 

      // CBC specific 
      if (ciphertext.length < n + n || ciphertext.length % n != 0) { 
       throw new IllegalArgumentException("Ciphertext has incorrect size"); 
      } 

      final IvParameterSpec ivspec = new IvParameterSpec(ciphertext, 0, n); 
      cipher.init(Cipher.DECRYPT_MODE, AES_KEY, ivspec); 
      final byte[] plaintext = cipher.doFinal(ciphertext, n, ciphertext.length - n); 
      return new String(plaintext, UTF_8); 
     } catch (final GeneralSecurityException e) { 
      throw new IllegalStateException("Could not encrypt string", e); 
     } 
    } 

    public static SecretKey retrieveSecretKey(final String keyResource) { 
     try (final InputStream fis = VeryStaticStringEncryption.class.getResourceAsStream(keyResource)) { 
      final byte[] rawkey = new byte[16]; 
      for (int i = 0; i < 16; i++) { 
       final int b = fis.read(); 
       if (b == -1) { 
        throw new IOException("Key is not 16 bytes"); 
       } 
       rawkey[i] = (byte) b; 
      } 

      if (fis.read() != -1) { 
       throw new IOException("Key is not 16 bytes"); 
      } 

      return new SecretKeySpec(rawkey, "AES"); 
     } catch (final IOException e) { 
      // e may contain confidential information 
      throw new IllegalStateException("AES key resource not available"); 
     } 
    } 

    public static void main(String[] args) { 
     String ct = encrypt("owlstead"); 
     System.out.println(ct); 
     String pt = decrypt(ct); 
     System.out.println(pt); 
    } 
} 

请注意,我用了一个包,本地资源,因为我不想弄乱我的SO项目。

重要提示:如果可能发生中间人攻击,CBC加密对于传输模式加密不安全。如果需要,重构为GCM模式。

+0

谢谢马滕。该帖子中缺少getKeySpec()。 – Anand

+0

谢谢马丁。该帖子中缺少方法getKeySpec()。我已经添加了它。基本上,我有一个AES密钥文件和方法getKeySpec()读取此密钥文件并返回SecretKeySpec的一个对象。encrypt()和decrypt()方法将使用这些方法,并将对给定的参数字符串执行加密,解密。代码在我的本地JDK机器上正常工作。但是它不能在跨平台上工作,例如,当传递给不同平台的主机,salesforce等加密字符串时,另一个平台无法解密为实际值。 – Anand

+0

尝试并指定用于编码/解码明文的“StandardCharsets.UTF_8”。这可能不是问题,但是对关键字的单个“读取”不是好的流处理 - 可能返回少于16个字节。如果二进制密钥被系统作为文本处理(例如通过FTP),则复制二进制密钥也会非常棘手。你可以尝试我已经表明的修复吗? –

相关问题