总结Java常用到的六个加密技术和代码
加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。大体上分为双向加密和单向加密,而双向加密又分为对称加密和非对称加密(有些资料将加密直接分为对称加密和非对称加密)。
双向加密大体意思就是明文加密后形成密文,可以通过算法还原成明文。而单向加密只是对信息进行了摘要计算,不能通过算法生成明文,单向加密从严格意思上说不能算是加密的一种,应该算是摘要算法吧。
具体来说:
系统必须可用,非数学上不可译码。
系统不一定要保密,可以轻易落入敌人手中。
密匙必须可以不经书写的资料交换和记忆,且双方可以改变密匙。
系统可以用于电讯。
系统可以转移位置,它的功能必须不用经过几个人之手才可达到。
系统容易使用,不要求使用者的脑力过份操劳或有很多的规则。
一、主要的加密方式代码提供方
jdk:代码在java安装目录下的jre\lib\jce.jar包里;
cc:apache公司提供的org.apache.commons.codec
主页:
bc:org.bouncecastle
主页:
基本常用的使用jdk就够了。
二、base64算法
1、从现在加密算法的复杂性来看base64这种都不好意思说自己是加密,不过对于完全不懂计算机的人来说也够用了。采用base64编码具有不可读性,即所编码的数据不会被人用肉眼所直接看到。
base64编码一般用于url的处理,或者说任何你不想让普通人一眼就知道是啥的东西都可以用base64编码处理后再发布在网络上。
package com.amuro.strategy.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); } }
2、base64编码对应关系表
三、消息摘要算法(message digest)
消息摘要(message digest)又称为数字摘要(digital digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向hash加密函数对消息进行作用而产生。hash函数的抗冲突性使得如果一段明文稍有变化,哪怕只更改该段落的一个字母,通过哈希算法作用后都将产生不同的值。而hash算法的单向性使得要找到哈希值相同的两个不同的输入消息,在计算上是不可能的。所以数据的哈希值,即消息摘要,可以检验数据的完整性。
用大白话来说,任何一段数据应该都和人一样是唯一的,唯一的标识是什么,人类的话目前就是指纹,而数据的指纹是什么呢?没错,就是消息摘要算法产生的这一段string。比如我们在注册网站的时候,客户端向服务器传输的,应该是我们输入的密码进行消息摘要处理后的内容,这样就算服务器被攻破,hack也无法知道用户真实的密码是什么。不过有说现在md5和sha已经被攻破了,具体大家可以谷歌。
1、md5
package com.amuro.strategy.message_digest; 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"); } }
2、sha
package com.amuro.strategy.message_digest; 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 位的密钥按位替代或交换的方法形成密文组的加密方法。
package com.amuro.strategy.des; 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; } }
2、3des3des,也就是“triple des”,中文名“三重数据加密算法”,它相当于是对每个数据块应用三次 des 加密算法。由于计算机运算能力的增强,原版 des 密码的密钥长度变得容易被暴力破解;3des 即是设计用来提供一种相对简单的方法,即通过增加 des 的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
package com.amuro.strategy.des; 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; } }
3、aesaes,全称为“advanced encryption standard”,中文名“高级加密标准”,在密码学中又称 rijndael 加密法,是美国联邦*采用的一种区块加密标准。aes 加密算法作为新一代的数据加密标准汇聚了强安全性、高性能、高效率、易用和灵活等优点。aes 设计有三个密钥长度:128,192,256 位。相对而言,aes 的 128 密钥比 des 的 56 密钥强了 1021 倍。
package com.amuro.strategy.des; 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; } }
4、pbe
pbe,全称为“password base encryption”,中文名“基于口令加密”,是一种基于密码的加密算法,其特点是使用口令代替了密钥,而口令由用户自己掌管,采用随机数杂凑多重加密等方法保证数据的安全性。
pbe算法没有密钥的概念,把口令当做密钥了。因为密钥长短影响算法安全性,还不方便记忆,这里我们直接换成我们自己常用的口令就大大不同了,便于我们的记忆。但是单纯的口令很容易被字典法给穷举出来,所以我们这里给口令加了点“盐”,这个盐和口令组合,想破解就难了。同时我们将盐和口令合并后用消息摘要算法进行迭代很多次来构建密钥初始化向量的基本材料,使破译更加难了。
package com.amuro.strategy.pbe; 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 算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
package com.amuro.strategy.asymmetric; 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; } }
2、dh算法
dh,全称为“diffie-hellman”,他是一种确保共享key安全穿越不安全网络的方法,也就是常说的密钥一致协议。由公开密钥密码*的奠基人diffie和hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成“一致”的、可以共享的密钥。也就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。
以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(secretkey)对数据加密。这样,在互通了本地密钥(secretkey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!
package com.amuro.strategy.asymmetric; 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的:
package com.amuro.strategy.signature; 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; } }
关于数字签名和非对称加密算法的使用,还看到一个非常棒的例子,分享给大家:
唉,这个月买了太多的书,到月底揭不开锅了。正巧在qq上遇到了clark:
1-2-3:“clark,我需要200两纹银,能否借给我?”
clark:“没问题。我这就给你转账。请给我一张借条。”
1-2-3:“太谢谢了,我这就用word写一个借条给你。”
然后,我新建一个word文档,写好借条,存盘。然后,然后怎么办呢?我不能直接把借条发送给clark,原因有:
1. 我无法保证clark不会在收到借条后将“纹银200两”改为“纹银2000两”。
2. 如果我赖账,clark无法证明这个借条就是我写的。
3. 普通的word文档不能作为打官司的证据。
好在我早就申请了数字证书。我先用我的私钥对借条进行加密,然后将加密后的密文用qq发送给clark。clark收到了借条的密文后,在数字证书认证中心的网站上下载我的公钥,然后使用我的公钥将密文解密,发现确实写的是“借纹银200两”,clark就可以把银子放心的借给我了,我也不会担心clark会篡改我的借条,原因是:
1. 由于我发给clark的是密文,clark无法进行修改。clark倒是可以修改解密后的借条,但是clark没有我的私钥,没法模仿我对借条进行加密。这就叫防篡改。
2. 由于用我的私钥进行加密的借条,有且只有我的公钥可以解密。反过来讲,能用我的公钥解密的借条,一定是使用我的私钥加密的,而只有我才拥有我的私钥,这样clark就可以证明这个借条就是我写的。这就叫防抵赖。
3. 如果我一直赖着不还钱,clark把我告上了法庭,这个用我的私钥加密过的word文档就可以当作程堂证供。因为我国已经出台了《*电子签名法》,使数字签名具有了法律效力。
您一定已经注意到了,这个使用我的私钥进行了加密的借条,具有了防篡改、防抵赖的特性,并且可以作为程堂证供,就跟我对这个借条进行了“签名”的效果是一样的。对了,“使用我的私钥对借条进行加密”的过程就叫做数字签名。
这是一篇总结类文章,把一些常用的java加密技术和核心代码写在这边,供给需要朋友参考。
推荐阅读