使用AES 128使用JDK 7时出现问题

时间:2015-10-19 12:43:54

标签: java encryption encoding aes

我有一个独立的Java程序,它从Mainframe_JCL触发。 Java程序有一个加密和解密字符串的代码。

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

但是当JCL执行此操作时,这会在大型机上运行..我得到了奇怪的解密值..有点像垃圾。

不确定问题是什么。任何帮助,将不胜感激。

我们正在使用以下JDK: IBM SDK for 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;
}

1 个答案:

答案 0 :(得分:1)

您没有提供足够的信息来运行您的代码(所以我不会修复),但我发现有两件事可能会破坏结果:

  1. 在使用Base64编码之前,应该在16字节IV前面加上前缀;目前你可能正在混合使用IV和密文。
  2. 您应该为toBytesnew String定义字符编码;目前,您可能有两个不同的平台字符集来满足。
  3. 最后,网址编码基础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模式。