使用JDK的密码流的加密怪癖(以及如何做)

在我们的日常工作中,我们经常遇到经常性的主题,即将数据(例如文件)从一个位置传输到另一个位置。 这听起来像是一个非常简单的任务,但让我们通过声明这些文件可能包含机密信息并可以通过非安全的通信渠道进行传输这一事实,使其变得更加困难。

首先想到的解决方案之一是使用加密算法。 由于文件可能真的很大,可能是数百兆或数十千兆字节,所以使用像AES这样的对称加密方案可能很有意义。 除了仅加密外,确保数据在传输过程中不被篡改也将是一件很棒的事情。 幸运的是,有一种叫做认证加密的东西,它同时为我们提供了机密性,完整性和真实性保证。 Galois /计数器模式 ( GCM )是最流行的模式之一,支持身份验证加密 ,可以与AES一起使用。 这些想法使我们使用了足够强大的加密方案AES256-GCM128

如果您使用的是JVM平台,则应该感到幸运,因为Java密码体系结构 ( JCA )支持AES和GCM 。 话虽这么说,让我们看看我们能走多远。

我们要做的第一件事是生成一个新的AES256密钥。 与往常一样, OWASP对于正确使用JCA / JCE API 提出了许多建议 。

 final SecureRandom secureRandom = new SecureRandom();          final byte [] key = new byte [ 32 ];  secureRandom.nextBytes(key);  final SecretKey secretKey = new SecretKeySpec(key, "AES" ); 

另外,要初始化AES / GCM密码,我们需要生成随机初始化向量(或简称为IV)。 根据NIST的建议,其长度应为12个字节 (96位)。


对于IV,建议实现将支持范围限制为96位,以提高互操作性,效率和设计的简便性。
针对块密码模式的建议:伽罗瓦/计数器模式(GCM)和GMAC

所以我们在这里:

 final byte [] iv = new byte [ 12 ];  secureRandom.nextBytes(iv); 

准备好AES密钥和IV后,我们可以创建一个密码实例并实际执行加密部分。 处理大文件假定依赖于流,因此我们将BufferedInputStream / BufferedOutputStreamCipherOutputStream结合使用进行加密。

 public static void encrypt(SecretKey secretKey, byte [] iv, final File input, final File output) throws Throwable { final Cipher cipher = Cipher.getInstance( "AES/GCM/NoPadding" ); final GCMParameterSpec parameterSpec = new GCMParameterSpec( 128 , iv); cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); try ( final BufferedInputStream in = new BufferedInputStream( new FileInputStream(input))) { try ( final BufferedOutputStream out = new BufferedOutputStream( new CipherOutputStream( new FileOutputStream(output), cipher))) { int length = 0 ; byte [] bytes = new byte [ 16 * 1024 ]; while ((length = in.read(bytes)) != - 1 ) { out.write(bytes, 0 , length); } } }  } 

请注意,我们如何指定标签大小为128位的 GCM密码参数,并以加密模式对其进行初始化(在处理64Gb以上的文件时要注意一些GCM限制 )。 除了在解密模式下初始化密码之外,解密部分没有什么不同。

 public static void decrypt(SecretKey secretKey, byte [] iv, final File input, final File output) throws Throwable { final Cipher cipher = Cipher.getInstance( "AES/GCM/NoPadding" ); final GCMParameterSpec parameterSpec = new GCMParameterSpec( 128 , iv); cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);         try (BufferedInputStream in = new BufferedInputStream( new CipherInputStream( new FileInputStream(input), cipher))) { try (BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(output))) { int length = 0 ; byte [] bytes = new byte [ 16 * 1024 ];                 while ((length = in.read(bytes)) != - 1 ) { out.write(bytes, 0 , length); } } }  } 

看来我们完成了,对吧? 不幸的是,并不是真的,对小文件进行加密和解密只需要花一点时间,但是处理或多或少的实际数据样本却会产生令人震惊的结果。

处理一个〜42Mb文件通常需要8分钟(您可能会猜到,文件越大,花费的时间就越长),快速分析显示,大部分时间都是在解密数据时花费的(请注意,这绝不是基准,仅是测试)。 在这里 , 这里 , 这里和这里 ,寻找可能的罪魁祸首指出了JCA实现中AES / GCM和CipherInputStream / CipherOutputStream的长期问题清单。

那么还有哪些选择呢? 似乎有可能牺牲CipherInputStream / CipherOutputStream ,重构实现以直接使用密码,并使用JCA原语使加密/解密工作。 但是可以说,引入经过战斗测试的BouncyCastle库是更好的方法。

