花了一周时间看了一本叫《Java加密与解密的艺术》的书,这本书还是可以看看,第三章是对java API中类的一些说明,以及一些小列子,4~9章是对这些类的详细代码表述,我都是随便扫了一眼看了一下,第10~12是关于加密解密的应用,其中有关于如何使用keytool工具生成一个证书,如何使用证书配置在tomcat服务器中,进行单向或双向的验证等。
类说明:(其中案例代码保存在github上)
https://github.com/ivan123king/LocalProject/tree/master/Exercise/src/com/lw/decodeAencode/classcase
java.security.Provider Java中提供者类
java.security.Security Java中管理提供者类的类
java.security.MessageDigest 继承MessageDigestSpi类,实现消息摘要算法
目前Java7支持的消息摘要算法: MD2,MD5,SHA-1(SHA),SHA-256,SHA-384,SHA-512
java.security.DigestInputStream 流式消息摘要方式类
java.security.DigestOutputStream 流式消息摘要方式类
MessageDigestTest 生成消息摘要的测试类
java.security.Key(Interface) 所有秘钥接口的顶层接口
其中SecretKey,PublicKey,PrivateKey三大接口定义了对称秘钥和非对称秘钥 (Interface)
java.security.AlgorithmParameters 提供密码参数的不透明表示
不透明: 不可以直接访问各参数域,只能得到与参数集相关联的算法名及该参数集的某类编码
DigitalSignature 生成呢验证数字签名的类
KeyPairGenerator,KeyPair,Signature
java.security.SignedObject 用来创建实际运行时对象的类
SignedObjectTest 同DigitalSignature,也是用来创建数字签名的类
java.security.Timestamp 封装有关签署时间戳的信息,且不可改变
TimestampTest 需要一个x.cer是一个数字证书文件,Timestamp是时间戳,还有其他创建方式,Date
java.security.CodeSigner 封装了代码签名者的信息,不可变,称之为代码签名
案例: CodeSignerCase
java.security.KeyStore 被称为密钥库,用于管理密钥和证书的存储。
证书类型: KeyStore.getDefaultType() 找到这个文件:C:\Program Files\Java\jdk1.8.0_111\jre\lib\security\java.security
案例: KeyStoreCase
javax.crypto.Mac 安全消息摘要类,必须有一个由发送方和接收方共享的秘密密钥才能生成最终消息摘要 对比MessageDigest
案例: MacCase
javax.crypto.KeyGenerator 同KeyPairGenerator(security包)相似,用来生成秘密密钥
案例: MacCase
javax.crypto.KeyAgreement 提供密钥协定协议的功能
案例: KeyAgreementCase
javax.crypto.SecretKeyFactory 同KeyFactory,用于产生秘密密钥
案例: SecretKeyFactoryCase
javax.crypto.Cipher 为加密解密提供密码功能
案例: CipherCase 加包和解包,对密钥等操作
加密,解密,对数据的操作
javax.crypto.CipherInputStream
javax.crypto.CipherOutputStream 属于Cipher扩展类
案例: 加密 :CipherInputStreamCase Cipher.DECRYPT_MODE
解密: CipherOutputStreamCase Cipher.ENCRYPT_MODE
javax.crypto.SealedObject 用加密算法创建对象并保护其机密性
案例: SealedObjectCase 不安全
javax.crypto.SecretKeySpec 用于构建秘密密钥规范
byte[] key = secretKey.getEncoded();
//通过密钥编码字节数组还原秘密密钥 DES是生成secretKey的算法,需要根据情况改变
SecretKey secretKey2 = new SecretKeySpec(key,"DES");
java.security.cert.Certificate
java.security.cert.CertificateFactory 有关证书的操作类
案例; CertificateCase
java.security.cert.CRL 证书撤销列表,也就是无效证书的列表
案例: CRLCase
SSLSocketCase SSL握手连接
org.bouncycastle.util.encoders.Base64 Base64编码解码
案例: Base64Case 需要bcprov-jdk15on-159.jar
jar包下载地址:http://www.bouncycastle.org/latest_releases.html
org.bouncycastle.util.encoders.UrlBase64 UrlBase64编码,编码后和Base64一样
案例: UrlBase64Case 需要bcprov-jdk15on-159.jar
org.bouncycastle.util.encoders.Hex 用于16进制编码
案例: HexCase
org.apache.commons.codec.binary.Base64 base64编码
案例: CommonBase64Case 需要: commons-codec-1.11.jar
下载地址: Commons Codec辅助工具下载
和Bouncy Castle Base64编码不同之处在于,Bouncy Castle使用"."符号替代"="进行补位
而Commons Codec不进行补位
org.apache.commons.codec.binary.Base64InputStream
org.apache.commons.codec.binary.Base64OutputStream
Base64输入流和输出流支持
案例: CommonBase64StreamCase
org.apache.commons.codec.digest.DigestUtils 对MessageDigest的二次封装,提供更便捷更多方式消息摘要
java.util.zip.CRC32 循环冗余校验(Cyclic Redundancy Check) 一般在压缩数据中
案例: CRC32Case
AlgorithmCase 是一些基本算法案例
idea 目前很难破解的对称加密算法,基于密钥
pbe 基于口令的对称加密算法
dh算法: 基于密钥的非对称加密算法
DH算法说明:
1. 甲方生成公钥私钥,将公钥发送给乙方
2. 乙方根据甲方的公钥生成公钥私钥,将公钥发送给甲方
3. 甲方根据乙方的公钥和甲方的私钥生成私密密钥
4. 乙方根据甲方的公钥和乙方的私钥生成私密密钥
5. 甲方用自己的私密密钥加密
6. 乙方用自己的私密密钥解密
原因: 甲方私密密钥==乙方私密密钥,所以才能做到解密
rsa: 基于密钥的非对称加密算法,
1. 甲方构建公钥,私钥,公钥发送给乙方
2. 甲方使用私钥加密数据发送给乙方
3. 乙方使用公钥解密数据
4. 乙方使用公钥加密数据发送给甲方
5. 甲方使用私钥解密数据
缺点: 公钥是公布的,所以任何人都可以伪装成乙方
elgamal: 基于密钥的非对称加密算法
流程等同于rsa算法,只不过密钥对的构建由乙方完成
Sun Java Cryptography Extension(JCE) Unlimited Strenght Jurisdiction Policy File 下载地址
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
Commons Codec辅助工具下载:
http://commons.apache.org/proper/commons-codec/
一些需要记住的代码:
idea算法基于密钥对加密解密
/** * idea算法 * @throws NoSuchAlgorithmException * @throws NoSuchPaddingException * @throws InvalidKeyException * @throws BadPaddingException * @throws IllegalBlockSizeException */ public void ideaCase() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{ /* *1.生成密钥 */ Security.addProvider(new BouncyCastleProvider()); KeyGenerator kg = KeyGenerator.getInstance("IDEA"); kg.init(128); SecretKey key = kg.generateKey(); byte[] keyB = key.getEncoded(); /* * 2.加密 */ Security.addProvider(new BouncyCastleProvider()); Key k = new SecretKeySpec(keyB,"IDEA"); //IDEA 算法 ,ECB 工作模式, ISO10126Padding 填充模式 Cipher cipher = Cipher.getInstance("IDEA/ECB/ISO10126Padding"); cipher.init(Cipher.ENCRYPT_MODE, k); byte[] enData = cipher.doFinal("待加密数据".getBytes()); /* * 3.解密 */ Security.addProvider(new BouncyCastleProvider()); Key dk = new SecretKeySpec(keyB,"IDEA"); Cipher dCipher = Cipher.getInstance("IDEA/ECB/ISO10126Padding"); cipher.init(Cipher.DECRYPT_MODE, dk); byte[] deData = dCipher.doFinal(enData); }注释1处由甲方生成私密密钥, 将密钥(keyB)发送给乙方,对数据加密后,加密数据发送给乙方,
乙方通过还原私密密钥,然后解密数据。其中IDEA可以改变成其他密钥生成算法,如DES,DESede,AES,工作模式和填充模式都可以改变。
PBE基于口令加密解密:
/** * pbe算法:基于口令方式,而非密钥 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidKeyException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws InvalidAlgorithmParameterException */ public void pbeCase() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException{ /* * 1.生成盐 */ SecureRandom random = new SecureRandom(); byte[] salt = random.generateSeed(8); /* * 2.加密 */ String password = "口令"; PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); SecretKeyFactory factory = SecretKeyFactory.getInstance("PEBWITHMD5andDES"); SecretKey secretKey = factory.generateSecret(keySpec); //100 是迭代次数 PBEParameterSpec paramSpec = new PBEParameterSpec(salt,100); Cipher cipher = Cipher.getInstance("PBEWITHMD5andDES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey,paramSpec); byte[] enData = cipher.doFinal("加密数据".getBytes()); /* * 3. 解密 */ cipher.init(Cipher.DECRYPT_MODE, secretKey,paramSpec); byte[] deData = cipher.doFinal(enData); }盐是一个混淆数据,甲方将secretKey.getEncoded()即口令发送给乙方,使用口令(password)和盐对数据加密后将盐和数据发送给乙方,乙方使用口令和盐对数据解密。
DH私钥公钥生成密钥的非对称加密解密算法:
/** * DH非对称加密 * 甲乙双方构建的私密密钥相同,DH算法使用同一个私密密钥完成加密,解密 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidAlgorithmParameterException * @throws IllegalStateException * @throws InvalidKeyException * @throws NoSuchPaddingException * @throws BadPaddingException * @throws IllegalBlockSizeException * * 如果运行报错:java.security.InvalidKeyException: Illegal key size or default parameters * Sun Java Cryptography Extension(JCE) Unlimited Strenght Jurisdiction Policy File 下载地址 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html * 上面下载的里面的两个jar包:local_policy.jar 和 US_export_policy.jar 替换掉原来安装目录C:\Program Files\Java\jdk1.8.0_111\jre\lib\security 下的两个jar包接可以了然后就重新运行程序,不会报错了,测试代码如下: * * 原因:因为美国的出口限制,Sun通过权限文件(local_policy.jar、US_export_policy.jar)做了相应限制。 * 所以需要到官网上下载没有限制的local_policy.jar和US_export_policy.jar * * DH算法说明: * 1. 甲方生成公钥私钥,将公钥发送给乙方 * 2. 乙方根据甲方的公钥生成公钥私钥,将公钥发送给甲方 * 3. 甲方根据乙方的公钥和甲方的私钥生成私密密钥 * 4. 乙方根据甲方的公钥和乙方的私钥生成私密密钥 * 5. 甲方用自己的私密密钥加密 * 6. 乙方用自己的私密密钥解密 * * 原因: 甲方私密密钥==乙方私密密钥,所以才能做到解密 * */ public void dhCase() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalStateException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{ /* * 1.构建甲方公钥,私钥 */ KeyPairGenerator fKeyPairGenerator = KeyPairGenerator.getInstance("DH"); fKeyPairGenerator.initialize(512);//密钥长度,必须是64的倍数 512~2048 KeyPair fKeyPair = fKeyPairGenerator.generateKeyPair(); //甲方公钥 DHPublicKey fpublicKey = (DHPublicKey) fKeyPair.getPublic(); //甲方私钥 DHPrivateKey fprivateKey = (DHPrivateKey) fKeyPair.getPrivate(); /* * 2.构建乙方公钥,私钥 */ //解析甲方公钥 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(fpublicKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("DH"); //产生公钥,应该是和 fpublicKey一样的,只不过传输使用byte传递甲方公钥 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); DHParameterSpec dhParamSpec = ((DHPublicKey)pubKey).getParams(); //实例化密钥对生成器 KeyPairGenerator sKeyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm()); sKeyPairGenerator.initialize(dhParamSpec); KeyPair sKeyPair = sKeyPairGenerator.generateKeyPair(); //乙方公钥 DHPublicKey spublicKey = (DHPublicKey) sKeyPair.getPublic(); //乙方私钥 DHPrivateKey sprivateKey = (DHPrivateKey) sKeyPair.getPrivate(); /* * 3.生成甲方本地密钥,使用甲方私钥,乙方公钥构建 */ //解析乙方公钥 keyFactory = KeyFactory.getInstance("DH"); x509KeySpec = new X509EncodedKeySpec(spublicKey.getEncoded()); pubKey = keyFactory.generatePublic(x509KeySpec); //解析甲方私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(fprivateKey.getEncoded()); PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm()); keyAgree.init(priKey); keyAgree.doPhase(pubKey, true); //生成本地密钥 SecretKey fSecretKey = keyAgree.generateSecret("AES"); /* * 4.生成乙方本地密钥,使用甲方公钥,乙方私钥 */ //解析甲方公钥 keyFactory = KeyFactory.getInstance("DH"); x509KeySpec = new X509EncodedKeySpec(fpublicKey.getEncoded()); pubKey = keyFactory.generatePublic(x509KeySpec); //解析乙方私钥 pkcs8KeySpec = new PKCS8EncodedKeySpec(sprivateKey.getEncoded()); priKey = keyFactory.generatePrivate(pkcs8KeySpec); keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm()); keyAgree.init(priKey); keyAgree.doPhase(pubKey, true); //生成本地密钥 SecretKey sSecretKey = keyAgree.generateSecret("AES"); //fSecretKey和sSecretKey其实是一样的 System.out.println(Base64.encodeBase64String(fSecretKey.getEncoded())); System.out.println(Base64.encodeBase64String(sSecretKey.getEncoded())); /* * 5.甲方加密发送数据 使用甲方私密密钥加密 * qteiCN0nJdLe0IFymk4bSw== * 3nz7OhvgNKcfgRb3uwJD+72Tc1i6gxEU7J4pUZWOf9s= */ SecretKey secretKey = new SecretKeySpec(fSecretKey.getEncoded(),"AES"); Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] fdata = cipher.doFinal("待加密数据".getBytes()); /* * 6.乙方解密甲方发送到的加密数据 使用乙方私密密钥解密 */ secretKey = new SecretKeySpec(sSecretKey.getEncoded(),"AES"); cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] sdata = cipher.doFinal(fdata); System.out.println(new String(sdata)); }
代码中的算法标识DH根据算法不同需要填充不同的算法标识,比如其他非对称加密算法ECDH,RSA,ElGamal
数字签名:签名类是Signature
/** * 数字签名:私钥签名,公钥验证签名 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws SignatureException */ public void sign() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException{ /* * 1.生成公钥私钥密钥对 */ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(512); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); /* * 2.甲方私钥签名 */ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); //下面就是用于签名的类 Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(priKey); signature.update("用作签名的数据".getBytes()); byte[] sign = signature.sign();//sign就是用来传递的签名 /* * 3. 乙方使用公钥验证签名 */ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getEncoded()); keyFactory = KeyFactory.getInstance("RSA"); PublicKey pubKey = keyFactory.generatePublic(keySpec); signature = Signature.getInstance("MD5withRSA"); signature.initVerify(pubKey); signature.update("用作签名的数据".getBytes()); boolean status = signature.verify(sign); }
通过数字证书提取公钥私钥:
/** * 数字证书加密解密 * 1.加载数字证书 * 2.获取数字证书中的公钥和私钥 * 3.使用密钥对加解密,或者签名 * * 生成密钥对 zlex.keystore文件 * keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 365 -alias www.zlex.org -keystore zlex.keystore -dname "CN =wwww.zlex.org,OU=zlex,O=zlex,L=BJ,ST=Bj,C=CN" 导出数字证书:zlex.cer文件 keytool -exportcert -alias www.zlex.org -keystore zlex .keystore -file zlex.cer -rfc * @throws KeyStoreException * @throws IOException * @throws CertificateException * @throws NoSuchAlgorithmException * @throws UnrecoverableKeyException */ public void certificate() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException{ String keyStorePath = "D:\\tools\\java生成数字证书\\zlex.keystore"; String certificatePath = "D:\\tools\\java生成数字证书\\zlex.cer"; String password = "123456"; String alias = "www.zlex.org"; /* * 1.加载密钥库文件 zlex.keystore */ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream is = new FileInputStream(keyStorePath); ks.load(is,password.toCharArray()); is.close(); /* * 2.获取密钥库文件中公钥私钥 */ //获取私钥需要密码 PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password.toCharArray()); //获取公钥需要有证书中获得 /* * 3. 加载证书文件 * 当然也可以从keystore文件中获取证书 ks.getCertificate(alias); */ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); is = new FileInputStream(certificatePath); Certificate certificate = certificateFactory.generateCertificate(is); is.close(); /* * 4.获取公钥 */ PublicKey publicKey = certificate.getPublicKey(); /** * 有了公钥私钥,就完全可以仿照上面的方法做签名和加解密 */ }
byte[] input = str.getBytes(); //URL Base64 编码 byte[] data = UrlBase64.encode(input);
MessageDigest sha = MessageDigest.getInstance("MD5"); //更新摘要信息 sha.update(input); //获取消息摘要结果 byte[] output = sha.digest();这里使用MD5算法,当然也可以使用其他算法。
PS: MD5等算法在以前看新闻好像看到已经被王小云等研究人员使用碰撞算法破解。
对自己的话: 以前也知道加密解密的机制等,也知道各种加密解密算法,私钥公钥等,但是从来没有去记代码,一方面是用的不多,直到前几个月公司需要和别人数据交互,需要加密解密,结果代码方面完全不知道使用哪些类,代码真的很重要。写个文章至少以后自己遇到可以看看该使用哪些类。
使用的包:
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; import java.security.SignatureException; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyAgreement; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.interfaces.DHPrivateKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.MessageDigest;