(cipher——
- n. 密码;暗号;零
- vi. 使用密码;计算;做算术
- vt. 计算;做算术;将…译成密码
)
|
Cipher cipher = Cipher.getInstance(algorithName);
1.2)获取密码对象:或者调用下面这个方法:
Cipher cipher = Cipher.getInstance(algorithName, providerName);
2)SunJCE:JDK中是由名为"SunJCE"的提供商提供密码,如果没有指定其他提供商,则会默认该提供商。
Attention)int mode = . . .; Key key = . . .; cipher.init(mode, key);
A2)wrap 和 unwrap模式用一个密钥对另一个密钥进行加密;Cipher.ENCRYPT_MODE Cipher.DECRYPT_MODE Cipher.WRAP_MODE Cipher.UNWRAP_MODE
step3) 完成上述操作后,还必须调用一次doFinal方法。
step3.1)如果有最后一个输入数据块(其字节数小于blockSize),那么就要调用:
outBytes = cipher.doFinal(inBytes, 0, inLength);
step3.2)如果所有的输入数据都已经加密,则用下面的方法调用来代替:
outBytes = cipher.doFinal();
Attention)
对doFinal的调用是必要的,以便对最后的块进行"填充"。
step2) 用随机源来初始化密钥发生器。如果密码块的长度是可变的,还需要指定期望的密码块长度。
step3) 调用generateKey方法。
2)看个荔枝:下面是如何生成AES密钥的方法
2.1)或者,可以从一组固定的原始数据(也许是由口令或者随机击键产生的)来生成一个密钥,这时可以使用如下的SecretKeyFactory:(干货——引入SecretKeyFactory)keyGenerator keygen = KeyGenerator.getInstance("AES"); SecureRandom random = new SecureRandom(); keygen.init(random); Key key = keygen.generateKey();
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("AES");
byte[] keyData = . . .; // 16 bytes for AES
SecretKeySpec keySpec = new SecretKeySpec(keyData, "AES");
Key key = keyFactory.generateSecret(keySpec);
3)problem+solution:
3.1)problem: 如果要生成密钥,必须使用"真正的随机"数。例如,在Random类中的常规的随机数发生器,是根据当前的日期和时间来产生随机数的,因此它不够随机。例如,假设计算机时钟可以精确到1/10秒,那么,每天最多存在864,000个种子。如果攻击者知道发布密钥的日期(通常可以由截止日期推算出来),那么就可以很容易地生成那一天所有可能的种子。
3.2)solution: SecureRandom类产生的随机数,远比由Random类产生的那些数字安全得多。一旦你在字节数组中收集到随机位后,就可以将它传递给setSeed方法。(干货-SecureRandom类产生的随机数,远比由Random类产生的那些数字安全得多。)
SecureRandom secrand = new SecureRandom();
byte[] b = new byte[20];
// fill with truly random bits
secrand.setSeed(b);
Attention)
如果没有为随机数发生器提供种子,那么它将通过启动线程,使它们睡眠,然后测量它们被唤醒的准确时间,来计算自己的20个字节的种子。
package com.corejava.chapter9.cryption;
import java.io.*;
import java.security.*;
import javax.crypto.*;
public class AESTest
{
public static void main(String[] args)
throws IOException, GeneralSecurityException, ClassNotFoundException
{
if (args[0].equals("-genkey")) // 产生密钥
{
// 获取密钥生成器
KeyGenerator keygen = KeyGenerator.getInstance("AES");
// 创建随机源
SecureRandom random = new SecureRandom();
// 用随机源来初始化密钥发生器
keygen.init(random);
// 生成密钥
SecretKey key = keygen.generateKey();
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[1])))
{
out.writeObject(key); // 写出密钥到文件
}
}
else // 加密或者解密
{
int mode;
// 加密(解密)模式
if (args[0].equals("-encrypt")) mode = Cipher.ENCRYPT_MODE;
else mode = Cipher.DECRYPT_MODE;
// 带资源的try 语句, args[3]==secret.key
try (ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3]));
InputStream in = new FileInputStream(args[1]);
OutputStream out = new FileOutputStream(args[2]))
{
Key key = (Key) keyIn.readObject();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(mode, key); // 设置模式和密钥对其初始化
Util.crypt(in, out, cipher);
}
}
}
}
package com.corejava.chapter9.cryption;
import java.io.*;
import java.security.*;
import javax.crypto.*;
public class Util
{
public static void crypt(InputStream in, OutputStream out, Cipher cipher) throws IOException,
GeneralSecurityException
{
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
boolean more = true;
while (more)
{
// inBytes 就是一个缓冲区
inLength = in.read(inBytes);
if (inLength == blockSize)
{
// 只要输入数据块具有全长度(长度能够被8整除): 就要调用update方法;
int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
out.write(outBytes, 0, outLength);
}
else more = false;
}
// 而如果输入数据块不具有全长度(长度不能被8整除,此时需要填充): 就要调用 doFinal 方法;
if (inLength > 0)
outBytes = cipher.doFinal(inBytes, 0, inLength);
else
outBytes = cipher.doFinal();
out.write(outBytes);
}
}
5.1)请按照如下 steps 使用上述程序:
step1)首先生成一个密钥,运行如下命令行:
java AESTest -genkey secret.key
step2)密钥就被保存在secret.key文件中了。现在可以用如下命令进行加密:
java AESTest -encrypt plaintextFile encryptedFile secret.key
step3)用如下命令进行解密:
java AESTest -decrypt encryptedFile decryptedFile secret.key
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>javac com/corejava/chapter9/cryption/AES
Test.java
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST
est -genkey com/corejava/chapter9/cryption/secret.key
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST
est -encrypt com/corejava/chapter9/cryption/input.txt com/corejava/chapter9/cryption/output.txt com/
corejava/chapter9/cryption/secret.key
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST
est -encrypt com/corejava/chapter9/cryption/input.txt com/corejava/chapter9/cryption/encrypted.txt c
om/corejava/chapter9/cryption/secret.key
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST
est -decrypt com/corejava/chapter9/cryption/encrypted.txt com/corejava/chapter9/cryption/decrypted.t
xt com/corejava/chapter9/cryption/secret.key
2.2)可以使用CipherInputStream,对文件的数据进行读取和解密:Cipher cipher = . . .; cipher.init(Cipher.ENCRYPT_MODE, key); CipherOutputStream out = new CipherOutputStream(new FileOutputStream(outputFileName), cipher); byte[] bytes = new byte[BLOCKSIZE]; int inLength = getData(bytes); // get data from data source while (inLength != -1) { out.write(bytes, 0, inLength); inLength = getData(bytes); // get more data from data source } out.flush();
Attention) 密码流类能够透明地进行update 和 doFinal方法的调用,所以非常方便。Cipher cipher = . . .; cipher.init(Cipher.DECRYPT_MODE, key); CipherInputStream in = new CipherInputStream(new FileInputStream(inputFileName), cipher); byte[] bytes = new byte[BLOCKSIZE]; int inLength = in.read(bytes); while (inLength != -1) { putData(bytes, inLength); // put data to destination inLength = in.read(bytes); }
s1) Alice生成一个随机对称加密密钥,她用该密钥对明文进行加密。(第一次加密:对明文加密)
s2) Alice用Bob的公共密钥给对称密钥进行加密。(第二次加密:对对称密钥加密)
s3)Alice将加密后的对称密钥和加密后的明文同时发送给Bob。
s4) Bob用他的私有密钥给对称密钥解密。
s5) Bob用解密后的对称密钥给信息解密。
4)最普通的公共密钥算法: 是Rivest, Shamir, 和 Adleman发明的RSA算法。
5)如何使用RSA算法?
step1)如果要使用RSA算法,需要一对公共/私有密钥。你可以按如下方法使用KeyPairGenerator来获得:
step2)程序有三个选项。-genkey选项用于产生一个密钥对,-encrypt选项用于生成AES密钥,并且用公共密钥对其进行包装。KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA"); SecureRandom random = new SecureRandom(); pairgen.initialize(KEYSIZE, random); KeyPair keyPair = pairgen.generateKeyPair(); Key publicKey = keyPair.getPublic(); Key privateKey = keyPair.getPrivate();
Key key = . . .; // an AES key
Key publicKey = . . .; // a public RSA key
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, publicKey);
byte[] wrappedKey = cipher.wrap(key);
step2.1)然后它便生成一个包含下列内容的文件(files):
f1)包装过的密钥的长度;
f2)包装过的密钥字节;
f3)用AES密钥加密的明文;
Attention)-decrypt选项: 用于对这样的文件进行解密。
6)代码列表
package com.corejava.chapter9.cryption;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class RSATest
{
private static final int KEYSIZE = 512;
public static void main(String[] args) throws GeneralSecurityException, IOException, ClassNotFoundException
{
if(args[0].equals("-genkey")) // 生成密钥对(公钥+私钥)
{
// 密钥对生成器
KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA");
SecureRandom sr = new SecureRandom();
pairgen.initialize(KEYSIZE, sr); // 密钥对生成器初始化
KeyPair pair = pairgen.generateKeyPair(); // 生成密钥对(公钥+私钥)
try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[1])))
{
out.writeObject(pair.getPublic()); // 写入公钥到文件
}
try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[2])))
{
out.writeObject(pair.getPrivate()); // 写入私钥到文件
}
}
else if(args[0].equals("-encrypt")) // 加密模块
{
// 生成密钥
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sr = new SecureRandom();
keygen.init(sr);
SecretKey key = keygen.generateKey();
// wrap with RSA public key
// args[3]==public.key,args[2]==encryptedFile,args[1]=inputFile
try(ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3]));
DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(args[2]));
InputStream in = new FileInputStream(args[1]))
{
Key publicKey = (Key)keyIn.readObject();// 读入公钥
Cipher cipher = Cipher.getInstance("RSA");// RSA密码对象
cipher.init(Cipher.WRAP_MODE, publicKey); // 通过设置打包模式和公钥 来对RSA密码对象进行初始化
byte[] wrappedKey = cipher.wrap(key);// 通过带有公钥的RSA算法对象给密钥加密
dataOut.writeInt(wrappedKey.length); // 将加密后的密钥写入到输出流 dataOut
dataOut.write(wrappedKey);
cipher = Cipher.getInstance("AES"); // AES 密码对象
cipher.init(Cipher.ENCRYPT_MODE, key); // 通过设置加密模式和密钥 来对 AES 密码对象进行初始化
Util.crypt(in, dataOut, cipher); // 利用AES密码对象对inFile 进行加密并写入到输出流 dataOut
}
}
else // 解密模块
{
//args[1]==encryptedFile,args[3]=private.key,args[2]=decryptedFile;
try(DataInputStream dataIn = new DataInputStream(new FileInputStream(args[1]));
ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3]));
OutputStream out = new FileOutputStream(args[2]))
{
int length = dataIn.readInt();
byte[] wrappedKey = new byte[length];
dataIn.read(wrappedKey, 0, length); // 读入加密后的文件(经过公钥加密后的密钥 和 经过密钥加密后的文件内容)
// unwrap with RSA private key
Key privateKey = (Key)keyIn.readObject(); // 读入private.key 到 wrappedKey
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.UNWRAP_MODE, privateKey); // 通过设置解包模式和私钥 来对RSA密码对象进行初始化
// 通过带有私钥的RSA算法对象给密钥解密
Key key = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
cipher = Cipher.getInstance("AES"); // AES 密码对象
cipher.init(Cipher.DECRYPT_MODE, key); // 通过设置解密模式和密钥 来对 AES 密码对象进行初始化
Util.crypt(dataIn, out, cipher); // 通过使用解密后的密钥 对 加密后的文件内容 进行解密并写入到输出流 out
}
}
}
}<strong>
</strong>
7)运行该程序的steps:
step1)首先生成RSA密钥:
java RSATest -genkey public.key private.key |
step2)然后对一个文件进行加密:
java RSATest -encrypt plaintextFile encryptedFile public.key |
step3)最后,对文件进行解密,并且检验解密后的文件是否与明文相匹配。
java RSATest -decrypt encryptedFile decryptedFile private.key |
最后的执行结果:
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.RSAT
est -genkey com/corejava/chapter9/cryption/public.key com/corejava/chapter9/cryption/private.key
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.RSAT
est -encrypt com/corejava/chapter9/cryption/rsa_input.txt com/corejava/chapter9/cryption/rsa_encrypt
ed.txt com/corejava/chapter9/cryption/public.key
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.RSAT
est -decrypt com/corejava/chapter9/cryption/rsa_encrypted.txt com/corejava/chapter9/cryption/rsa_dec
rypted.txt com/corejava/chapter9/cryption/private.key
Attention)但是我们没有涉及许多高级和专有的话题,比如有:
A1)提供了对Kerberos协议进行支持的"通用安全服务"的GSS-API(原则上同样支持其他安全信息交换协议)。
A2)对SASL的支持,SASL即简单认证和安全层,可以为LDAP和IMAP协议所使用。
A3)对SSL的支持,SSL即安全套接层。在HTTP上使用SSL对应用程序的编程人员是透明的,只需要直接使用以https开头的URL即可。