从实现的角度来看,解决方案看起来几乎是相同的。 确实,尽管命名约定没有改变,但以下代码段中的CipherOutputStream / CipherInputStream来自BouncyCastle 。

 public static void encrypt(SecretKey secretKey, byte [] iv, final File input, final File output) throws Throwable { final GCMBlockCipher cipher = new GCMBlockCipher( new AESEngine()); cipher.init( true , new AEADParameters( new KeyParameter(secretKey.getEncoded()), 128 , iv)); try (BufferedInputStream in = new BufferedInputStream( new FileInputStream(input))) { try (BufferedOutputStream out = new BufferedOutputStream( new CipherOutputStream( new FileOutputStream(output), cipher))) { int length = 0 ; byte [] bytes = new byte [ 16 * 1024 ]; while ((length = in.read(bytes)) != - 1 ) { out.write(bytes, 0 , length); } } }  }  public static void decrypt(SecretKey secretKey, byte [] iv, final File input, final File output) throws Throwable { final GCMBlockCipher cipher = new GCMBlockCipher( new AESEngine()); cipher.init( false , new AEADParameters( new KeyParameter(secretKey.getEncoded()), 128 , iv)); try (BufferedInputStream in = new BufferedInputStream( new CipherInputStream( new FileInputStream(input), cipher))) { try (BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(output))) { int length = 0 ; byte [] bytes = new byte [ 16 * 1024 ];                 while ((length = in.read(bytes)) != - 1 ) { out.write(bytes, 0 , length); } } }  } 

使用BouncyCastle加密原语重新运行之前的加密/解密测试会产生完全不同的画面。

公平地说,JVM平台上的文件加密/解密最初看起来像是一个已解决的问题,但事实证明它充满了令人惊讶的发现。 尽管如此,由于BouncyCastle的存在 , JCA实施的一些缺陷得以有效,简洁地解决。

请在Github上找到完整的资源。

翻译自: https://www.javacodegeeks.com/2020/05/the-crypto-quirks-using-jdks-cipher-streams-and-what-to-do-about-that.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339755.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学java专科_专科学历可以学习java开发吗

学习Java的热潮越来越高涨,除了转行而来的人,很多刚毕业的学生也加入到其中。很多人都觉得学习Java需要有一个高学历作为基础,一些专科生在学习之前会犹豫,他们是否能学习Java,首先学程序开发,入行Java开发…

具有InlfuxDB的Spring Boot和Micrometer第3部分:Servlet和JDBC

在上一个博客中,我们使用由InfluxDB支持的千分尺设置了反应式应用程序。 在本教程中,我们将使用传统的带JDBC阻塞式Servlet的Spring Stack。 我选择的数据库是postgresql。 我将使用与先前博客文章相同的脚本。 因此,我们将拥有初始化数据库…

jsf xhtml调用方法_JSF的工作方式以及调试方法–可以使用polyglot吗?

jsf xhtml调用方法JSF不是我们通常认为的那样。 这也是一个调试起来可能有些棘手的框架,尤其是在初次遇到时。 在这篇文章中,让我们继续探讨为什么会出现这种情况,并提供一些JSF调试技术。 我们将讨论以下主题: JSF不是我们经常想…

将Auth0 OIDC(OAUTH 2)与授权(组和角色)集成

如果您正在使用Auth0对多个现有应用程序中的用户进行身份验证和授权,则可能需要将下一个Web应用程序与Auth0集成。 有多种方法可以执行此操作,例如,如果要将Jenkins与Auth0集成,则可以使用SAML v2;否则,可…

tomee_一罐将其全部统治:Apache TomEE + Shrinkwrap == JavaEE引导

tomee警告:我不是Spring Boot的专家。 我发现很多事情对此非常有趣,并且当然可以真正改善您的日常工作。 而且,我对Spring Boot没有任何反对,也没有开发或使用它的人。 但是我认为社区高估了该产品。 一年前,我开始收…

使用Spring Boot和Project Reactor处理SQS消息

我最近参与了一个项目,在该项目中,我不得不有效地处理通过AWS SQS Queue流入的大量消息。 在这篇文章(可能还有一篇)中,我将介绍使用出色的Project Reactor处理消息的方法。 以下是我要进行的设置: 设置本…

初级测试开发面试题_初级开发人员在编写单元测试时常犯的错误

初级测试开发面试题自从我编写第一个单元测试以来已经有10年了。 从那时起,我不记得我已经编写了成千上万的单元测试。 老实说,我在源代码和测试代码之间没有任何区别。 对我来说是同一回事。 测试代码是源代码的一部分。 在过去的3-4年中,我…

