简体   繁体   English

使用 JDK 7 使用 AES 128 时的问题

[英]Issue when using AES 128 using JDK 7

I have a stand alone Java program that gets triggered from Mainframe_JCL.我有一个从 Mainframe_JCL 触发的独立 Java 程序。 The Java program has a code to encrypt and decrypt a string. Java 程序有一个代码来加密和解密一个字符串。

When I run the program in my Local.当我在本地运行程序时。 After encryption, when I decrypt, the value is correct ( I am getting the string that I have supplied for encryption).加密后,当我解密时,该值是正确的(我得到了我为加密提供的字符串)。

But when this runs on Mainframe by JCL executing this.. I have getting weird decrypted value.. kind of Junk.但是当它通过 JCL 在大型机上运行时执行这个.. 我得到了奇怪的解密值.. 有点垃圾。

Not sure whats the problem.不知道是什么问题。 Any help would be appreciated.任何帮助,将不胜感激。

We are using following JDK : IBM SDK for z/OS, Java Technology Edition, Version 7我们使用以下 JDK:IBM SDK for z/OS,Java 技术版,版本 7

Below are methods used for encryption and decryption :以下是用于加密和解密的方法:

    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;
}

You haven't supplied enough information to run your code (so I won't fix), but I see two things that may corrupt the result:你没有提供足够的信息来运行你的代码(所以我不会修复),但我看到两件事可能会破坏结果:

  1. You should prefix the 16 byte IV before you encode using Base64;在使用 Base64 编码之前,您应该在 16 字节 IV 前加上前缀; currently you are possibly mixing IV and ciphertext.目前您可能正在混合 IV 和密文。
  2. You should define the character encoding for toBytes and new String ;您应该为toBytesnew String定义字符编码; currently you may have two different platform character sets to content with.目前您可能有两个不同的平台字符集来满足。

Finally, URL encoding base 64 may not be very efficient, you are better off just replacing the + and / characters by URL safe counterparts .最后,URL 编码 base 64 可能不是很有效,你最好只用URL 安全对应物替换+/字符。


So I was in for a puzzle:所以我有一个谜题:

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);
    }
}

Note that I used a package-local resource as I didn't want to clutter up my SO project.请注意,我使用了包本地资源,因为我不想弄乱我的 SO 项目。

IMPORTANT NOTE: CBC encryption is not secure for transport mode encryption if a man-in-the-middle attack is possible.重要说明:如果可能存在中间人攻击,CBC 加密对于传输模式加密是不安全的。 Refactor to GCM mode if required.如果需要,重构为 GCM 模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM