在项目中,只要涉及到敏感信息,或者对安全有一定要求的场景,都需要对数据进行加密。Java原生API可以实现对称加密和非对称加密,支持常用的加密算法。对称加密对称加密使用单一密钥完成加密和解密,加密和解密使用同一个密钥。对称加密速度很快,通常用于加密大量数据。主流算法有:AES、3DES。生成3DES密钥/***对称加密-3DES算法,替换旧的DES*/SecretKeydesKey=KeyGenerator.getInstance("DESede").generateKey();generateAESkey/***生成AES算法的密钥*/SecretKeyaesKey=KeyGenerator.getInstance("AES").generateKey();保存密钥对称密钥一般存储为二进制SecretKeydesKey=KeyGenerator.getInstance("DESede").generateKey();//二进制密钥byte[]bkey=desKey.getEncoded();//转换为十六进制StringkeyStr=Hex.byteCoverToString(bkey);读取key可以自己生成一个随机字符串,转换成byte数组生成key。注意字节长度不能小于24位。//bkey是字节数组DESedeKeySpeckeySpec=newDESedeKeySpec(bkey);//读取AES密钥SecretKeysrtKey=SecretKeyFactory.getInstance("AES").generateSecret(keySpec);Hex是我自己写的一个实用类。字节数组和十六进制数的转换方法有很多,这里限于篇幅就不贴出来了。可以引入ApacheCommonsCodec工具类,提供Hex、Base64等常用方法。非对称加密非对称加密使用密钥对进行加密。一般采用私密加密,公钥解密,反之亦然。但是加密后的数据自己无法解密,只能由对方解密,可以很好的防止单方密钥泄露。常用的加密算法有:RSA、DSA。生成密钥对/***可以通过RSA或者DSA算法*/KeyPairkeyPair=KeyPairGenerator.getInstance("RSA").generateKeyPair();//私钥PrivateKeyprvKey=keyPair.getPrivate();//公钥PublicKeypubKey=keyPair.getPublic();保存密钥非对称密钥一般以Base64编码保存。//私钥字符串StringprvKeyStr=Base64.getEncoder().encodeToString(prvKey.getEncoded());//公钥字符串StringpubKeyStr=Base64.getEncoder().encodeToString(pubKey.getEncoded());非对称加密对加密内容的长度有限制,不能超过192位。虽然可以使用加密数据流来突破限制,但由于非对称加密的效率不如对称加密,所以一般采用非对称加密来加密对称密钥。读取密钥//从二进制读取私钥byte[]bPrvKey=Base64.getDecoder().decode(prvKeyStr);PKCS8EncodedKeySpecrsaKeySpec=newPKCS8EncodedKeySpec(bPrvKey);PrivateKeyprvKey=KeyFactory.getInstance("RSA")。generatePrivate(rsaKeySpec);//从二进制读取公钥byte[]bPubKey=Base64.getDecoder().decode(pubKeyStr);X509EncodedKeySpecrsaKeySpec1=newX509EncodedKeySpec(bPubKey);PublicKeypubKey=KeyFactory.getInstance("RSA").generatePublic(rsaKeySpec1);当我们访问其他系统时,有时我们给的不是密钥字符串,而是CA证书或PFX密钥文件。HTTS网站使用非对称加密。网站会在上面挂上CA证书,供浏览器下载,并与服务器进行交互,进行加解密。从CA证书中读取公钥//keyFilePath是证书文件f.generateCertificate(fin);PublicKeypubKey=certificate.getPublicKey();}catch(IOException|CertificateExceptione){e.printStackTrace();}从PFX文件中读取密钥根据密钥存储类型,下面可以读取pfx或者jks类型的密钥文件。try(InputStreamis=newFileInputStream(keyFilePath)){//pkcs12或jksKeyStorestore=KeyStore.getInstance("pkcs12");//passwordstore.load(is,"password".toCharArray());//获取所有key别名的列表Enumeratione=store.aliases();//如果有,读取第一个keyif(e.hasMoreElements()){Stringalias=e.nextElement();//私钥PrivateKeyprvKey=(PrivateKey)store.getKey(alias,password.toCharArray());//公钥PublicKeypubKey=store.getCertificate(alias).getPublicKey();//创建密钥对KeyPairkeyPare=newKeyPair(pubKey,prvKey);}}catch(IOException|KeyStoreException|CertificateException|NoSuchAlgorithmException|UnrecoverableKeyExceptione){e.printStackTrace();}密钥文件是用来存放密钥的仓库。Java中的KeyTool工具可以创建jks密钥文件。jks密钥文件中可以存储多个密钥。如果有多个key文件,可以根据别名读取指定的key。加密扩展包-JCE使用JDK8,读取PFX密钥可能会遇到:java.security.InvalidKeyException:illegalKeySize这是因为jdksercurity的jar包的限制,只支持128bit的密钥。jar包如下:$JAVA_HOME/lib/security/local_policy.jar$JAVA_HOME/jre/lib/security/US_export_policy.jar去Oracle官网下载JDK对应的加密扩展包(JCE)。下面是JDK8的JCEhttps://www.oracle.com/java/technologies/javase-jce8-downloads.html解压下载的文件,找到local_policy.jar和US_export_policy.jar覆盖jdk目录,$JAVA_HOME/jre/lib/security和$JAVA_HOME/lib/security下的文件。(有些版本的JDK可能没有$JAVA_HOME/lib/security目录,忽略即可)然后运行代码,可以正常读取256bit的密钥。加密&解密加密或解密//ciphertextStringciphertext=null;{Stringplaintext="encryptedcontent";//keyalgorithm,RSA/DAS/AESDESedCiphercipher=Cipher.getInstance("RSA");//设置加密模式cipher.init(Cipher.ENCRYPT_MODE,prvKey);//加密最后的字节数组byte[]cipherbyte=cipher.doFinal(plaintext.getBytes());//转base64ciphertext=Base64.getEncoder().encodeToString(cipherbyte);logger.info(ciphertext);}{//密钥算法,RSA/DAS/AESDESedCiphercipher=Cipher.getInstance("RSA");//设置解密模式cipher.init(Cipher.DECRYPT_MODE,pubKey);//base64解码得到密文字节数组byte[]cipherbyte=Base64.getDecoder().decode(ciphertext);//解密后的字节数组byte[]plainbyte=cipher.doFinal(cipherbyte);//bytearraytostringStringplaintext=newString(plainbyte);logger.info(plaintext);}签名算法保障仅靠加密不足以保障数据安全。如果密钥泄露,攻击者可以伪造数据,用密钥加密后发送给服务器。因此,还需要对数据进行签名,为安全增加一道屏障。常用的签名算法有:MD5、SHA。非对称加密的公钥/私钥也可以对数据进行签名。MD5或SHA签名//可以传入MD5或SHA算法MessageDigestmd=MessageDigest.getInstance("MD5");//对str数据进行签名byte[]bSign=md.digest(str.getBytes());//转换为十六进制签名字符串StringsignStr=Hex.byteCoverToString(bSign);公钥/私钥签名/***signature*@paramkey*@paramalgorithm*@paramdata*@return*/publicstaticbyte[]sign(PrivateKeykey,Stringalgorithm,byte[]data){try{Signaturesignature=Signature.getInstance(algorithm);signature.initSign(key);signature.update(data);returnsignature.sign();}catch(NoSuchAlgorithmException|SignatureException|InvalidKeyExceptione){thrownewCommonException(e);}}/***签名验证*@paramkey*@paramalgorithm*@paramsign*@paramdata*@return*/publicstaticbooleanverify(PublicKeykey,Stringalgorithm,byte[]sign,byte[]data){try{Signaturesignature=Signature.getInstance(算法);signature.initVerify(key);signature.update(data);returnsignature.verify(sign);}catch(NoSuchAlgorithmException|SignatureException|InvalidKeyExceptione){thrownewCommonException(e);}}设计规范示例1第一次接触Java加密是做一个银联直连支付插件,做了12年。后台加密规范由银联发布。对称加密和非对称加密都被使用。顺序如下:协议版本|加密密钥|消息密文|消息签名协议版本号一般是固定的,比如:1.0.加密密钥是动态生成的对称密钥,为每个请求生成并用于加密消息。加密密钥使用自己的私钥加密,Base64编码。消息密文在使用动态生成的加密密钥加密后进行Base64编码。消息签名是将消息的文本参数按照协议进行排序,并进行MD5计算。例2由于工作需要的内容,我连接了几十个支付系统,它们的加密方式和加密方式都差不多。例子1中的银联加密方案,后面就见不到了。许多公司使用的对称密钥+签名。消息(key1=value1&k2=value2&sign=value)首先将消息按照规则排序(一般key的ascii码是升序或者降序),然后使用事先约定的加密密钥(16进制字符串)进行splicein之后,进行MD5计算。有些还使用RSA密钥签名验证。Java原生API中跨语言加解密的加密算法提供者是SUN或者Oracle。使用相同的Java代码,用Android加密,后台解密不通过。在Android上,算法必须使用RSA/ECB/PKCS1Padding来对应OracleJDK的RSA。主要原因是非对称加密默认的填充方式不同。同样,使用PHP或.NET语言与Java交互时,也会出现无法解密的情况。因此,在一些系统设计中,只使用签名和对称加密,使用HTTPS来保证传输中的内容加密。虽然方便,但实际上并不能解决问题。Java背景的同学可能会注意到,导入第三方提供的加解密SDK时,有的会依赖org.bouncycastle包。这是一个开源项目:BouncyCastle,支持Java和C#语言。官网:www.bouncycastle.orgGit:github.com/bcgit/bc-javaBC包需要下载对应的JDK版本,不同版本的BC包可能会冲突,甚至无法启动项目。可以使用Security.addProvider()手动添加加密提供程序。密钥管理JDK中有一个keytool工具。可用于创建密钥库、管理密钥和证书。keytool创建的keystorekeystore是jks类型的,jkskeystore的读取上面已经解释过了。在tomcat中配置https时,直接使用keystorekeystore即可。nginx或apache仍然使用pfx或pem格式(pem一般只存储私钥)。我们在使用Java的Connection连接https的时候,可能会遇到证书错误,因为证书是可信的,也可能不是可信机构颁发的。JDK有一个密钥库来存储受信任的证书。$JAVA_HOME/jre/lib/security/cacerts我们可以使用keytool来管理cacerts,默认密码:changeit使用keytool在cacerts中添加对应的证书,然后使用Connection访问https就不会报错了。不建议在cacerts中添加证书,会导致程序可移植性差。您可以在阿里云或Let'sEncrypt申请免费证书。