基于Java的256位AES密码加密

Java 256-bit AES Password-Based Encryption

我需要实现256位AES加密,但是我在网上找到的所有示例都使用"keygenerator"来生成256位密钥,但是我想使用自己的密钥。如何创建自己的密钥?我试过将其填充到256位,但后来出现了一个错误,说明密钥太长。我确实安装了无限制权限补丁,所以这不是问题所在:)

也就是说,钥匙发生器看起来像这样…

1
2
3
4
5
6
7
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available

// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();

代码取自此处

编辑

实际上,我把密码填充到了256字节,而不是太长的位。下面是我现在使用的一些代码,我有更多的经验。

1
2
3
4
5
6
7
8
byte[] key = null; // TODO
byte[] input = null; // TODO
byte[] output = null;
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key,"AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
output = cipher.doFinal(input)

你自己需要做的"要做的事":-)

  • 你能澄清一下:打Kgen.init(256)能用吗?
  • 是的,但这会自动生成一个密钥…但是因为我想在两个地方之间加密数据,所以我需要事先知道密钥,所以我需要指定一个而不是"生成"一个。我可以指定一个16位的,适用于128位加密的,可以工作的。我尝试了一个32位的256bit加密,但没有按预期工作。
  • 如果我理解正确,您将尝试使用预先安排的256位密钥,例如,将其指定为字节数组。如果是这样,Darksquid使用SecretKeyspec的方法应该有效。还可以从密码中派生一个AES密钥;如果您需要的是密码,请告诉我,我将向您展示正确的方法;简单地散列密码不是最佳做法。
  • 小心填充数字,可能会降低AES的安全性。
  • @埃里克森:这正是我需要做的(从密码派生一个aes密钥)。
  • @埃里克森:不。我用.NET和Java都编译了我认为是256位的东西,但是结果不同。毕竟,我可能不需要做Java端。作为最后的手段,我们可能会使用bouncycastle:)
  • @日本龙你能让它工作吗?如果是,您是否介意发布完整的解决方案,包括来自aes_v1.html的代码?
  • 对不起,这是两年多以前,我没有做任何Java以来。
  • 这里给出了完整的Java AES 256加密解密实例。


password(a char[]salt(a byte[]和mdash;由SecureRandom选择的8个字节是很好的盐,不需要保密),与接收者共享到带外。然后从这些信息中获得一个好的键:

1
2
3
4
5
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(),"AES");

幻数(可以定义为常量)65536和256分别是密钥派生迭代计数和密钥大小。

密钥派生函数的迭代需要大量的计算工作,从而防止攻击者快速尝试许多不同的密码。迭代计数可以根据可用的计算资源进行更改。

密钥大小可以减少到128位,这仍然被认为是"强"加密,但如果发现攻击会削弱AES,它不会提供很大的安全边际。

使用适当的块链接模式时,可以使用相同的派生密钥加密许多消息。在密码块链接(CBC)中,为每个消息生成一个随机初始化向量(IV),即使纯文本相同,也会产生不同的密码文本。CBC可能不是您可以使用的最安全的模式(请参阅下面的AEAD);有许多具有不同安全属性的其他模式,但它们都使用类似的随机输入。在任何情况下,每个加密操作的输出都是密码文本和初始化向量:

1
2
3
4
5
6
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));

存储ciphertextiv。解密时,使用具有相同salt和迭代参数的密码以完全相同的方式重新生成SecretKey。用此密钥初始化密码,并用消息存储初始化向量:

1
2
3
4
5
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext),"UTF-8");
System.out.println(plaintext);

Java 7包含了对AEAD密码模式的API支持,包括OpenJDK和Oracle发行版的"SunJCE"提供者实现了Java 8的这些启动。强烈建议使用其中一种模式来代替CBC;它将保护数据的完整性和隐私。

带有"非法密钥大小或默认参数"消息的java.security.InvalidKeyException表示加密强度有限;无限制强度的权限策略文件不在正确的位置。在JDK中,它们应该放在${jdk}/jre/lib/security下。

