听说微信搜索《Java鱼仔》会变更强哦!
本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看哦
(一)关于加密算法
信息加密是现在几乎所有项目都需要用到的技术,身份认证、单点登陆、信息通讯、支付交易等场景中经常会需要用到加密算法,所谓加密算法,就是将原本的明文通过一系列算法操作变成密文。接下来就介绍一下目前比较常用的一些加密算法,本期不涉及算法底层,以应用介绍和代码展示为主。
如果只想了解原理,可跳过代码部分,代码可直接拿来使用。
(二)MD5算法
准确来讲,MD5不是一种加密算法,而是一种摘要算法,MD5能将明文输出为128bits的字符串,这个字符串是无法再被转换成明文的。网上一些MD5解密网站也只是保存了一些字符串对应的md5串,通过已经记录的md5串来找出原文。
我做过的几个项目中经常见到MD5用在加密上的场景。比如对密码的加密,生成一个密码后,使用MD5生成一个128位字符串保存在数据库中,用户输入密码后也先生成MD5串,再去数据库里比较。因此我们在找回密码时是无法得到原来的密码的,因为明文密码根本不会被保存。
publicclassMD5 { /*** 生成MD5* @param str* @return*/publicStringencode(Stringstr) { byte[] result=null; try { MessageDigestmd=MessageDigest.getInstance("MD5"); md.update(str.getBytes("UTF-8")); result=md.digest(); } catch (Exceptione) { e.printStackTrace(); returnnull; } returnparseByte2HexStr(result); } /*** 将二进制转换成十六进制** @param buf* @return*/privateStringparseByte2HexStr(bytebuf[]) { StringBuffersb=newStringBuffer(); for (inti=0; i<buf.length; i++) { Stringhex=Integer.toHexString(buf[i] &0xFF); if (hex.length() ==1) { hex='0'+hex; } sb.append(hex.toUpperCase()); } returnsb.toString(); } publicstaticvoidmain(String[] args) { MD5md5=newMD5(); Stringcontent="测试test"; System.out.println(md5.encode(content)); } }
(三)SHA1算法
SHA1也是和MD5类似的信息摘要算法,但是它比MD5更加安全。
publicclassSHA1 { publicStringencode(Stringstr) { try { MessageDigestmd=MessageDigest.getInstance("SHA-1"); md.update(str.getBytes("utf-8")); byte[] digest=md.digest(); returnbyteToHexString(digest); } catch (Exceptione) { e.printStackTrace(); returnnull; } } publicstaticStringbyteToHexString(byte[] bytes) { returnString.valueOf(Hex.encodeHex(bytes)); } publicstaticvoidmain(String[] args) { SHA1sha1=newSHA1(); Stringcontent="测试test"; System.out.println(sha1.encode(content)); } }
(四)AES算法
AES是很常见的对称加密算法,所谓对称加密,就是通过密钥加密后可以再通过密钥解密。我接触过的某个国企现在内部就是采用AES的方式实现集成登陆。第三方系统提供一个接收用户信息的接口,该国企将用户信息AES加密后通过这个接口传递给第三方系统,第三方系统自行实现登陆操作。这里需要注意的是密钥十分重要,如果密钥丢失,就有信息泄漏的风险。
publicclassAES { /*** 将传入的明文转换为密文* @param str* @param pwd* @return*/publicStringencode(Stringstr,Stringpwd) { byte[] result=null; try { KeyGeneratorkgen=KeyGenerator.getInstance("AES"); SecureRandomrandom=SecureRandom.getInstance("SHA1PRNG"); random.setSeed(pwd.getBytes()); kgen.init(128, random); SecretKeysecretKey=kgen.generateKey(); byte[] enCodeFormat=secretKey.getEncoded(); SecretKeySpeckey=newSecretKeySpec(enCodeFormat, "AES"); // 创建密码器Ciphercipher=Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] byteContent=str.getBytes(); result=cipher.doFinal(byteContent); } catch (Exceptione) { returnnull; } returnparseByte2HexStr(result); } /*** 将传入的密文转换为明文* @param str* @param pwd* @return*/publicStringdecode(Stringstr,Stringpwd) { byte[] result=null; byte[] content=parseHexStr2Byte(str); try { KeyGeneratorkgen=KeyGenerator.getInstance("AES"); SecureRandomrandom=SecureRandom.getInstance("SHA1PRNG"); random.setSeed(pwd.getBytes()); kgen.init(128, random); SecretKeysecretKey=kgen.generateKey(); byte[] enCodeFormat=secretKey.getEncoded(); SecretKeySpeckey=newSecretKeySpec(enCodeFormat, "AES"); // 创建密码器Ciphercipher=Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key); result=cipher.doFinal(content); } catch (Exceptione) { e.printStackTrace(); returnnull; } returnnewString(result); } /*** 将二进制转换成十六进制** @param buf* @return*/privateStringparseByte2HexStr(bytebuf[]) { StringBuffersb=newStringBuffer(); for (inti=0; i<buf.length; i++) { Stringhex=Integer.toHexString(buf[i] &0xFF); if (hex.length() ==1) { hex='0'+hex; } sb.append(hex.toUpperCase()); } returnsb.toString(); } /*** 将十六进制转换为二进制** @param hexStr* @return*/privatebyte[] parseHexStr2Byte(StringhexStr) { if (hexStr.length() <1) { returnnull; } byte[] result=newbyte[hexStr.length() /2]; for (inti=0; i<hexStr.length() /2; i++) { inthigh=Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); intlow=Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); result[i] = (byte) (high*16+low); } returnresult; } publicstaticvoidmain(String[] args) { AESaes=newAES(); Stringcontent="测试加密"; // AES的密钥长度最好是16位(不是必须)Stringpwd="javayznbjavayznb"; // 加密System.out.println("加密前:"+content); StringencodeResultStr=aes.encode(content,pwd); System.out.println("加密后:"+encodeResultStr); // 解密StringdecodeResultStr=aes.decode(encodeResultStr,pwd); System.out.println("解密后:"+decodeResultStr); } }
(五)DES
DES也是一种对称加密算法,但是在安全性、效率和灵活性上比AES略差,但是也能保证安全,DES也需要通过密钥进行加密,通过密钥进行解密,因此密钥很重要:
publicclassDES { /*** 将传入的明文转换为密文* @param str* @param pwd* @return*/publicStringencode(Stringstr,Stringpwd) { byte[] result=null; try { DESKeySpeckeySpec=newDESKeySpec(pwd.getBytes()); SecretKeyFactorykeyFactory=SecretKeyFactory.getInstance("DES"); SecretKeykey=keyFactory.generateSecret(keySpec); Ciphercipher=Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] byteContent=str.getBytes(); result=cipher.doFinal(byteContent); } catch (Exceptione) { e.printStackTrace(); returnnull; } returnparseByte2HexStr(result); } /*** 将传入的密文转换为明文* @param str* @param pwd* @return*/publicStringdecode(Stringstr,Stringpwd) { byte[] result=null; byte[] content=parseHexStr2Byte(str); try { DESKeySpeckeySpec=newDESKeySpec(pwd.getBytes()); SecretKeyFactorykeyFactory=SecretKeyFactory.getInstance("DES"); SecretKeykey=keyFactory.generateSecret(keySpec); Ciphercipher=Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key); result=cipher.doFinal(content); } catch (Exceptione) { e.printStackTrace(); returnnull; } returnnewString(result); } /*** 将二进制转换成十六进制** @param buf* @return*/privateStringparseByte2HexStr(bytebuf[]) { StringBuffersb=newStringBuffer(); for (inti=0; i<buf.length; i++) { Stringhex=Integer.toHexString(buf[i] &0xFF); if (hex.length() ==1) { hex='0'+hex; } sb.append(hex.toUpperCase()); } returnsb.toString(); } /*** 将十六进制转换为二进制** @param hexStr* @return*/privatebyte[] parseHexStr2Byte(StringhexStr) { if (hexStr.length() <1) { returnnull; } byte[] result=newbyte[hexStr.length() /2]; for (inti=0; i<hexStr.length() /2; i++) { inthigh=Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); intlow=Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); result[i] = (byte) (high*16+low); } returnresult; } publicstaticvoidmain(String[] args) { DESdes=newDES(); Stringcontent="测试test"; // DES的密钥长度必须是8位(小于8位则会报错,8位之后对加密结果不会产生影响)Stringpwd="javayznb"; // 加密System.out.println("加密前:"+content); StringencodeResultStr=des.encode(content,pwd); System.out.println("加密后:"+encodeResultStr); //解密StringdecodeResultStr=des.decode(encodeResultStr,pwd); System.out.println("解密后:"+decodeResultStr); } }
(六)RSA
RSA是目前最具影响力的公钥加密算法,并且可以用于加密和验签。支付宝支付对接时用的加密方式就是RSA。在RSA中,存在一对密钥,分别称为公钥和私钥,通过私钥由个人保存,公钥可能多人持有。
RSA的主要应用场景就是加密和验签,加密就不用说了,验签是指通过私钥对消息进行签名,使得消息无法篡改和伪造。
加密方式:B传加密数据给A
1、A生成公钥和私钥,私钥自己保留,公钥任何人可以获取。
2、B拿到公钥,将数据通过公钥加密
3、A收到密文,通过私钥解密。
验签方式:A传消息给B 1、A生成公钥和私钥,私钥自己保留,公钥任何人可以获取。
2、A使用私钥对消息加签,并将加签后的消息传给B。
3、B通过公钥验签,如果返回是true则说明消息是A发过来的且未被篡改。
publicclassTestRSA { /*** RSA最大加密明文大小*/privatestaticfinalintMAX_ENCRYPT_BLOCK=117; /*** RSA最大解密密文大小*/privatestaticfinalintMAX_DECRYPT_BLOCK=128; /*** 获取密钥对** @return 密钥对*/publicstaticKeyPairgetKeyPair() throwsException { KeyPairGeneratorgenerator=KeyPairGenerator.getInstance("RSA"); generator.initialize(1024); returngenerator.generateKeyPair(); } /*** 获取私钥** @param privateKey 私钥字符串* @return*/publicstaticPrivateKeygetPrivateKey(StringprivateKey) throwsException { KeyFactorykeyFactory=KeyFactory.getInstance("RSA"); byte[] decodedKey=Base64.decodeBase64(privateKey.getBytes()); PKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(decodedKey); returnkeyFactory.generatePrivate(keySpec); } /*** 获取公钥** @param publicKey 公钥字符串* @return*/publicstaticPublicKeygetPublicKey(StringpublicKey) throwsException { KeyFactorykeyFactory=KeyFactory.getInstance("RSA"); byte[] decodedKey=Base64.decodeBase64(publicKey.getBytes()); X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(decodedKey); returnkeyFactory.generatePublic(keySpec); } /*** RSA加密** @param data 待加密数据* @param publicKey 公钥* @return*/publicstaticStringencrypt(Stringdata, PublicKeypublicKey) throwsException { Ciphercipher=Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); intinputLen=data.getBytes().length; ByteArrayOutputStreamout=newByteArrayOutputStream(); intoffset=0; byte[] cache; inti=0; // 对数据分段加密while (inputLen-offset>0) { if (inputLen-offset>MAX_ENCRYPT_BLOCK) { cache=cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK); } else { cache=cipher.doFinal(data.getBytes(), offset, inputLen-offset); } out.write(cache, 0, cache.length); i++; offset=i*MAX_ENCRYPT_BLOCK; } byte[] encryptedData=out.toByteArray(); out.close(); // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串// 加密后的字符串returnnewString(Base64.encodeBase64String(encryptedData)); } /*** RSA解密** @param data 待解密数据* @param privateKey 私钥* @return*/publicstaticStringdecrypt(Stringdata, PrivateKeyprivateKey) throwsException { Ciphercipher=Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] dataBytes=Base64.decodeBase64(data); intinputLen=dataBytes.length; ByteArrayOutputStreamout=newByteArrayOutputStream(); intoffset=0; byte[] cache; inti=0; // 对数据分段解密while (inputLen-offset>0) { if (inputLen-offset>MAX_DECRYPT_BLOCK) { cache=cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK); } else { cache=cipher.doFinal(dataBytes, offset, inputLen-offset); } out.write(cache, 0, cache.length); i++; offset=i*MAX_DECRYPT_BLOCK; } byte[] decryptedData=out.toByteArray(); out.close(); // 解密后的内容returnnewString(decryptedData, "UTF-8"); } /*** 签名** @param data 待签名数据* @param privateKey 私钥* @return 签名*/publicstaticStringsign(Stringdata, PrivateKeyprivateKey) throwsException { byte[] keyBytes=privateKey.getEncoded(); PKCS8EncodedKeySpeckeySpec=newPKCS8EncodedKeySpec(keyBytes); KeyFactorykeyFactory=KeyFactory.getInstance("RSA"); PrivateKeykey=keyFactory.generatePrivate(keySpec); Signaturesignature=Signature.getInstance("MD5withRSA"); signature.initSign(key); signature.update(data.getBytes()); returnnewString(Base64.encodeBase64(signature.sign())); } /*** 验签** @param srcData 原始字符串* @param publicKey 公钥* @param sign 签名* @return 是否验签通过*/publicstaticbooleanverify(StringsrcData, PublicKeypublicKey, Stringsign) throwsException { byte[] keyBytes=publicKey.getEncoded(); X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(keyBytes); KeyFactorykeyFactory=KeyFactory.getInstance("RSA"); PublicKeykey=keyFactory.generatePublic(keySpec); Signaturesignature=Signature.getInstance("MD5withRSA"); signature.initVerify(key); signature.update(srcData.getBytes()); returnsignature.verify(Base64.decodeBase64(sign.getBytes())); } publicstaticvoidmain(String[] args) { try { // 生成密钥对KeyPairkeyPair=getKeyPair(); StringprivateKey=newString(Base64.encodeBase64(keyPair.getPrivate().getEncoded())); StringpublicKey=newString(Base64.encodeBase64(keyPair.getPublic().getEncoded())); System.out.println("私钥:"+privateKey); System.out.println("公钥:"+publicKey); // RSA加密Stringdata="待加密的文字内容"; StringencryptData=encrypt(data, getPublicKey(publicKey)); System.out.println("加密后内容:"+encryptData); // RSA解密StringdecryptData=decrypt(encryptData, getPrivateKey(privateKey)); System.out.println("解密后内容:"+decryptData); // RSA签名Stringsign=sign(data, getPrivateKey(privateKey)); System.out.println("加签后:"+sign); // RSA验签booleanresult=verify(data, getPublicKey(publicKey), sign); System.out.print("验签结果:"+result); } catch (Exceptione) { e.printStackTrace(); System.out.print("加解密异常"); } } }
(七)总结
关于加密这个问题不管是工作中还是面试中都会被提及,也必须要掌握。不要求加密的代码能手写,但是要知道每种加密算法是干什么的,是什么样的效果。