关于AES加密、解密的问题记录

业务需求:文件上传至服务器后,对内容进行加密,然后设备端下载该文件,并获取文件的加密秘钥等信息,然后解密文件内容。

服务端: Java实现文件内容的上传、读取、加密;

设备端: C语言实现文件的下载、解密;

        TODO1: 在Java中,使用AES加密算法进行加密文件,对应的模式为AES/ECB/PKCS5Padding,得到加密文件,以及加密秘钥KEY(随机生成);然后使用RSA算法、RSA私钥对秘钥KEY进行加密,并得到Base64秘钥,并使用SHA256算法获取文件内容的hash值并转为hex字符串Hash_Code_Str(防篡改)。

        TODO2: 然后,将密文文件、加密后的秘钥KEY的经过Base64编码的字符串KEY_STR、RSA算法的公钥的Base64编码后的字符串PK_STR文件(publicKey.pem)、文件内容的Hash_Code_Str校验码等,发送给上位机设备端(C语言开发、解析),或由设备端主动获取。

        TODO3: C语言解析时,调用RSA算法、公钥文件(publicKey.pem,内容为Base64编码后的字符串)解密得到AES的私钥KEY,再用AES算法、以及私钥KEY,解密密文文件,最终得到原文,再调用Sha256计算解密得到的文件Hash,与获取的Hash_code_str校验,确认文件内容是否被篡改。

主要是解密文件内容失败,问题主要出在上位机(设备)端,即C语言解析密文文件时:

问题1: C语言中,使用RSA算法解密AES秘钥失败;

原因: 因为AES秘钥是经过RSA加密、并经过Base64编码的字符串,因此需要将秘钥的Base64字符串进行decode得到内容,然后调用RSA算法,根据公钥文件(.pem文件)解析AES秘钥;

问题2: C语言中,调用aes算法解密失败;

原因: 文件内容加密时,使用模式为AES/ECB/PKCS5Padding模式, 而C中的AES算法默认是Nopadding模式,因此在解密文件内容后,出现内容错误

解决方案一: 不使用C中默认的aes解密算法的NoPadding模式,调用openssl的AES算法,支持PSCK5Padding模式;

解决方案二: Java中,调用AES/ECB/NoPadding模式,NoPadding模式下,如果要加密的字节长度不是16的倍数,加密时会出现异常,因此,需要对要加密的文件内容进行填充,然后对填充后的内容进行加密; 解密时,在完成解密后的文件内容,需要去除掉文件的填充数据,再根据Sha256算法计算文件内容的Hash值

解决方案二的代码:

    /**
     * 加密文件内容,并输出值目标文件
     * @param srcFile 源文件
     * @param dstFile 加密后文件
     * @param keys 秘钥,16字节
     */
    public static void encrypt(String srcFile, String dstFile, byte[] keys) {
        try {
            if (keys.length != 16) {
                throw new NoSuchAlgorithmException("秘钥长度错误");
            }
            SecretKeySpec keySpec = new SecretKeySpec(keys, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/Nopadding");
            System.out.println(cipher.getBlockSize());
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);

            byte[] srcData = FileUtil.readBytes(srcFile); // 源文件内容

            byte[] d = new byte[len(srcData.length, cipher.getBlockSize())];
            ArrayUtil.copy(srcData, d, srcData.length);
            if (d.length > srcData.length) {
                // 填充
                Arrays.fill(d, srcData.length, d.length, (byte) cipher.getBlockSize());
            }
            byte[] data = cipher.doFinal(d); // 加密

            OutputStream out = new FileOutputStream(dstFile);
            out.write(data);
            out.flush();
            out.close();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (NoSuchPaddingException e) {
            throw new RuntimeException(e);
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException(e);
        } catch (BadPaddingException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println(SecureUtil.sha256(new File(srcFile)));
        }
    }

    /**
     * 计算原文内容长度,如果不足则需要扩展至字节块长度的倍数
     * @param len 原文长度
     * @param blockSize 字节块长度
     * @return
     */
    public static int len(int len, int blockSize) throws NoSuchAlgorithmException {
        if (len <= 0) {
            throw new NoSuchAlgorithmException("文件内容长度异常");
        }
        if (len % blockSize == 0) {
            return len;
        }
        return (len / blockSize + 1) * blockSize;
    }

问题3: C语言中,使用RSA算法,使用公钥解密得到AES秘钥,又转为Hex字符串,再用AES解密文件内容,此时解密错误;

原因: 解密得到文件秘钥后,将文件秘钥的byte[],直接使用AES算法解密文件内容即可,切记不需要要再次将私钥解码得到的byte[]转为Hex等字符串

PS: C语言中使用AES、RSA等算法时,可以调用OpenSSL中支持的;

PS: 最后,提醒自己,不同环境的加解密相同内容时,提前确认好加解密的算法、模式等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值