使用SoapUI调用安全WCF SOAP服务–第1部分,该服务

在这个由三部分组成的传奇中,我将演示如何使用SoapUI API工具来调用安全的SOAP服务。 首先,我将专注于创建服务,在接下来的文章中它将充当被测系统。 使用基本身份验证传输安全性机制维护对该服务中资源的访问。 Windows Communication Foun…

java简单系统_Java简单学生管理系统

Java简单学生管理系统这个不需要手动输入,笔记记录//studentpublic class student(){private String id;//学号private String name;//姓名private int age;//年龄public String getId() {return id;}public void setId(String id) {this.id id;}public String get…

kafka java编程demo_Kafka简单客户端编程实例

今天,我们给大家带来一篇如何利用Kafka的API进行客户端编程的文章,这篇文章很简单,就是利用Kafka的API创建一个生产者和消费者,生产者不断向Kafka写入消息,消费者则不断消费Kafka的消息。下面是具体的实例代码。一、创…

java我的世界极限生存_我的世界 1.7.10 极限生存整合包

整合包介绍:最近总有人觉得Minecraft很无聊,没有什么可玩的,或者觉得生存太简单 那么就来试试这个吧,全部是增强怪物的MOD,保证不无聊,保证不简单 基本上没有增加一些新的东西,只增加了几种怪物…

具有InlfuxDB的Spring Boot和Micrometer第1部分:基础项目

对于那些关注此博客的人来说,难怪我倾向于大量使用InfluxDB。 我喜欢这样一个事实,它是一个真正的单一用途的数据库(时间序列),具有许多功能,并且还带有企业支持。 Spring也是我选择的工具之一。 因此&…

PIT,JUnit 5和Gradle –仅需额外的一行配置

在Gradle(带有gradle-pitest-plugin 1.4.7)中发现简单,经过改进的PIT和JUnit 5配置。 不可否认,如今JUnit 5越来越受欢迎。 虽然为JUnit 5提供了一个专用于PIT的插件,并且gradle-pitest-plugin支持了很多年&#xff0…

apache camel_使用WildFly 8在Java EE7中自举Apache Camel

apache camel从Camel版本2.10开始,支持CDI(JSR-299)和DI(JSR-330)。 这为在Java EE容器中以及在独立的Java SE或CDI容器中开发和部署Apache Camel项目提供了新的机会。 是时候尝试一下并熟悉它了。 骆驼到底是什么&am…

Hibernate中保存与持久性以及saveOrUpdate之间的区别

保存与保存或更新与持久保存在Hibernate中 save和saveOrUpdate之间的区别是什么或save和persist之间的区别是任何Hibernate面试中常见的面试问题,就像Hibernate中get和load方法之间的区别一样。 Hibernate Session类提供了几种方法,可以通过诸如save&am…

java中的语句有哪些_java中的循环语句有哪些

Java中有三种主要的循环结构:while 循环do…while 循环for 循环顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,,就需要使用循环结构。一、while循环语法:while( 布尔表达式 ) {     //循环内容   }只要符合布尔表达…

php无法新数据类型,新手入门PHP必知的七种数据类型

想要入门PHP,首先要学会搭建环境,其次是学习基础语法。PHP的基础包括数据类型,运算符,变量和常量等。在这篇文章中,我们主要了解什么是数据类型。数据类型是指同种数据的一个统称,一般会描述为XX数据类型。…

攻防世界web高手进阶php_rce,php_rce 攻防世界xctf web

php_rce首先了解ThinkPHP5.x rec 漏洞分析与复现https://blog.csdn.net/qq_40884727/article/details/101452478var_pathinfo的默认配置为s,我们可以通过$_GET[‘s’]来传参于是构造payloadhttp://111.198.29.45:30600/index.php?sindex/\think\App/invokefunction&functi…

具有InlfuxDB的Spring Boot和Micrometer第2部分:添加InfluxDB

自从我们添加了基本应用程序以来&#xff0c;是时候启动InfluxDB实例了。 我们将按照之前的教程进行操作&#xff0c;并添加一个docker实例。 docker run –rm -p 8086&#xff1a;8086 –name influxdb-本地influxdb 是时候在我们的pom上添加微米InfluxDB依赖项了 < dep…

使用比较器的nulls对具有null值的列表进行排序

你好朋友&#xff0c; 在本教程中&#xff0c;我们将看到如何使用Java 8 Comparator.nullsFirst在列表中的项目很少为空时如何对项目列表进行排序&#xff0c;以便将null视为列表中的最小元素。 –什么是比较器 – nullsFirst方法在Comparator中做什么 –排序具有非空名称的…