根据问题描述,似乎没有正确安装策略文件。系统可以轻松地拥有多个Java运行时;仔细检查以确保使用正确的位置。

  • 哎呀。对称加密不需要盐。IVS有着类似的用途,大多数(全部?)都是预先准备好的密文。加密库。
  • @尼克:读一下pkcs 5。对于pbkdf2,salts是必需的,这就是基于密码加密的API要求它们作为密钥派生的输入的原因。如果没有salts,就可以使用字典攻击,从而可以预先计算出最可能的对称加密密钥列表。密码IVS和密钥衍生盐有不同的用途。IVS允许一个对多个消息重复使用同一个密钥。盐可以防止字典对密钥的攻击。
  • 使用securekey和pbekeyspec是否生成符合rfc2898的基于密码的密钥?ietf.org/rfc/rfc2898.txt文件
  • 是的,这是secretkeyFactory算法名称的"pbkdf2"。它指的是pkcs 5的pbkdf2(基于密码的密钥派生函数2)。
  • 如果您使用上述方法将加密数据存储在数据库中,您会将密文(blob)和iv(char)存储在数据库中吗?密码和盐将由用户或应用程序的配置文件提供给客户机应用程序?
  • 在这种情况下,我将在一个字段中存储密文,在另一个字段中存储IV,在第三个字段中存储"salt"和"iterations"。我会在客户机应用程序中提示用户输入密码,并使用存储的salt和迭代来派生密钥。然后用派生密钥初始化密码并存储IV,然后解密内容。
  • @埃里克森:如果您在数据库中存储IV、salt等,为什么不直接使用"pbewithmd5anddes"并将salt附加到加密文本中,在加载后,您可以将salt剥离并用于解密。通过这种方式,PBE在jasipt框架中实现:www.jasipt.org
  • 首先,这将是DES加密,而不是AES。大多数供应商不支持PBEwithand算法;例如,SUNJCE不提供AES,PBE也不提供AES。其次,启用Jasipt是一个非目标。一个声称在不需要理解基本原则的情况下提供安全保障的方案,表面上看起来是危险的。
  • @埃里克森,那么缺乏支持是唯一不使用"pbewithmd5anddes"的标准吗?有没有其他标准不使用这个算法加密数据库中存储的数据?
  • 虽然MD5在某些应用程序中是"坏的",但我不认为这是一个问题。但DES的弱点绝对是个问题。使用价值几千美元的设备,可以在数小时或数天内破解DES的小钥匙。
  • 我已经实现了@erickson's answer作为一个类:github.com/mrclay/jsecuredit/tree/master/src/org/mrclay/cry‌&8203;pop(pbe完成工作,pbestorage是一个将iv/ciphertext存储在一起的值对象。)
  • 尝试使用下面的Doug代码实现此功能。埃里克森说"与接收者共享密码(char[])和salt(byte[]-8)",然后"将密文和iv发送给接收者"是什么?静脉注射还是盐?它们是不同的,对吗?
  • @它们是不同的,你和他们一样。在设置密码的同时,您可能会共享salt(用于密钥派生)。但是在那之后发送的每一条消息都应该包括用于加密该消息的唯一的IV(并且只包括该消息)。
  • @顺便说一下,道格的代码有很多安全缺陷。我不能以身作则。
  • @埃里克森,我只是在数据库中存储哈希/加密密码。您的示例是否可以用于安全存储密码,而不是使用密码和salt?
  • @andynuss这个例子是用于可逆加密的,通常不应该用于密码。您可以使用pbkdf2密钥派生安全地"散列"密码。这意味着在上面的示例中,您将把tmp.getEncoded()的结果存储为散列值。您还应该存储salt和迭代(本例中为65536),以便在有人试图进行身份验证时重新计算哈希。在这种情况下,每次更改密码时,使用加密随机数生成器生成salt。
  • 要运行此代码,请确保您在JRE中拥有如ngs.ac.uk/tools/jcepolicyfiles中所述的无限制强度权限策略文件。
  • 有没有一种方法可以做到这一点,没有无限的权限权限政策文件?
  • @ ArchimedesTrajano没有Java加密体系结构。我建议使用AES-128。AES-128很好。NIST建议在2030年之前使用。如果您真的需要AES-256并且无法通过JCA,您可以使用非标准API,比如BouncyCastle的"轻量级"加密API来直接绕过这些限制。我不能解释这样做的法律后果(您的软件可能非法导入某些区域)。
  • +1但是,如果您按照答案中的暗示通过网络发送加密数据,则必须确保消息的完整性。换句话说,您需要生成另一个密钥来使用一个MAC,或者您可以使用一个经过身份验证的加密模式,如GCM。
  • 是的,我是在API在Java SE中进行GCM之前编写的,但是它们是在Java 7中引入的,而在标准的SunjCE提供程序中的实现则在Java 8中添加。因此,如果在Java 7中拥有正确的提供者,或者使用Java 8,则强烈建议使用GCM来避免MyX-MalLink的密码末日原理。
  • 另外请注意,如果每次明文加密时salt都是完全随机的,则可以跳过随机iv的创建(因此发送/通信)。只有在每次加密密钥不变时才需要唯一/随机iv,但使用随机salt将导致每次加密的密钥不同。
  • 是的,我假设在选择密码时salt和rounds是固定的,并且存储在某个地方作为密钥派生参数。
  • 在阅读了所有这些内容之后,我有几个问题:1)您必须使用params.getParameterSpec(IvParameterSpec.class).getIV()还是可以直接调用'cipher.getiv();'?2)假设我正在尝试实现简单的聊天应用程序,我想设置一次Passsword,然后两个人就可以相互通信了。IV是否只生成一次,然后用于加密每个消息,或者是否必须为每个消息生成它(如果是,那么如何生成)?
  • @伏尔塔是的,你可以打电话给getIV()。对于聊天应用程序,每条消息都有不同的IV。消息的构成取决于您如何编写代码。如果您创建一个CipherOutputStream并在聊天会话期间使用它加密套接字的OutputStream,那么只有一个iv。如果您将单个聊天发布到Web应用程序,那么每个聊天都有一个唯一的iv。基本上,在调用Cipher上的doFinal()后,应该生成一个新的iv。
  • 谢谢你这么快的回答!所以每次调用getiv()时,都会返回新的向量(根据docs)?
  • @Voltar No.每次调用Cipher上的init()方法时,都会生成一个新的IV。您可以在不再次调用init()的情况下重用Cipher实例,但它的iv不会改变,这是一个问题。getIV()文档没有说明每次调用都会选择新的IV。也许你被"新缓冲区"的措辞弄糊涂了;这仅仅意味着当前的IV被复制到一个新的数组中,而这个数组在其他任何地方都没有被引用。
  • 什么意思是钥匙出带。
  • @Doughauf意味着你必须事先共享密钥,通过一些安全的方法;它不能通过不安全的通道发送。
  • 从Android4.4Kitkat开始,我们对SecretKeyFactory的行为进行了细微的改变。此更改可能会破坏某些使用对称加密的应用程序
  • @尼克健你在说这个吗?
  • 是的,我也试图发布链接,但反垃圾邮件阻止了我这样做。android开发者.blogspot.ca/2013/12/…
  • 加密代码是否符合此openssl命令?openssl enc-aes-256-cbc-a-in t.txt-k测试通过
  • @默认情况下,openssl使用非标准、低质量的密钥派生算法而不是pbkdf2,如我上面所示。但是,我编写了一些Java代码来解密用OpenSSL加密的消息。对于AES-256,您需要调整派生密钥的长度(密钥需要32个字节,IV需要16个字节),并将转换更改为"AES/CBC/PKCS5Padding"
  • 感谢您提供的信息,我遇到了麻烦,我应该重新创建确切的openssl行为来与外部系统交互。因此,我在寻找OpenSSL命令的Java等价物:StdPox.com /问题/ 32508961 / & Helip;
  • 警告:发送cbc ciphertext会导致填充Oracle攻击以及使纯文本易受更改的攻击。要使用aes-256发送密文,请使用带有aes-256密码套件的tls,可能使用带有派生密钥的psk。
  • @马尔滕博德韦斯很有道理。在这方面,我重新措辞了答案,并为AEAD提供了建议。时间允许的话,我举个例子。
  • 在Java6和Java 7中不支持PBKDF2HMCASH256
  • 以上生成密钥的代码不正确,因为它使用基于PBE的加密密钥"pbkdf2withhmacsha256"生成密钥。然后它将生成的密钥与"aes"加密算法关联起来。那是错误的!用于生成密钥的算法必须与secretkey中关联的算法相同。
  • @用户2213684您错了。PBKDF算法是从任何对称算法的密码派生密钥的正确方法。将AES指定为算法并使用PBEKeySpec将失败。当你没有任何经验或知识的时候,你为什么要发表这个评论?
  • @内里读了整篇文章。讨论了该准确错误消息的解决方案。
  • @我见过埃里克森,但没能解决。谢谢,让我用128号就可以了。
  • @Neri您必须确保正确的文件安装在正确的位置。为什么在您的案例中没有发生这种情况,需要在一个新问题中进行更详细的故障排除。但你是对的,128提供了充足的安全保障。256只是额外的保险,以防有一天在AES中发现一些弱点;它提供了可能永远不需要的安全边际。
  • 有用,但缺少包装/导入?网站/新(完整)的好地方示例@erickson?我认为是这样。
  • @埃里克森感谢这一伟大的概述。在另一个问题中,您建议某人使用加密库。使用图书馆有什么好处?我个人希望避免使用我无法(安全地)验证的大量代码。如果正确实现,上述解决方案(256位)在保存密码/银行数据时是否会提供适当的保护?
  • Neri是否安装了Java安装中的匹配策略文件?有不同的……对于Java7,您将需要这些Oracle、COM/TeaTeWorks/Java/JavaSe/下载/Helip;如果运行Java8,则需要Java8的包。将它们插入到我的Mac上的JAVA_HOME/jre/lib/security中,路径看起来像/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/H‌​ome/jre/lib/security‌​/INSERT HERE
  • @ MartinPfeffer是的,在一个很好的Java密码库的基础上,基于密码的加密,我希望找到基本上相同的代码,正如我上面写的。密码管理器(如passwordsafe)通常使用相同的方法。不过,我强烈建议采用AEAD模式,我提到过,但没有说明。你可以再问一个问题。
  • 对于这个示例,openssl命令行是否等同于解密密文?我绝对不是加密人……
  • @Jointefort没有。openssl主要是一个用于其他应用程序的库;它的cli工具,特别是enc命令,更多的是事后才想到的。主要的问题是,它的pbkdf2(将密码转换为密钥的算法)支持没有通过命令行接口公开。


