(cipher——
- n. 密码;暗号;零
- vi. 使用密码;计算;做算术
- vt. 计算;做算术;将…译成密码
)
|
- Cipher cipher = Cipher.getInstance(algorithName);
1.2)获取密码对象:或者调用下面这个方法:
- Cipher cipher = Cipher.getInstance(algorithName, providerName);
2)SunJCE:JDK中是由名为"SunJCE"的提供商提供密码,如果没有指定其他提供商,则会默认该提供商。
- int mode = . . .;
- Key key = . . .;
- cipher.init(mode, key);
- 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();
step2) 用随机源来初始化密钥发生器。如果密码块的长度是可变的,还需要指定期望的密码块长度。
step3) 调用generateKey方法。
2)看个荔枝:下面是如何生成AES密钥的方法
- 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);
- 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
- 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();
- 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来获得:
- 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即可