赞
踩
对称加算法就是传统的==用一个密钥进行加密和解密==,例如,常用的 WinZip 和WinRAR 对压缩包的加密和解密,就是使用对称加密算法
算法 | 密钥长度 | 工作模式 | t填充模式 |
---|---|---|---|
DES | 56/54 | ECB/CBC/PCBC/CTR | NoPadding/PKCS5Padding |
AES | 128/192/256 | ECB/CBC/PCBC/CTR | NoPadding/PKCS5Padding/PKCS7Padding |
IDEA | 128 | ECB | PKCS5Padding/PKCS7Padding |
说明:
AES算法是目前应用最广泛的加密算法。比较常见的工作模式是ECB和CBC。
ECB模式是最简单的AES加密模式,它需要一个固定长度的密钥,固定的明文会生成固定的密文
解密与加密均使用同一个密钥,所以安全性较低
// AES + ECB public class Demo01 { public static void main(String[] args) throws GeneralSecurityException { // 原文: String message = "天生我材必有用飞流直下三千尺"; System.out.println("Message(原始信息): " + message); // 128位密钥 = 16 bytes Key: byte[] key = "1234567890abcdef".getBytes(); // 加密: byte[] data = message.getBytes(); byte[] encrypted = encrypt(key, data); System.out.println("Encrypted(加密内容): " + Base64.getEncoder().encodeToString(encrypted)); // 解密: byte[] decrypted = decrypt(key, encrypted); System.out.println("Decrypted(解密内容): " + new String(decrypted)); } // 加密: public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 创建密码对象,需要传入算法/工作模式/填充模式 Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding"); // 根据key的字节内容,"恢复"秘钥对象 SecretKey keySpec=new SecretKeySpec(key, "AES"); // 初始化秘钥:设置加密模式ENCRYPT_ cipher.init(Cipher.ENCRYPT_MODE, keySpec); // 根据原始内容(字节),进行加密 return cipher.doFinal(input); } // 解密: public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 创建密码对象,需要传入算法/工作模式/填充模式 Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding"); // 根据key的字节内容,"恢复"秘钥对象 SecretKey keySpec=new SecretKeySpec(key, "AES"); // 初始化秘钥:设置解密模式DECRYPT_MODE cipher.init(Cipher.DECRYPT_MODE, keySpec); // 根据原始内容(字节),进行解密 return cipher.doFinal(input); } }
步骤:
CBC模式,需要一个随机数作为 IV 参数,这样对于同一份明文,每次生成的密文都不同
//AES + CBC public class Demo02 { public static void main(String[] args) throws Exception { // 原文: String message = "Hello"; System.out.println("Message(原始信息): " + message); // 256位密钥 = 32 bytes Key: byte[] key = "1234567890abcdef1234567890abcdef".getBytes(); // 加密: byte[] data = message.getBytes(); byte[] encrypted = encrypt(key, data); System.out.println("Encrypted(加密内容): " + Base64.getEncoder().encodeToString(encrypted)); // 解密: byte[] decrypted = decrypt(key, encrypted); System.out.println("Decrypted(解密内容): " + new String(decrypted)); } // 加密: public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 设置算法/工作模式CBC/填充 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 恢复秘钥对象 SecretKey keySpec = new SecretKeySpec(key, "AES"); // CBC模式需要生成一个16 bytes的initialization vector: SecureRandom sr = SecureRandom.getInstanceStrong();// 生成器 byte[] iv = sr.generateSeed(16); System.out.println("iv字节数组内容:" + Arrays.toString(iv)); System.out.println("iv字节数组长度:" + iv.length); // 随机数封装成 IvParameterSpec 参数对象 IvParameterSpec ivps = new IvParameterSpec(iv); // 初始化秘钥:操作模式、秘钥、IV参数 cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps); // 加密 byte[] data = cipher.doFinal(input); // IV不需要保密,把IV和密文一起返回: return join(iv,data); } // 解密: public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 把input分割成IV和密文: byte[] iv=new byte[16]; byte[] data=new byte[input.length-16]; System.arraycopy(input, 0, iv, 0, 16); System.arraycopy(input, 16, data, 0, data.length); // 解密: Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec=new SecretKeySpec(key, "AES"); IvParameterSpec ivps = new IvParameterSpec(iv); // 初始化秘钥:操作模式、秘钥、IV参数 cipher.init(Cipher.DECRYPT_MODE, keySpec,ivps); // 解密操作 return cipher.doFinal(data); } // 合并数组 public static byte[] join(byte[] bs1, byte[] bs2) { byte[] r=new byte[bs1.length+bs2.length]; System.arraycopy(bs1, 0, r, 0, bs1.length); System.arraycopy(bs2, 0, r, bs1.length, bs2.length); return r; } }
步骤与 ECB 模式步骤相似,只需要额外添加一个随机生成的 IV 参数,然后将这个参数加密后与原文合并,解密时也需要将合并的数据拆分,分别获取 IV 参数和加密后的原文
AES 加密算法,需要将密钥进行传输,在这个过程中存在安全隐患,导致密钥泄露因此,需要使用密钥交换算法:DH
DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换,这个神奇的交换原理完全由数学理论支持。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOzHanTA-1689078393304)(E:\桌面\870edf07cee6af069cffdd61c74ac66.png)]
由于在这个过程中,只有公钥进行传输,私钥并没有参与传输,因此保证了数据在传输过程的安全性
public class Demo03 { public static void main(String[] args) { // Bob和Alice: Person bob = new Person("Bob"); Person alice = new Person("Alice"); // 各自生成KeyPair: 公钥+私钥 bob.generateKeyPair(); alice.generateKeyPair(); // 双方交换各自的PublicKey(公钥): // Bob根据Alice的PublicKey生成自己的本地密钥(共享公钥): bob.generateSecretKey(alice.publicKey.getEncoded()); // Alice根据Bob的PublicKey生成自己的本地密钥(共享公钥): alice.generateSecretKey(bob.publicKey.getEncoded()); // 检查双方的本地密钥是否相同: bob.printKeys(); alice.printKeys(); //使用共享密钥,继续dosth } } //用户类 class Person { public final String name; // 姓名 // 密钥 public PublicKey publicKey; // 公钥 private PrivateKey privateKey; // 私钥 private byte[] secretKey; // 本地秘钥(共享密钥) // 构造方法 public Person(String name) { this.name = name; } // 生成本地KeyPair:(公钥+私钥) public void generateKeyPair() { KeyPairGenerator kpGen; try { // 创建DH算法的“密钥对”生成器 kpGen = KeyPairGenerator.getInstance("DH"); kpGen.initialize(512); KeyPair kp=kpGen.generateKeyPair(); this.privateKey=kp.getPrivate(); this.publicKey=kp.getPublic(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } // 按照 "对方的公钥" => 生成"共享密钥" public void generateSecretKey(byte[] receivedPubKeyBytes) { try { // 从byte[] 恢复PublicKey X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receivedPubKeyBytes); // 根据DH算法获取KeyFactory KeyFactory kf = KeyFactory.getInstance("DH"); // 通过KeyFactory创建公钥 PublicKey recePublicKey = kf.generatePublic(keySpec); // 创建密钥协议对象(用于密钥协商) KeyAgreement keyAgreement = KeyAgreement.getInstance("DH"); keyAgreement.init(this.privateKey); // 初始化“自己的PrivateKey” keyAgreement.doPhase(recePublicKey, true); // 根据"对方的PublicKey" // 生成SecretKey本地密钥(共享公钥) this.secretKey = keyAgreement.generateSecret(); } catch (Exception e) { throw new RuntimeException(e); } } public void printKeys() { System.out.printf("Name: %s\n", this.name); System.out.printf("Private key: %x\n", new BigInteger(1, this.privateKey.getEncoded())); System.out.printf("Public key: %x\n", new BigInteger(1, this.publicKey.getEncoded())); System.out.printf("Secret key: %x\n", new BigInteger(1, this.secretKey)); } }
非对称加密:加密和解密使用的不是相同的密钥,用户A密钥加密后所得的信息,只能用用户A的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。称公开的密钥为公钥;不公开的密钥为私钥。只有同一个公钥-私钥对才能正常加解密。
非对称加密的缺点:运算速度非常慢,比对称加密要慢很多。
小明要加密一个消息发送给小红,他应该首先向小红索取她的公钥,然后,他用小红的公钥加密,把加密结果发送给小红,此文件只能由小红的私钥解开,因为小红的私钥在她自己手里,所以,除了小红,没有任何人能解开此加密结果。
// RSA public class Demo05 { public static void main(String[] args) throws Exception { // 明文: byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8"); // 创建公钥/私钥对 Human hong = new Human("小红"); Human ming = new Human("小明"); // 小明使用小红的公钥进行加密 // 1.获取小红的公钥 PublicKey hongPublicKey = hong.getPublicKey(); System.out.println(String.format("小红的public key(公钥): %x", new BigInteger(1, hongPublicKey.getEncoded()))); // 2.使用公钥加密 byte[] encrypted = ming.encrypt(plain, hongPublicKey); System.out.println(String.format("encrypted(加密): %x", new BigInteger(1, encrypted))); // 小红使用自己的私钥解密: // 1.获取小红的私钥,并输出 PrivateKey hongPrivateKey = hong.getPrivateKey(); System.out.println(String.format("小红的private key(私钥): %x", new BigInteger(1, hongPrivateKey.getEncoded()))); // 2.使用私钥解密 byte[] decrypted = hong.decrypt(encrypted); System.out.println("decrypted(解密): " + new String(decrypted, "UTF-8")); } } // 用户类 class Human { // 姓名 String name; // 私钥: PrivateKey privatekey; // 公钥: PublicKey publickey; // 构造方法 public Human(String name) throws GeneralSecurityException { // 初始化姓名 this.name = name; // 生成公钥/私钥对: KeyPairGenerator kpGen=KeyPairGenerator.getInstance("RSA"); kpGen.initialize(1024); KeyPair kp=kpGen.generateKeyPair(); this.privatekey=kp.getPrivate(); this.publickey=kp.getPublic(); } // 把私钥导出为字节 public PrivateKey getPrivateKey() { return this.privatekey; } // 把公钥导出为字节 public PublicKey getPublicKey() { return this.publickey; } // 用公钥加密 public byte[] encrypt(byte[] message,PublicKey publickey) throws GeneralSecurityException { // 使用公钥进行初始化 Cipher cipher=Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publickey); // 使用公钥进行初始化 return cipher.doFinal(message); } // 用私钥解密: public byte[] decrypt(byte[] input) throws GeneralSecurityException { // 使用私钥进行初始化 Cipher cipher=Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privatekey); return cipher.doFinal(input); } }
RSA的公钥和私钥都可以通过getEncoded()方法获得以byte[]表示的二进制数据,并根据需要保存到文件中。要从byte[]数组恢复公钥或私钥,可以这么写:
byte[] pkData = ...
byte[] skData = ...
KeyFactory kf = KeyFactory.getInstance("RSA");
// 恢复公钥:
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pkData);
PublicKey pk = kf.generatePublic(pkSpec);
// 恢复私钥:
PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(skData);
PrivateKey sk = kf.generatePrivate(skSpec);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。