考虑使用Spring安全加密模块

The Spring Security Crypto module provides support for symmetric encryption, key generation, and password encoding. The code is distributed as part of the core module but has no dependencies on any other Spring Security (or Spring) code.

它为加密提供了一个简单的抽象,并且似乎符合这里的要求,

The"standard" encryption method is 256-bit AES using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2). This method requires Java 6. The password used to generate the SecretKey should be kept in a secure place and not be shared. The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised. A 16-byte random initialization vector is also applied so each encrypted message is unique.

通过对内部结构的观察,我们发现了一个类似于埃里克森答案的结构。

正如在该问题中所指出的,这也需要Java加密扩展(JCE)无限强度管辖权策略(否则您将遇到EDCOX1 OR 2)。它可以下载Java 6、Java 7和Java 8。

示例用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;

public class CryptoExample {
    public static void main(String[] args) {
        final String password ="I AM SHERLOCKED";  
        final String salt=KeyGenerators.string().generateKey();

        TextEncryptor encryptor = Encryptors.text(password, salt);      
        System.out.println("Salt: "" + salt +""");

        String textToEncrypt ="*royal secrets*";
        System.out.println("Original text: "" + textToEncrypt +""");

        String encryptedText = encryptor.encrypt(textToEncrypt);
        System.out.println("Encrypted text: "" + encryptedText +""");

        // Could reuse encryptor but wanted to show reconstructing TextEncryptor
        TextEncryptor decryptor = Encryptors.text(password, salt);
        String decryptedText = decryptor.decrypt(encryptedText);
        System.out.println("Decrypted text: "" + decryptedText +""");

        if(textToEncrypt.equals(decryptedText)) {
            System.out.println("Success: decrypted text matches");
        } else {
            System.out.println("Failed: decrypted text does not match");
        }      
    }
}

