混合加密是在大数据上安全但缓慢的加密(不对称加密)和不安全但快速的加密(对称加密)之间的一线希望。 混合密码术结合了单键加密和解密的速度以及公私钥对提供的安全性,因此被认为是高度安全的加密类型。
混合密码术最常用的方法是使用对称密钥加密数据,然后使用发送方的私钥或接收方的公钥对对称密钥进行加密。 为了解密,接收器必须首先使用相应的非对称密钥对对称密钥进行解密,然后使用该对称密钥来解密接收到的数据。 此示例可能有助于阐明:
想象一下有两个已经交换了公共密钥的用户Alice和Bob的场景。 爱丽丝想向鲍勃发送一个名为“ confidential.txt”的文本文件。 她将按照以下步骤对邮件进行加密:
- 使用强大的算法生成新的对称密钥
- 使用第1步中的密钥对“ confidential.txt”进行加密
- 使用Bob的公钥对第1步中的密钥进行加密
- 将加密的文本和密钥发送到Bob
鲍勃收到两个文件。 他将对它们进行解密的步骤如下:
- 使用自己的私钥解密收到的密钥
- 使用解密后在步骤1中获得的密钥解密他收到的文本
注意
如果您想了解有关对称和非对称加密的更多信息,请参阅“ 对称密钥密码术”示例和“ 非对称密钥密码术”示例
下面的代码中的过程分为单独的文件,以帮助理解过程并简化学习者。
1.项目目录
2.为爱丽丝和鲍勃生成密钥
根据您的平台,有几种方法可以生成公用-专用密钥对。 在此示例中,我们将使用Java创建两对,一对用于Alice,另一种用于Bob。 在此示例中,我们将使用的密码算法是RSA。
package com.mkyong.keypair;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
public class GenerateKeys {
private KeyPairGenerator keyGen;
private KeyPair pair;
private PrivateKey privateKey;
private PublicKey publicKey;
public GenerateKeys(int keylength)
throws NoSuchAlgorithmException, NoSuchProviderException {
this.keyGen = KeyPairGenerator.getInstance("RSA");
this.keyGen.initialize(keylength);
}
public void createKeys() {
this.pair = this.keyGen.generateKeyPair();
this.privateKey = pair.getPrivate();
this.publicKey = pair.getPublic();
}
public PrivateKey getPrivateKey() {
return this.privateKey;
}
public PublicKey getPublicKey() {
return this.publicKey;
}
public void writeToFile(String path, byte[] key) throws IOException {
File f = new File(path);
f.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(f);
fos.write(key);
fos.flush();
fos.close();
}
public static void main(String[] args)
throws NoSuchAlgorithmException, NoSuchProviderException, IOException {
GenerateKeys gk_Alice;
GenerateKeys gk_Bob;
gk_Alice = new GenerateKeys(1024);
gk_Alice.createKeys();
gk_Alice.writeToFile("KeyPair/publicKey_Alice", gk_Alice.getPublicKey().getEncoded());
gk_Alice.writeToFile("KeyPair/privateKey_Alice", gk_Alice.getPrivateKey().getEncoded());
gk_Bob = new GenerateKeys(1024);
gk_Bob.createKeys();
gk_Bob.writeToFile("KeyPair/publicKey_Bob", gk_Bob.getPublicKey().getEncoded());
gk_Bob.writeToFile("KeyPair/privateKey_Bob", gk_Bob.getPrivateKey().getEncoded());
}
}
输出:
3. Alice生成一个对称密钥
使用下面的代码,您可以生成一个对称密钥。 该代码使用SecureRandom
为密钥添加随机性。 有关SecureRandom
更多信息,请参阅Java –如何创建强随机数 。
构造函数使用将保存密钥的目录的路径,密钥的长度以及将用于创建密钥的算法进行初始化。 要了解有关每种算法的密钥长度的更多信息,请参阅加密算法的导入限制 。
在此示例中,我们使用AES,因为它被认为是速度和安全性之间的一线希望。 如果您想了解有关加密算法的更多信息,请参阅数据加密算法的性能分析:2.5比较算法 。
package com.mkyong.onekey;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class GenerateSymmetricKey {
private SecretKeySpec secretKey;
public GenerateSymmetricKey(int length, String algorithm)
throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException {
SecureRandom rnd = new SecureRandom();
byte [] key = new byte [length];
rnd.nextBytes(key);
this.secretKey = new SecretKeySpec(key, algorithm);
}
public SecretKeySpec getKey(){
return this.secretKey;
}
public void writeToFile(String path, byte[] key) throws IOException {
File f = new File(path);
f.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(f);
fos.write(key);
fos.flush();
fos.close();
}
public static void main(String[] args)
throws NoSuchAlgorithmException, NoSuchPaddingException, IOException {
GenerateSymmetricKey genSK = new GenerateSymmetricKey(16, "AES");
genSK.writeToFile("OneKey/secretKey", genSK.getKey().getEncoded());
}
}
输出:
4.爱丽丝写下她的信息
5.爱丽丝加密数据
5.1 StartEncryption
类包含三个方法getPrivate()
, getPublic()
和getSecretKey()
,用于从文件中构建相应的密钥。 在main方法中,我们构造了需要用于加密的两个类的File objects
。 EncryptData
和EncryptKey
。
package com.mkyong.hybrid.encrypt;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class StartEncryption {
public PrivateKey getPrivate(String filename, String algorithm) throws Exception {
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
public PublicKey getPublic(String filename, String algorithm) throws Exception {
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(spec);
}
public SecretKeySpec getSecretKey(String filename, String algorithm) throws IOException{
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
return new SecretKeySpec(keyBytes, algorithm);
}
public static void main(String[] args)
throws IOException, GeneralSecurityException, Exception{
StartEncryption startEnc = new StartEncryption();
File originalKeyFile = new File("OneKey/secretKey");
File encryptedKeyFile = new File("EncryptedFiles/encryptedSecretKey");
new EncryptKey(startEnc.getPublic("KeyPair/publicKey_Bob", "RSA"),
originalKeyFile, encryptedKeyFile, "RSA");
File originalFile = new File("confidential.txt");
File encryptedFile = new File("EncryptedFiles/encryptedFile");
new EncryptData(originalFile, encryptedFile,
startEnc.getSecretKey("OneKey/secretKey", "AES"), "AES");
}
}
5.2 EncryptData
构造接收作为参数的两个File
对象(第一个是文件我们要加密,第二个是输出文件),一个SecretKeySpec
对象其是对称密钥,我们要使用的加密和一个String
与名我们将用于Cipher
的算法。
package com.mkyong.hybrid.encrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
public class EncryptData {
private Cipher cipher;
public EncryptData(File originalFile, File encrypted, SecretKeySpec secretKey, String cipherAlgorithm)
throws IOException, GeneralSecurityException{
this.cipher = Cipher.getInstance(cipherAlgorithm);
encryptFile(getFileInBytes(originalFile), encrypted, secretKey);
}
public void encryptFile(byte[] input, File output, SecretKeySpec key)
throws IOException, GeneralSecurityException {
this.cipher.init(Cipher.ENCRYPT_MODE, key);
writeToFile(output, this.cipher.doFinal(input));
}
private void writeToFile(File output, byte[] toWrite)
throws IllegalBlockSizeException, BadPaddingException, IOException{
output.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(output);
fos.write(toWrite);
fos.flush();
fos.close();
System.out.println("The file was successfully encrypted and stored in: " + output.getPath());
}
public byte[] getFileInBytes(File f) throws IOException{
FileInputStream fis = new FileInputStream(f);
byte[] fbytes = new byte[(int) f.length()];
fis.read(fbytes);
fis.close();
return fbytes;
}
}
5.3 EncryptKey
构造函数接收将用于加密的PublicKey
作为参数,两个File
对象(第一个是包含Symmetric Key的文件,第二个是输出文件),以及一个带有我们所用算法名称的String
将用于Cipher
。
package com.mkyong.hybrid.encrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
public class EncryptKey {
private Cipher cipher;
public EncryptKey(PublicKey key, File originalKeyFile, File encryptedKeyFile, String cipherAlgorithm)
throws IOException, GeneralSecurityException{
this.cipher = Cipher.getInstance(cipherAlgorithm);
encryptFile(getFileInBytes(originalKeyFile), encryptedKeyFile, key);
}
public void encryptFile(byte[] input, File output, PublicKey key)
throws IOException, GeneralSecurityException {
this.cipher.init(Cipher.ENCRYPT_MODE, key);
writeToFile(output, this.cipher.doFinal(input));
}
private void writeToFile(File output, byte[] toWrite)
throws IllegalBlockSizeException, BadPaddingException, IOException{
output.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(output);
fos.write(toWrite);
fos.flush();
fos.close();
System.out.println("The key was successfully encrypted and stored in: " + output.getPath());
}
public byte[] getFileInBytes(File f) throws IOException{
FileInputStream fis = new FileInputStream(f);
byte[] fbytes = new byte[(int) f.length()];
fis.read(fbytes);
fis.close();
return fbytes;
}
}
输出:
The key was successfully encrypted and stored in: EncryptedFiles\encryptedSecretKey
The file was successfully encrypted and stored in: EncryptedFiles\encryptedFile
6. Bob接收数据并解密
6.1 StartDecryption
类包含三个方法getPrivate()
, getPublic()
和getSecretKey()
,用于从文件中构建相应的密钥。 在main方法中,我们构造需要用于解密的两个类的File
对象; DecryptData
和DecryptKey
。
package com.mkyong.hybrid.decrypt;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class StartDecryption {
public PrivateKey getPrivate(String filename, String algorithm) throws Exception {
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
public PublicKey getPublic(String filename, String algorithm) throws Exception {
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePublic(spec);
}
public SecretKeySpec getSecretKey(String filename, String algorithm) throws IOException{
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
return new SecretKeySpec(keyBytes, algorithm);
}
public static void main(String[] args) throws IOException, GeneralSecurityException, Exception{
StartDecryption startEnc = new StartDecryption();
File encryptedKeyReceived = new File("EncryptedFiles/encryptedSecretKey");
File decreptedKeyFile = new File("DecryptedFiles/SecretKey");
new DecryptKey(startEnc.getPrivate("KeyPair/privateKey_Bob", "RSA"),
encryptedKeyReceived, decreptedKeyFile, "RSA");
File encryptedFileReceived = new File("EncryptedFiles/encryptedFile");
File decryptedFile = new File("DecryptedFiles/decryptedFile");
new DecryptData(encryptedFileReceived, decryptedFile,
startEnc.getSecretKey("DecryptedFiles/SecretKey", "AES"), "AES");
}
}
6.2 DecryptKey
构造接收作为参数的PrivateKey
将被用于解密中,两个File
对象(第一个是加密密钥和第二是输出文件)和一个String
与算法的,我们将使用的名称Cipher
。
package com.mkyong.hybrid.decrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
public class DecryptKey {
private Cipher cipher;
public DecryptKey(PrivateKey privateKey, File encryptedKeyReceived, File decreptedKeyFile, String algorithm)
throws IOException, GeneralSecurityException {
this.cipher = Cipher.getInstance(algorithm);
decryptFile(getFileInBytes(encryptedKeyReceived), decreptedKeyFile, privateKey);
}
public void decryptFile(byte[] input, File output, PrivateKey key)
throws IOException, GeneralSecurityException {
this.cipher.init(Cipher.DECRYPT_MODE, key);
writeToFile(output, this.cipher.doFinal(input));
}
private void writeToFile(File output, byte[] toWrite)
throws IllegalBlockSizeException, BadPaddingException, IOException{
output.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(output);
fos.write(toWrite);
fos.flush();
fos.close();
}
public byte[] getFileInBytes(File f) throws IOException{
FileInputStream fis = new FileInputStream(f);
byte[] fbytes = new byte[(int) f.length()];
fis.read(fbytes);
fis.close();
return fbytes;
}
}
6.3 DecryptData
构造函数接收两个File
对象(第一个是加密文件,第二个是输出文件)作为参数,一个SecretKeySpec
对象(这是我们要用于解密的对称密钥)和一个带有算法名称的String
我们将使用密码。
package com.mkyong.hybrid.decrypt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
public class DecryptData {
private Cipher cipher;
public DecryptData(File encryptedFileReceived, File decryptedFile, SecretKeySpec secretKey, String algorithm)
throws IOException, GeneralSecurityException {
this.cipher = Cipher.getInstance(algorithm);
decryptFile(getFileInBytes(encryptedFileReceived), decryptedFile, secretKey);
}
public void decryptFile(byte[] input, File output, SecretKeySpec key)
throws IOException, GeneralSecurityException {
this.cipher.init(Cipher.DECRYPT_MODE, key);
writeToFile(output, this.cipher.doFinal(input));
}
private void writeToFile(File output, byte[] toWrite)
throws IllegalBlockSizeException, BadPaddingException, IOException{
output.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(output);
fos.write(toWrite);
fos.flush();
fos.close();
System.out.println("The file was successfully decrypted. You can view it in: " + output.getPath());
}
public byte[] getFileInBytes(File f) throws IOException{
FileInputStream fis = new FileInputStream(f);
byte[] fbytes = new byte[(int) f.length()];
fis.read(fbytes);
fis.close();
return fbytes;
}
}
输出:
The file was successfully decrypted. You can view it in: DecryptedFiles\decryptedFile
下载源代码
下载它– HybridCryptographyExample.zip (11 KB)
参考文献
翻译自: https://mkyong.com/java/java-hybrid-cryptography-example/