首页 > 解决方案 > 即使输入弹簧长度变化,也产生具有固定长度的 AES 加密输出结果

问题描述

我有一个名为 user.that 的表,它有一列 firstName varchar(25)。

使用 AES 算法插入时,我正在加密名字值。(AES/CBC/PKCS5Padding)

但是每次我加密名字值时,我都没有得到具有相同长度的输出值(加密)。当名字值字符串长度变化时,加密输出长度不适合为列定义的 varchar 长度。

字符串输入=“测试”;//加密值:978fbfa24962827da56b1f00896a4f2cbfc25b744e836c2a22d4292fb16dd53a

String input = "这是测试"; //加密值:978fbfa24962827da56b1f00896a4f2cbfc25b744e836c22a22d4292fb16dd53aa22d4292fb16dd53a

我得到以下异常。

数据截断:数据对于列来说太长

我在这里添加了我的代码。

public String encrypt(String plainText) {

        byte[] cipherBytes = null;

        log.info("Started encryption...");

        if (plainText != null && !plainText.isEmpty()) {
            if (cipher != null && key != null) {
                try {
                    byte[] ivByte = new byte[cipher.getBlockSize()];
                    IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
                    cipher.init(Cipher.ENCRYPT_MODE, key, ivParamsSpec);
                    cipherBytes = cipher.doFinal(plainText.getBytes());
                    plainText = Hex.encodeHexString(cipherBytes);
                    log.info("Completed encryption.");
                    log.info("Encrypted data : "
                            + new String(cipherBytes, "UTF8"));
                } catch (BadPaddingException | IllegalBlockSizeException
                        | InvalidKeyException
                        | InvalidAlgorithmParameterException
                        | UnsupportedEncodingException e) {
                    log.error("Encryption failed : " + e.getMessage());
                    e.printStackTrace();
                    throw new RuntimeException("Encryption failed : "
                            + e.getMessage());
                }
            } else {
                log.error("Encryption failed, cipher, key is null.");
                throw new RuntimeException(
                        "Encryption failed, cipher, key  is null.");
            }
        } else {
            return plainText;
        }
        return plainText;
    }

    public String decrypt(String cipherHexString) throws AuthorizationException {

        log.info("Started decryption...");
        byte[] plainTextBytes = null;
        String resource = "kk";
        String resourceCatagory = "kk tables";
        String accessType = "write";

        try {
            if (decryptionAuthorizer.authorize(resource, resourceCatagory,
                    accessType)) {
                if (cipherHexString != null && !cipherHexString.isEmpty()) {
                    if (cipher != null && key != null) {
                        try {
                            byte[] ivByte = new byte[cipher.getBlockSize()];
                            IvParameterSpec ivParamsSpec = new IvParameterSpec(
                                    ivByte);
                            cipher.init(Cipher.DECRYPT_MODE, key, ivParamsSpec);
                            plainTextBytes = cipher.doFinal(Hex
                                    .decodeHex(cipherHexString.toCharArray()));
                            cipherHexString = new String(plainTextBytes);
                            log.info("Completed decryption.");
                        } catch (InvalidKeyException
                                | InvalidAlgorithmParameterException
                                | IllegalBlockSizeException
                                | BadPaddingException | DecoderException e) {
                            log.error("Decryption failed : " + e.getMessage());
                            e.printStackTrace();
                            throw new RuntimeException("Decryption failed : "
                                    + e.getMessage());

                        }
                    } else {
                        log.error("Decryption failed, cipher, key is null.");
                        throw new RuntimeException(
                                "Decryption failed, cipher, key is null.");
                    }
                } else {
                    return cipherHexString;
                }
            }
        } catch (AuthorizationException e) {
            throw new AuthorizationException(
                    "User not authorized to decrypt  data.");
        }

        return cipherHexString;
    }

    /**
     * This method is used to manually override max key length permission, as an
     * application level solution instead of configuration changes in
     * security.policy.
     */
    private void fixKeyLength() {
        String errorString = "Failed manually overriding key-length permissions.";
        int newMaxKeyLength;

        try {
            newMaxKeyLength = javax.crypto.Cipher.getMaxAllowedKeyLength("AES");
            log.info("Initial max key size for AES : " + newMaxKeyLength);

            if (newMaxKeyLength < 256) {
                Class c = Class
                        .forName("javax.crypto.CryptoAllPermissionCollection");
                Constructor con = c.getDeclaredConstructor();
                con.setAccessible(true);
                Object allPermissionCollection = con.newInstance();
                Field f = c.getDeclaredField("all_allowed");
                f.setAccessible(true);
                f.setBoolean(allPermissionCollection, true);

                c = Class.forName("javax.crypto.CryptoPermissions");
                con = c.getDeclaredConstructor();
                con.setAccessible(true);
                Object allPermissions = con.newInstance();
                f = c.getDeclaredField("perms");
                f.setAccessible(true);
                ((Map) f.get(allPermissions)).put("*", allPermissionCollection);

                c = Class.forName("javax.crypto.JceSecurityManager");
                f = c.getDeclaredField("defaultPolicy");
                f.setAccessible(true);
                Field mf = Field.class.getDeclaredField("modifiers");
                mf.setAccessible(true);
                mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
                f.set(null, allPermissions);

                newMaxKeyLength = Cipher.getMaxAllowedKeyLength("AES");
                log.info("Max key size permission changed, new max key size for AES : "
                        + newMaxKeyLength);
            }
        } catch (Exception e) {
            throw new RuntimeException(errorString, e);
        }
        if (newMaxKeyLength < 256)
            throw new RuntimeException(errorString);
    }

    public String getAlgo() {
        return algo;
    }

    public void setAlgo(String algo) {
        this.algo = algo;
    }

    public String getKeyAlias() {
        return keyAlias;
    }

有没有办法加密产生具有相同长度的输出,即使输入字符串值长度是变化的。所以我不明白

数据截断:数据对于列来说太长

标签: javamysqlencryption

解决方案


很明显,您不会得到相同长度的结果,因为对称加密的结果是加密算法块大小的倍数(在 和 等非流模式的情况下ECBCBC

在您的情况下,您有一个varchar(25),因此最大大小为 50 个字节长(每个varchar为 2 个字节)。然后加密结果最大为 32 字节,因为您使用的AES算法具有 16 字节长的块大小。

如果您必须具有相同的大小,则需要使用流模式,例如CTR. 但我建议将列保持足够长的时间以将加密数据保持在CBC模式中。

无论如何,您的加密结果太长了。我猜(只是猜测,因为没有足够的代码)从数据库中获取字符串后,将其编码为十六进制字符串,然后将其用于加密功能。这就是为什么你会得到一个很长的加密结果:

varchar("test") -> 8 bytes long -> 16 bytes hex string -> 32 bytes after encrypting (with padding) -> 64 bytes hex string
varchar("test is test") -> 24 bytes long -> 48 bytes hex string -> 64 bytes after encrypting (with padding) -> 128 bytes hex string

推荐阅读