以及样本输出,

1
2
3
4
5
Salt:"feacbc02a3a697b0"
Original text:"*royal secrets*"
Encrypted text:"7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a"
Decrypted text:"*royal secrets*"
Success: decrypted text matches

  • 你可以使用所有的弹簧加载的模块,没有是吗?他们不似乎有可用的下载JAR文件制造。
  • "theglauber是的,你可以使用安全模块没有弹簧或弹簧框架。在看《POM,唯一的依赖是测井运行Apache Commons 1.1.1。你可以拉一个Maven在罐或直接从官方下载的二进制(二进制回购湖春4下载更多的信息,在跳的二进制文件)。
  • 看起来像一个接口,在最新的版本吗?(3.2.5)
  • "blankman嗯,一把跳安全密码3.2.5.release executes作为例子,和安静的海滩。什么是你看到这是意外吗?
  • 对不起,它没有工作,我不是编译测试项目的其他原因。我很抱歉谢谢。
  • 知道这个错误吗?"由于密钥无效,无法初始化"我已经在"jre/lib/security"中包含了所需的依赖项"。
  • @技术官僚,我没见过。您是否有"由"异常以及其他详细信息?另请参阅如何解决可能有一些想法的InvalidKeyException。
  • 是否可以将密钥长度设置为128位?在每台电脑中修改安全文件夹对我来说不是一个选项。
  • @伊凡夫对不起,看起来不像。256在源中是硬编码的
  • Spring实用程序使用的NULL_IV_GENERATOR不安全。如果应用程序不提供IV,则让提供程序选择它,并在初始化后查询它。


在阅读了Erickson的建议,并从其他几篇文章和这里的这个示例中收集到我可以做的事情之后,我尝试用推荐的更改来更新Doug的代码。请随意编辑以使其更好。

  • 初始化向量不再固定
  • 加密密钥是使用来自Erickson的代码派生的
  • 使用secureRandom()在setupEncrypt()中生成8字节salt
  • 解密密钥由加密盐和密码生成
  • 解密密码由解密密钥和初始化向量生成
  • 删除了十六进制旋转代替了org.apache.commons编解码器十六进制例程

一些注释:这使用128位加密密钥- Java显然不会做256位加密的方块。实现256需要在Java安装目录中安装一些额外文件。

