总结类文章,把一些常用的Java加密技术和核心代码写在这边,供参考。
首先大家要记住现代密码学最重要的原则柯克霍夫原则:数据的安全基于密钥而不是算法的保密。也就是说即使密码系统的任何细节已为人悉知,只要密匙未洩漏,它也应是安全的。
具体来说:
系统必须可用,非数学上不可译码。
系统不一定要保密,可以轻易落入敌人手中。
密匙必须可以不经书写的资料交换和记忆,且双方可以改变密匙。
系统可以用于电讯。
系统可以转移位置,它的功能必须不用经过几个人之手才可达到。
系统容易使用,不要求使用者的脑力过份操劳或有很多的规则。
JDK:代码在java安装目录下的jre\lib\jce.jar包里;
CC:Apache公司提供的org.apache.commons.codec
主页: http://commons.apache.org/proper/commons-codec/
BC:org.bouncecastle
主页: http://www.bouncycastle.org/java.html
基本常用的使用JDK就够了。
1、从现在加密算法的复杂性来看Base64这种都不好意思说自己是加密,不过对于完全不懂计算机的人来说也够用了。采用Base64编码具有不可读性,即所编码的数据不会被人用肉眼所直接看到。
Base64编码一般用于url的处理,或者说任何你不想让普通人一眼就知道是啥的东西都可以用Base64编码处理后再发布在网络上。
import java.util.Base64; import com.amuro.strategy.IStrategy; /** * Base64算法基于64个基本字符,加密后的string中只包含这64个字符 * @author Amuro * */ public class Base64Strategy implements IStrategy { public String encode(String src) { byte[] encodeBytes = Base64.getEncoder().encode(src.getBytes()); return new String(encodeBytes); } public String decode(String src) { byte[] decodeBytes = Base64.getDecoder().decode(src.getBytes()); return new String(decodeBytes); } }
消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。HASH函数的抗冲突性使得如果一段明文稍有变化,哪怕只更改该段落的一个字母,通过哈希算法作用后都将产生不同的值。而HASH算法的单向性使得要找到哈希值相同的两个不同的输入消息,在计算上是不可能的。所以数据的哈希值,即消息摘要,可以检验数据的完整性。
用大白话来说,任何一段数据应该都和人一样是唯一的,唯一的标识是什么,人类的话目前就是指纹,而数据的指纹是什么呢?没错,就是消息摘要算法产生的这一段String。比如我们在注册网站的时候,客户端向服务器传输的,应该是我们输入的密码进行消息摘要处理后的内容,这样就算服务器被攻破,Hack也无法知道用户真实的密码是什么。不过有说现在MD5和SHA已经被攻破了,具体大家可以谷歌。
1、MD5
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; /** * 消息摘要算法 * @author Amuro * */ public class MD5Strategy implements IStrategy { public String encode(String src) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] encodeBytes = md.digest(src.getBytes()); return Hex.encodeHexString(encodeBytes); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } public String decode(String src) { throw new RuntimeException("MD5 no decode"); } }
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; /** * 安全散列算法 * @author Amuro * */ public class SHAStrategy implements IStrategy { public String encode(String src) { try { MessageDigest md = MessageDigest.getInstance("SHA"); md.update(src.getBytes()); return Hex.encodeHexString(md.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } public String decode(String src) { throw new RuntimeException("SHA no decode"); } }
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。而因为加密和解密都使用同一个密钥,如何把密钥安全地传递到解密者手上就成了必须要解决的问题。当然,安全性较低带来的优点就是优计算量小、加密速度快、加密效率高。
然并卵,现代计算机对这种级别的计算量早就不care了,安全才是最重要的。
1、DES
DES,全称为“Data Encryption Standard”,中文名为“数据加密标准”,是一种使用密钥加密的块算法。DES 算法为密码体制中的对称密码体制,又被称为美国数据加密标准,是 1972 年美国 IBM 公司研制的对称密码体制加密算法。 明文按 64 位进行分组,密钥长 64 位,密钥事实上是 56 位参与 DES 运算(第8、16、24、32、40、48、56、64 位是校验位, 使得每个密钥都有奇数个 1)分组后的明文组和 56 位的密钥按位替代或交换的方法形成密文组的加密方法。
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; /** * * @author Amuro * */ public class DESStrategy implements IStrategy { private Cipher cipher; private SecretKey generateKey; public String encode(String src) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); keyGenerator.init(56);//size SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); DESKeySpec desKeySpec = new DESKeySpec(keyBytes); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); generateKey = secretKeyFactory.generateSecret(desKeySpec); cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, generateKey); byte[] resultBytes = cipher.doFinal(src.getBytes()); return Hex.encodeHexString(resultBytes); } catch (Exception e) { e.printStackTrace(); } return null; } public String decode(String src) { try { cipher.init(Cipher.DECRYPT_MODE, generateKey); byte[] result = Hex.decodeHex(src.toCharArray()); return new String(cipher.doFinal(result)); } catch(Exception e) { e.printStackTrace(); } return null; } }
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; public class _3DESStrategy implements IStrategy { private Cipher cipher; private SecretKey generateKey; public String encode(String src) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede"); keyGenerator.init(168);//size SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); DESedeKeySpec desKeySpec = new DESedeKeySpec(keyBytes); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DESede"); generateKey = secretKeyFactory.generateSecret(desKeySpec); cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, generateKey); byte[] resultBytes = cipher.doFinal(src.getBytes()); return Hex.encodeHexString(resultBytes); } catch (Exception e) { e.printStackTrace(); } return null; } public String decode(String src) { try { cipher.init(Cipher.DECRYPT_MODE, generateKey); byte[] result = Hex.decodeHex(src.toCharArray()); return new String(cipher.doFinal(result)); } catch(Exception e) { e.printStackTrace(); } return null; } }
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; public class AESStrategy implements IStrategy { private Cipher cipher; private SecretKey generateKey; public String encode(String src) { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128);//size SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); generateKey = new SecretKeySpec(keyBytes, "AES"); cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, generateKey); byte[] resultBytes = cipher.doFinal(src.getBytes()); return Hex.encodeHexString(resultBytes); } catch (Exception e) { e.printStackTrace(); } return null; } public String decode(String src) { try { cipher.init(Cipher.DECRYPT_MODE, generateKey); byte[] result = Hex.decodeHex(src.toCharArray()); return new String(cipher.doFinal(result)); } catch(Exception e) { e.printStackTrace(); } return null; } }
import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; /** * 基于口令的加密(password),对称 + 消息摘要 * @author Amuro * */ public class PBEStrategy implements IStrategy { private Cipher cipher; private SecretKey generateKey; private PBEParameterSpec pbeParameterSpec; public String encode(String src) { try { SecureRandom secureRandom = new SecureRandom(); byte[] salt = secureRandom.generateSeed(8); String password = "amuro"; PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWITHMD5andDES"); generateKey = secretKeyFactory.generateSecret(pbeKeySpec); pbeParameterSpec = new PBEParameterSpec(salt, 100); cipher = Cipher.getInstance("PBEWITHMD5andDES"); cipher.init(Cipher.ENCRYPT_MODE, generateKey, pbeParameterSpec); byte[] resultBytes = cipher.doFinal(src.getBytes()); return Hex.encodeHexString(resultBytes); } catch(Exception e) { e.printStackTrace(); } return null; } public String decode(String src) { try { cipher.init(Cipher.DECRYPT_MODE, generateKey, pbeParameterSpec); byte[] result = Hex.decodeHex(src.toCharArray()); return new String(cipher.doFinal(result)); } catch(Exception e) { e.printStackTrace(); } return null; } }
非对称加密算法需要两个密钥来进行加密和解密,分别是公钥和私钥。需要注意的一点,这个公钥和私钥必须是一对的,如果用公钥对数据进行加密,那么只有使用对应的私钥才能解密,反之亦然。由于加密和解密使用的是两个不同的密钥,因此,这种算法叫做非对称加密算法。
1、RSA
其实,在早在 1978 年的时候,RSA就已经出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。其原理就如上面的工作过程所述。RSA 算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; public class RSAStrategy implements IStrategy { private RSAPublicKey rsaPublicKey; private RSAPrivateKey rsaPrivateKey; public String encode(String src) { try { //初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); rsaPublicKey = (RSAPublicKey)keyPair.getPublic(); rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate(); //私钥加密 公钥解密 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] resultBytes = cipher.doFinal(src.getBytes()); //私钥解密 公钥加密 // X509EncodedKeySpec x509EncodedKeySpec = // new X509EncodedKeySpec(rsaPublicKey.getEncoded()); // KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); // Cipher cipher = Cipher.getInstance("RSA"); // cipher.init(Cipher.ENCRYPT_MODE, publicKey); // byte[] resultBytes = cipher.doFinal(src.getBytes()); return Hex.encodeHexString(resultBytes); } catch(Exception e) { e.printStackTrace(); } return null; } public String decode(String src) { try { //私钥加密 公钥解密 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, publicKey); byte[] resultBytes = cipher.doFinal(Hex.decodeHex(src.toCharArray())); //私钥解密 公钥加密 // PKCS8EncodedKeySpec pkcs8EncodedKeySpec // = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); // KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); // Cipher cipher = Cipher.getInstance("RSA"); // cipher.init(Cipher.DECRYPT_MODE, privateKey); // byte[] resultBytes = cipher.doFinal(Hex.decodeHex(src.toCharArray())); return new String(resultBytes); } catch(Exception e) { e.printStackTrace(); } return null; } }
import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.Objects; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import org.apache.commons.codec.binary.Hex; import com.amuro.strategy.IStrategy; public class DHStrategy implements IStrategy { private Cipher cipher; private SecretKey receiverSecretKey; public String encode(String src) { try { //初始化发送方密钥 KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("DH"); senderKeyPairGenerator.initialize(512); KeyPair senderkeyPair = senderKeyPairGenerator.generateKeyPair(); PrivateKey senderPrivateKey = senderkeyPair.getPrivate(); byte[] senderPublicKeyBytes = senderkeyPair.getPublic().getEncoded();//发送方的公钥 //初始化接收方密钥,用发送方的公钥 KeyFactory receiverKeyFactory = KeyFactory.getInstance("DH"); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(senderPublicKeyBytes); PublicKey receiverPublicKey = receiverKeyFactory.generatePublic(x509EncodedKeySpec); DHParameterSpec dhParameterSpec = ((DHPublicKey)receiverPublicKey).getParams(); KeyPairGenerator receiverKeyPairGenerator = KeyPairGenerator.getInstance("DH"); receiverKeyPairGenerator.initialize(dhParameterSpec); KeyPair receiverKeyPair = receiverKeyPairGenerator.generateKeyPair(); PrivateKey receiverPrivateKey = receiverKeyPair.getPrivate(); byte[] receiverPublicKeyBytes = receiverKeyPair.getPublic().getEncoded(); KeyAgreement receiverKeyAgreement = KeyAgreement.getInstance("DH"); receiverKeyAgreement.init(receiverPrivateKey); receiverKeyAgreement.doPhase(receiverPublicKey, true); receiverSecretKey = receiverKeyAgreement.generateSecret("DES"); //发送方拿到接收方的public key就可以做加密了 KeyFactory senderKeyFactory = KeyFactory.getInstance("DH"); x509EncodedKeySpec = new X509EncodedKeySpec(receiverPublicKeyBytes); PublicKey senderPublicKey = senderKeyFactory.generatePublic(x509EncodedKeySpec); KeyAgreement senderKeyAgreement = KeyAgreement.getInstance("DH"); senderKeyAgreement.init(senderPrivateKey); senderKeyAgreement.doPhase(senderPublicKey, true); SecretKey senderSecretKey = senderKeyAgreement.generateSecret("DES"); if(Objects.equals(receiverSecretKey, senderSecretKey)) { cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, senderSecretKey); byte[] result = cipher.doFinal(src.getBytes()); return Hex.encodeHexString(result); } } catch(Exception e) { e.printStackTrace(); } return null; } public String decode(String src) { try { cipher.init(Cipher.DECRYPT_MODE, receiverSecretKey); byte[] result = Hex.decodeHex(src.toCharArray()); return new String(cipher.doFinal(result)); } catch(Exception e) { e.printStackTrace(); } return null; } }
非对称加密已经灰常安全了,但是还有一个破绽:
服务器A公布了自己的公钥,我的电脑是用服务器A的公钥加密数据后再发给服务器A的;这时候服务器B侵入了我的电脑,把我用来加密的公钥换成了它的公钥,于是我发出去的数据就会被服务器B的私钥破解了。肿么防止公钥被篡改呢?
对,我们想到了前面的消息摘要,服务器A把公钥丢给我的时候,同时去CA申请一份数字证书,其实主要就是公钥的消息摘要,有了这份证书,当我再用公钥加密的时候,我就可以先验证一下当前的公钥是否确定是服务器A发送给我的。
这里就贴一种RSA的:
import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; public class RSASign { public static boolean verifySign(String src) { try { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic(); PrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate(); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(privateKey); signature.update(src.getBytes()); //生成签名bytes byte[] signBytes = signature.sign(); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("MD5withRSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean isVerified = signature.verify(signBytes); return isVerified; } catch(Exception e) { e.printStackTrace(); } return false; } }