【问题标题】:Decryption does not work on Production(Red Hat Enterprise Linux 7) and works on UAT(SUSE based linux distro)解密不适用于生产(Red Hat Enterprise Linux 7)并且适用于 UAT(基于 SUSE 的 linux 发行版)
【发布时间】:2019-11-09 08:47:31
【问题描述】:

我不推荐使用 DES 进行加密/解密,但它是一个旧代码,所以我无法迁移到 AES,现在我的代码在本地环境(即 mac)和生产数据库中运行良好,它也运行良好在基于 SUSE 的 Linux 发行版的 UAT 上,但解密不适用于基于 redhat 的环境的生产。在生产上它抛出“输入长度(带填充)不是 8 字节的倍数”非法块大小异常

@Service
public class EncryptionUtil {

    private static final Logger log = LogManager.getLogger(EncryptionUtil.class);
    @Autowired
    GpsCacheManager gpsCacheManager;
    private Cipher ecipher;
    private Cipher dcipher;
    @Autowired
    private StringUtils stringUtils;

    public EncryptionUtil() throws Exception {
        ecipher = Cipher.getInstance("DES");
        dcipher = Cipher.getInstance("DES");
        initCipher();
    }


    private void initCipher() {
        try {
            String response = “[-3232, -34, -98, 111, -222, 33, -22, 55]”;
            String[] byteValues = response.substring(1, response.length() - 1).split(",");
            byte[] bytes = new byte[byteValues.length];
            for (int i = 0, len = bytes.length; i < len; i++) {
                bytes[i] = Byte.parseByte(byteValues[i].trim());
            }

            SecretKey key = new SecretKeySpec(bytes, "DES");
            ecipher.init(Cipher.ENCRYPT_MODE, key);
            dcipher.init(Cipher.DECRYPT_MODE, key);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    public String encryptUTF8(String str) throws Exception {
        // Encode the string into bytes using utf-8
        byte[] utf8 = str.getBytes("UTF8");

        // Encrypt
        byte[] enc = ecipher.doFinal(utf8);
        // Encode bytes to base64 to get a string
        return new String(Base64.encodeBase64(enc));
    }

    public String decryptUTF8(String str) throws Exception {

        if (stringUtils == null) {
            stringUtils = new StringUtils();
        }
        //do not decrypt if a valid email.
        if (stringUtils.isValidEmail(str)) {
            return str;
        }
        // Decode base64 to get bytes
        byte[] dec = Base64.decodeBase64(str.getBytes());

        byte[] utf8 = null;
        try {
            utf8 = dcipher.doFinal(dec);
        } catch (IllegalBlockSizeException e) {
            return str;
        }
        // Decode using utf-8
        return new String(utf8, "UTF8");
    }
}

【问题讨论】:

  • 当分布完全不同时,UAT 怎么可能是一个精确的副本?这不是人们对“精确复制品”的意思
  • 当你说它在 prod 中不起作用时,你是什么意思?
  • 那么您的输入数据不同(使用错误的键您可能会遇到填充异常)。没有理由为什么这个 Java 代码在不同的环境中会有所不同(除了 Cipher 对象不是线程安全的)。您需要查找并提供更多信息。
  • 这里有一些依赖默认值的加密反模式。下面@Joop Eggen 的答案突出显示了一个。另一个在这里:Cipher.getInstance("DES");。始终将完整的 algorithm/mode/padding 字符串指定为 Cipher.getInstance()
  • 另一个问题是您在decryptUTF8 中的异常处理。你抓住IllegalBlockSizeException 并默默地返回base64 编码的密文。您至少应该记录异常,以便您可以意识到它并修复错误。您也没有提到结果如何“不同”。

标签: java linux encryption redhat des


【解决方案1】:

String.getBytes()new String(byte[]) 存在问题,它们与平台相关,不应在此处使用。同时,我用标准 java 的 Base64 替换了那个 Base64 类,它打算在十年前替换几个 Base64 实现。

public String encryptUTF8(String str) throws Exception {
    // Encode the string into bytes using utf-8
    byte[] utf8 = str.getBytes(StandardCharsets.UTF_8);

    // Encrypt
    byte[] enc = ecipher.doFinal(utf8);
    // Encode bytes to base64 to get a string
    return Base64.getEncoder().encodeToString(enc));
    //Old class: return new String(Base64.encodeBase64(enc), StandardCharsets.US_ASCII);
}

public String decryptUTF8(String str) throws Exception {

     if (stringUtils == null) {
            stringUtils = new StringUtils();
     }
     //do not decrypt if a valid email.
     if (stringUtils.isValidEmail(str)) {
        return str;
     }
     // Decode base64 to get bytes
     //byte[] dec = Base64.getDecoder().decode(str.getBytes(StandardCharsets.US_ASCII));    
     byte[] dec = Base64.getDecoder().decode(str);    
     try {
         byte[] utf8 = dcipher.doFinal(dec);
         // Decode using utf-8
         return new String(utf8, StandardCharsets.UTF_8);
     } catch (IllegalBlockSizeException e) {
         return str;
     }
}

有一个问题:String 用于 Unicode 文本,有两个字节的 chars (UTF-16)。 这意味着任何byte[] 值必须是某种编码的文本,并且该编码转换为字符串。任何任意的byte[] 值都不是有效的字符串。特别是在具有卓越 UTF-8 编码的 Linux 上会损坏数据。

问题可能出在decryptUTF8。如果在原始代码中默认编码是单字节编码,那么一切都被原样吞没。对于 Linux、UTF-8,可能会遇到错误的 UTF-8 多字节序列。 或者编码是 7 位 ASCII。

通常将Stringbyte[] 分开;对非文本二进制数据使用byte[]

【讨论】:

  • 我觉得这不是很有说服力。是否存在默认字符集与 US_ASCII 不兼容的平台?有可能但不太可能,因此不太可能导致错误。
  • 对于 Base64 文本,其字节总是正确的 US_ASCII(除非 EBCDIC)。但是对于任意字节,这是一个问题。我也不完全相信,我会更倾向于return str; 造成严重破坏;任何此类特殊的控制流程。 Cipher 用法似乎是正确的:doFinal 为下一次调用重新初始化。 Red Hat 有特殊的并发优化,但这不应该在这里发挥作用。
  • 我在他的代码中没有看到他在 new String() 构造函数中放置任意字节的任何地方。
  • 如果默认字符集是 UTF-16,这一切都会失败,所以不明确指定字符集是 a 错误,但可能不是 i> 在这种情况下是错误的。虽然它可能是。 OP并没有真正详细说明问题所在。这就是为什么我在 OPs 问题中添加了一些 cmets。
  • j8+ Base64.getDecoder().decode(String) 更简单,并且避免了默认编码的任何问题。 -222 也超出了byte 的范围,他们都应该抛出NumberFormatException OP 没有指出。加上发布的源代码中使用的“智能引用”无法编译。
猜你喜欢
  • 2018-03-27
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 2016-11-30
  • 1970-01-01
  • 2017-06-14
  • 2012-11-03
  • 1970-01-01
相关资源
最近更新 更多