另外,我不是加密人。注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class Crypto
{
    String mPassword = null;
    public final static int SALT_LEN = 8;
    byte [] mInitVec = null;
    byte [] mSalt=null;
    Cipher mEcipher = null;
    Cipher mDecipher = null;
    private final int KEYLEN_BITS = 128; // see notes below where this is used.
    private final int ITERATIONS = 65536;
    private final int MAX_FILE_BUF = 1024;

    /**
     * create an object with just the passphrase from the user. Don't do anything else yet
     * @param password
     */

    public Crypto (String password)
    {
        mPassword = password;
    }

    /**
     * return the generated salt for this object
     * @return
     */

    public byte [] getSalt ()
    {
        return (mSalt);
    }

    /**
     * return the initialization vector created from setupEncryption
     * @return
     */

    public byte [] getInitVec ()
    {
        return (mInitVec);
    }

    /**
     * debug/print messages
     * @param msg
     */

    private void Db (String msg)
    {
        System.out.println ("** Crypt **" + msg);
    }

    /**
     * this must be called after creating the initial Crypto object. It creates a salt of SALT_LEN bytes
     * and generates the salt bytes using secureRandom().  The encryption secret key is created
     * along with the initialization vectory. The member variable mEcipher is created to be used
     * by the class later on when either creating a CipherOutputStream, or encrypting a buffer
     * to be written to disk.
     *  
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidParameterSpecException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     */

    public void setupEncrypt () throws NoSuchAlgorithmException,
                                                           InvalidKeySpecException,
                                                           NoSuchPaddingException,
                                                           InvalidParameterSpecException,
                                                           IllegalBlockSizeException,
                                                           BadPaddingException,
                                                           UnsupportedEncodingException,
                                                           InvalidKeyException
    {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;

        // crate secureRandom salt and store  as member var for later use
         mSalt=new byte [SALT_LEN];
        SecureRandom rnd = new SecureRandom ();
        rnd.nextBytes (mSalt);
        Db ("generated salt :" + Hex.encodeHexString (mSalt));

        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        /* Derive the key, given password and salt.
         *
         * in order to do 256 bit crypto, you have to muck with the files for Java's"unlimted security"
         * The end user must also install them (not compiled in) so beware.
         * see here:  http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
         */

        KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
        tmp = factory.generateSecret (spec);
        SecretKey secret = new SecretKeySpec (tmp.getEncoded(),"AES");

        /* Create the Encryption cipher object and store as a member variable
         */

        mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
        mEcipher.init (Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = mEcipher.getParameters ();

        // get the initialization vectory and store as member var
        mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV();

        Db ("mInitVec is :" + Hex.encodeHexString (mInitVec));
    }



    /**
     * If a file is being decrypted, we need to know the pasword, the salt and the initialization vector (iv).
     * We have the password from initializing the class. pass the iv and salt here which is
     * obtained when encrypting the file initially.
     *  
     * @param initvec
     * @param salt
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws DecoderException
     */

    public void setupDecrypt (String initvec, String salt) throws NoSuchAlgorithmException,
                                                                                       InvalidKeySpecException,
                                                                                       NoSuchPaddingException,
                                                                                       InvalidKeyException,
                                                                                       InvalidAlgorithmParameterException,
                                                                                       DecoderException
    {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;
        SecretKey secret = null;

        // since we pass it as a string of input, convert to a actual byte buffer here
        mSalt=Hex.decodeHex (salt.toCharArray ());
       Db ("got salt" + Hex.encodeHexString (mSalt));

        // get initialization vector from passed string
        mInitVec = Hex.decodeHex (initvec.toCharArray ());
        Db ("got initvector :" + Hex.encodeHexString (mInitVec));


        /* Derive the key, given password and salt. */
        // in order to do 256 bit crypto, you have to muck with the files for Java's"unlimted security"
        // The end user must also install them (not compiled in) so beware.
        // see here:
      // http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);

        tmp = factory.generateSecret(spec);
        secret = new SecretKeySpec(tmp.getEncoded(),"AES");

        /* Decrypt the message, given derived key and initialization vector. */
        mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
    }


    /**
     * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
     * Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
     *
     * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
     * into uncertain problems with that.
     *  
     * @param input - the cleartext file to be encrypted
     * @param output - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */

    public void WriteEncryptedFile (File input, File output) throws
                                                                                          IOException,
                                                                                          IllegalBlockSizeException,
                                                                                          BadPaddingException
    {
        FileInputStream fin;
        FileOutputStream fout;
        long totalread = 0;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        fout = new FileOutputStream (output);
        fin = new FileInputStream (input);

        while ((nread = fin.read (inbuf)) > 0 )
        {
            Db ("read" + nread +" bytes");
            totalread += nread;

            // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
            // and results in full blocks of MAX_FILE_BUF being written.
            byte [] trimbuf = new byte [nread];
            for (int i = 0; i < nread; i++)
                trimbuf[i] = inbuf[i];

            // encrypt the buffer using the cipher obtained previosly
            byte [] tmp = mEcipher.update (trimbuf);

            // I don't think this should happen, but just in case..
            if (tmp != null)
                fout.write (tmp);
        }

        // finalize the encryption since we've done it in blocks of MAX_FILE_BUF
        byte [] finalbuf = mEcipher.doFinal ();
        if (finalbuf != null)
            fout.write (finalbuf);

        fout.flush();
        fin.close();
        fout.close();

        Db ("wrote" + totalread +" encrypted bytes");
    }


    /**
     * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
     * to disk as (output) File.
     *
     * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
     *  and still have a correctly decrypted file in the end. Seems to work so left it in.
     *  
     * @param input - File object representing encrypted data on disk
     * @param output - File object of cleartext data to write out after decrypting
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */

    public void ReadEncryptedFile (File input, File output) throws
                                                                                                                                            IllegalBlockSizeException,
                                                                                                                                            BadPaddingException,
                                                                                                                                            IOException
    {
        FileInputStream fin;
        FileOutputStream fout;
        CipherInputStream cin;
        long totalread = 0;
        int nread = 0;
        byte [] inbuf = new byte [MAX_FILE_BUF];

        fout = new FileOutputStream (output);
        fin = new FileInputStream (input);

        // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
        cin = new CipherInputStream (fin, mDecipher);

        while ((nread = cin.read (inbuf)) > 0 )
        {
            Db ("read" + nread +" bytes");
            totalread += nread;

            // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
            byte [] trimbuf = new byte [nread];
            for (int i = 0; i < nread; i++)
                trimbuf[i] = inbuf[i];

            // write out the size-adjusted buffer
            fout.write (trimbuf);
        }

        fout.flush();
        cin.close();
        fin.close ();      
        fout.close();  

        Db ("wrote" + totalread +" encrypted bytes");
    }


    /**
     * adding main() for usage demonstration. With member vars, some of the locals would not be needed
     */

    public static void main(String [] args)
    {

        // create the input.txt file in the current directory before continuing
        File input = new File ("input.txt");
        File eoutput = new File ("encrypted.aes");
        File doutput = new File ("decrypted.txt");
        String iv = null;
        String salt=null;
        Crypto en = new Crypto ("mypassword");

        /*
         * setup encryption cipher using password. print out iv and salt
         */

        try
      {
          en.setupEncrypt ();
          iv = Hex.encodeHexString (en.getInitVec ()).toUpperCase ();
          salt=Hex.encodeHexString (en.getSalt ()).toUpperCase ();
      }
      catch (InvalidKeyException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchAlgorithmException e)
      {
          e.printStackTrace();
      }
      catch (InvalidKeySpecException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchPaddingException e)
      {
          e.printStackTrace();
      }
      catch (InvalidParameterSpecException e)
      {
          e.printStackTrace();
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (UnsupportedEncodingException e)
      {
          e.printStackTrace();
      }

        /*
         * write out encrypted file
         */

        try
      {
          en.WriteEncryptedFile (input, eoutput);
          System.out.printf ("File encrypted to" + eoutput.getName () +"
iv:"
+ iv +"
salt:"
+ salt +"

"
);
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (IOException e)
      {
          e.printStackTrace();
      }


        /*
         * decrypt file
         */

        Crypto dc = new Crypto ("mypassword");
        try
      {
          dc.setupDecrypt (iv, salt);
      }
      catch (InvalidKeyException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchAlgorithmException e)
      {
          e.printStackTrace();
      }
      catch (InvalidKeySpecException e)
      {
          e.printStackTrace();
      }
      catch (NoSuchPaddingException e)
      {
          e.printStackTrace();
      }
      catch (InvalidAlgorithmParameterException e)
      {
          e.printStackTrace();
      }
      catch (DecoderException e)
      {
          e.printStackTrace();
      }

        /*
         * write out decrypted file
         */

        try
      {
          dc.ReadEncryptedFile (eoutput, doutput);
          System.out.println ("decryption finished to" + doutput.getName ());
      }
      catch (IllegalBlockSizeException e)
      {
          e.printStackTrace();
      }
      catch (BadPaddingException e)
      {
          e.printStackTrace();
      }
      catch (IOException e)
      {
          e.printStackTrace();
      }
   }


}

  • 这基本上与埃里克森的答案相同,被一个——在我看来不是很好编程的——包装器包围着。
  • @猫头鹰-这是一个很好的答案。它演示了如何通过加密字节缓冲区来加密流,而不是将所有内容都保存在内存中。埃里克森的答案不适用于大文件,这不适合内存。所以+1到五福。:)
  • "dynamokaj使用CipherInputStreamCipherOutputStream是不多的一个问题。下表shuffling所有的例外是一个问题。事实上,这已经成为一场突然的和盐,这是一个问题,四是必需的。事实上,它不遵循Java编码公约是一个问题。这和事实,只在文件而不是T所寻求的是一个问题。和其余的代码,这是一份不多的帮助他们。但也许我会调整它,使它更好,他建议……
  • "我同意owlstead编码可以得到更好的是,它是第一个截止到1 / 4或什么,但我喜欢他介绍我到cipherinputstream和cipheroutputstream,那只是因为我需要它!);
  • 为什么两次?fout.close fout.close();();
  • 我创建了一个密码加密的流密码的数据完整性检查和故障jjybdx4il几乎和压缩:github.com / /其他/树/硕士/加密工具添加更多的是受欢迎的。
  • marianpa @?dzioch好抓。它是awhile自从我张贴这一个,但它是一surmise排版。有没有一个电话fout()逻辑的理由。


我在一个非常简单的课堂上实现了埃里克森的答案:Java AES 256位加密/解密类如果您获得EDCOX1 0,则必须安装Java加密扩展(JCE)无限强度管辖权策略文件:

  • Java 6链接
  • Java 7链接

把罐子放在你的电脑里。

  • 你的出现是共享相同的密钥值的实例之间的所有固定的盐。这是可能不是一个好主意。
  • 它的精细。然而,如果我在一个不同的实例使用的decrypting aesencrypter decrypted文件,有一些问题,在飞机的第一个字节(字节)。使用密码加密的文件I A I A,然后试图解密使用相同的密码。文件的所有内容,除了第一个decrypted 9字节的时间。
  • 可能工作精细,但WEP加密。盐是固定到您的Wi-Fi路由器。
  • 周杰伦与密码学",只是一个工作的执行是不一样的作为会议的需求。再次,这是一个包装类在埃里克森的解决方案(虽然它似乎在第一行代码的静态barring视线,盐)。请注意,不应该是唯一的答案,没有链接的过程,是他们的解释。
  • 1,本应该是一个如何在埃里克森的回答。有没有需要有一个单独的回答只是点出你已经实现,其他人的代码。
  • 这是一个潜在的危险的安全答案:A是硬和试图实现自己动手没有适当的同行评审和测试是危险的。请不要使用这个代码在任何应用程序中使用的安全是重要的,标准,测试库!
  • 本建议不跟随我的答案。


从字节数组生成自己的密钥很容易:

1
2
byte[] raw = ...; // 32 bytes in size for a 256 bit key
Key skey = new javax.crypto.spec.SecretKeySpec(raw,"AES");

但是创建一个256位的密钥是不够的。如果密钥生成器不能为您生成256位密钥,那么Cipher类可能也不支持AES 256位。你说你已经安装了无限制权限补丁,所以应该支持AES-256密码(但是256位密钥也应该是,所以这可能是配置问题)。

1
2
3
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());

缺少AES-256支持的一个解决方法是采用一些免费可用的AES-256实现,并将其用作自定义提供程序。这包括创建自己的Provider子类,并将其与Cipher.getInstance(String, Provider)一起使用。但这可能是一个复杂的过程。

  • 您应该始终指示模式和填充算法。Java默认使用不安全的ECB模式。
  • 你不能创建你自己的提供者,提供者必须被签署(不能相信我最初读过这个错误)。即使可以,密钥大小的限制也在Cipher的实现中,而不是在提供者本身中。您可以在Java 8和更低版本中使用AES-256,但需要使用专用API。或者运行时不会对密钥大小造成限制。
  • OpenJDK(和Android)的最新版本对添加您自己的安全/加密提供程序没有限制。当然,这样做的风险由你自己承担。如果您忘记保持库的最新状态,那么您可能会面临安全风险。


我过去所做的是通过类似sha256的方法散列密钥,然后将散列中的字节提取到密钥字节[]。

在您有了字节[]之后,您可以简单地执行以下操作:

1
2
3
4
SecretKeySpec key = new SecretKeySpec(keyBytes,"AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());

  • 对于其他人:这不是一个非常安全的方法。您应该使用pkdcs 5中指定的pbkdf 2。埃里克森在上面说了怎么做。Darksquid的方法容易受到密码攻击,而且也不起作用,除非纯文本的大小是aes块大小(128位)的倍数,因为他遗漏了填充。另外,它没有指定模式;出于考虑,请阅读维基百科的分组密码操作模式。
  • @darksquid Cipher aes256 = Cipher.getInstance("AES/OFB/NoPadding"); MessageDigest keyDigest = MessageDigest.getInstance("SHA-256"); byte[] keyHash = keyDigest.digest(secret.getBytes("UTF-8")); SecretKeySpec key = new SecretKeySpec(keyHash,"AES"); aes256.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(initializationVector));我也按照你的答案中的建议做了同样的事情,但是我最终还是得到了java.security.invalidkeyexception:非法密钥大小是下载JCE策略文件的强制要求吗?
  • 不要在任何类型的生产环境中使用此方法。当开始使用基于密码的加密时,许多用户会被代码墙淹没,不理解字典攻击和其他简单的黑客是如何工作的。虽然学习起来可能令人沮丧,但研究这一点是值得的。以下是一篇很好的初学者文章:adambard.com/blog/3-wrong-ways-to-store-a-password


添加到@wufoo的编辑中,以下版本使用inputstreams而不是文件,以使处理各种文件更加容易。它还将IV和salt存储在文件的开头,因此只需要跟踪密码。既然静脉注射和盐不需要是秘密的,这会让生活变得更容易一些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
    public final static int SALT_LEN     = 8;
    static final String     HEXES        ="0123456789ABCDEF";
    String                  mPassword    = null;
    byte[]                  mInitVec     = null;
    byte[]                  mSalt        = new byte[SALT_LEN];
    Cipher                  mEcipher     = null;
    Cipher                  mDecipher    = null;
    private final int       KEYLEN_BITS  = 128;    // see notes below where this is used.
    private final int       ITERATIONS   = 65536;
    private final int       MAX_FILE_BUF = 1024;

    /**
     * create an object with just the passphrase from the user. Don't do anything else yet
     * @param password
     */

    public AES(String password) {
        mPassword = password;
    }

    public static String byteToHex(byte[] raw) {
        if (raw == null) {
            return null;
        }

        final StringBuilder hex = new StringBuilder(2 * raw.length);

        for (final byte b : raw) {
            hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
        }

        return hex.toString();
    }

    public static byte[] hexToByte(String hexString) {
        int    len = hexString.length();
        byte[] ba  = new byte[len / 2];

        for (int i = 0; i < len; i += 2) {
            ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                                + Character.digit(hexString.charAt(i + 1), 16));
        }

        return ba;
    }

    /**
     * debug/print messages
     * @param msg
     */

    private void Db(String msg) {
        System.out.println("** Crypt **" + msg);
    }

    /**
     * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
     * Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
     *
     * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
     * into uncertain problems with that.
     *
     * @param input - the cleartext file to be encrypted
     * @param output - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */

    public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream)
            throws IOException, IllegalBlockSizeException, BadPaddingException {
        try {
            long             totalread = 0;
            int              nread     = 0;
            byte[]           inbuf     = new byte[MAX_FILE_BUF];
            SecretKeyFactory factory   = null;
            SecretKey        tmp       = null;

            // crate secureRandom salt and store  as member var for later use
            mSalt=new byte[SALT_LEN];

            SecureRandom rnd = new SecureRandom();

            rnd.nextBytes(mSalt);
            Db("generated salt :" + byteToHex(mSalt));
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            /*
             *  Derive the key, given password and salt.
             *
             * in order to do 256 bit crypto, you have to muck with the files for Java's"unlimted security"
             * The end user must also install them (not compiled in) so beware.
             * see here:  http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
             */

            KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

            tmp = factory.generateSecret(spec);

            SecretKey secret = new SecretKeySpec(tmp.getEncoded(),"AES");

            /*
             *  Create the Encryption cipher object and store as a member variable
             */

            mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            mEcipher.init(Cipher.ENCRYPT_MODE, secret);

            AlgorithmParameters params = mEcipher.getParameters();

            // get the initialization vectory and store as member var
            mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
            Db("mInitVec is :" + byteToHex(mInitVec));
            outputStream.write(mSalt);
            outputStream.write(mInitVec);

            while ((nread = inputStream.read(inbuf)) > 0) {
                Db("read" + nread +" bytes");
                totalread += nread;

                // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
                // and results in full blocks of MAX_FILE_BUF being written.
                byte[] trimbuf = new byte[nread];

                for (int i = 0; i < nread; i++) {
                    trimbuf[i] = inbuf[i];
                }

                // encrypt the buffer using the cipher obtained previosly
                byte[] tmpBuf = mEcipher.update(trimbuf);

                // I don't think this should happen, but just in case..
                if (tmpBuf != null) {
                    outputStream.write(tmpBuf);
                }
            }

            // finalize the encryption since we've done it in blocks of MAX_FILE_BUF
            byte[] finalbuf = mEcipher.doFinal();

            if (finalbuf != null) {
                outputStream.write(finalbuf);
            }

            outputStream.flush();
            inputStream.close();
            outputStream.close();
            outputStream.close();
            Db("wrote" + totalread +" encrypted bytes");
        } catch (InvalidKeyException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidParameterSpecException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeySpecException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
     * to disk as (output) File.
     *
     * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
     *  and still have a correctly decrypted file in the end. Seems to work so left it in.
     *
     * @param input - File object representing encrypted data on disk
     * @param output - File object of cleartext data to write out after decrypting
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */

    public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream)
            throws IllegalBlockSizeException, BadPaddingException, IOException {
        try {
            CipherInputStream cin;
            long              totalread = 0;
            int               nread     = 0;
            byte[]            inbuf     = new byte[MAX_FILE_BUF];

            // Read the Salt
            inputStream.read(this.mSalt);
            Db("generated salt :" + byteToHex(mSalt));

            SecretKeyFactory factory = null;
            SecretKey        tmp     = null;
            SecretKey        secret  = null;

            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

            tmp    = factory.generateSecret(spec);
            secret = new SecretKeySpec(tmp.getEncoded(),"AES");

            /* Decrypt the message, given derived key and initialization vector. */
            mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // Set the appropriate size for mInitVec by Generating a New One
            AlgorithmParameters params = mDecipher.getParameters();

            mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();

            // Read the old IV from the file to mInitVec now that size is set.
            inputStream.read(this.mInitVec);
            Db("mInitVec is :" + byteToHex(mInitVec));
            mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));

            // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
            cin = new CipherInputStream(inputStream, mDecipher);

            while ((nread = cin.read(inbuf)) > 0) {
                Db("read" + nread +" bytes");
                totalread += nread;

                // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
                byte[] trimbuf = new byte[nread];

                for (int i = 0; i < nread; i++) {
                    trimbuf[i] = inbuf[i];
                }

                // write out the size-adjusted buffer
                outputStream.write(trimbuf);
            }

            outputStream.flush();
            cin.close();
            inputStream.close();
            outputStream.close();
            Db("wrote" + totalread +" encrypted bytes");
        } catch (Exception ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * adding main() for usage demonstration. With member vars, some of the locals would not be needed
     */

    public static void main(String[] args) {

        // create the input.txt file in the current directory before continuing
        File   input   = new File("input.txt");
        File   eoutput = new File("encrypted.aes");
        File   doutput = new File("decrypted.txt");
        String iv      = null;
        String salt    = null;
        AES    en      = new AES("mypassword");

        /*
         * write out encrypted file
         */

        try {
            en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput));
            System.out.printf("File encrypted to" + eoutput.getName() +"
iv:"
+ iv +"
salt:"
+ salt +"

"
);
        } catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }

        /*
         * decrypt file
         */

        AES dc = new AES("mypassword");

        /*
         * write out decrypted file
         */

        try {
            dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput));
            System.out.println("decryption finished to" + doutput.getName());
        } catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }
    }
}

  • 这个解决方案似乎使用了一些笨拙的缓冲区处理和绝对低于标准的异常处理,基本上是将它们记录下来,然后忘记它们。请注意,对于文件使用CBC是可以的,但对于传输安全则不行。使用PBKDF2和AES当然可以被保护,在这个意义上,它可能是解决方案的良好基础。


考虑使用加密机4J

首先,确保在继续之前安装了无限制强度权限策略文件,以便可以使用256位AES密钥。

然后执行以下操作:

1
2
3
String password ="mysupersecretpassword";
Key key = KeyFactory.AES.keyFromPassword(password.toCharArray());
Encryptor encryptor = new Encryptor(key,"AES/CBC/PKCS7Padding", 16);

您现在可以使用加密程序加密您的邮件。如果愿意,还可以执行流加密。它会自动生成并准备一个安全的IV,以方便您使用。

如果你想压缩一个文件,看看这个答案。使用一个更简单的方法使用Java加密AES文件。

  • 嗨,马丁,如果你想指出的话,你应该指出你是图书馆的作者。有大量的加密包装器试图使事情变得简单。这家公司有没有安全文件,或者收到过任何评论,让我们觉得值得一试?


使用此类进行加密。它起作用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ObjectCrypter {


public static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] mes)
        throws NoSuchAlgorithmException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        IllegalBlockSizeException,
        BadPaddingException, IOException {

    AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    SecretKeySpec newKey = new SecretKeySpec(keyBytes,"AES");
    Cipher cipher = null;
    cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
    return  cipher.doFinal(mes);

}

public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] bytes)
        throws NoSuchAlgorithmException,
        NoSuchPaddingException,
        InvalidKeyException,
        InvalidAlgorithmParameterException,
        IllegalBlockSizeException,
        BadPaddingException, IOException, ClassNotFoundException {

    AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    SecretKeySpec newKey = new SecretKeySpec(keyBytes,"AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
    return  cipher.doFinal(bytes);

}

}

这些是ivbytes和随机密钥;

1
2
3
4
String key ="e8ffc7e56311679f12b6fc91aa77a5eb";

byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
keyBytes = key.getBytes("UTF-8");

  • "它"……是的,但它不满足要求的安全解决方案(NOR)创建一个Java编码标准是满足我对异常处理的,在我的意见)。
  • 四initialized是零。搜索和ACPA野兽的攻击。
  • "异常的屁股,"随机"方法生成的密钥和IV是一个零,这一执行问题,但这些问题的解决是微不足道的。+ 1。