密码学的知识
明文:
明文指的是未被加密过的原始数据。
密文:
明文被某种加密算法加密之后,会变成密文,从而确保原始数据的安全。密文也可以被解密,得到原始的明文。
密钥:
密钥是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥,分别应用在对称加密和非对称加密上。
对称加密:
对称加密又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密,常见的对称加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。
其加密过程如下:明文 + 加密算法 + 私钥 => 密文
解密过程如下:密文 + 解密算法 + 私钥 => 明文
对称加密中用到的密钥叫做私钥,私钥表示个人私有的密钥,即该密钥不能被泄露。
其加密过程中的私钥与解密过程中用到的私钥是同一个密钥,这也是称加密之所以称之为“对称”的原因。由于对称加密的算法是公开的,所以一旦私钥被泄露,那么密文就很容易被破解,所以对称加密的缺点是密钥安全管理困难。
非对称加密:
非对称加密也叫做公钥加密。非对称加密与对称加密相比,其安全性更好。对称加密的通信双方使用相同的密钥,如果一方的密钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。
被公钥加密过的密文只能被私钥解密,过程如下:
明文 + 加密算法 + 公钥 => 密文, 密文 + 解密算法 + 私钥 => 明文
被私钥加密过的密文只能被公钥解密,过程如下:
明文 + 加密算法 + 私钥 => 密文, 密文 + 解密算法 + 公钥 => 明文
由于加密和解密使用了两个不同的密钥,这就是非对称加密“非对称”的原因。
非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
在非对称加密中使用的主要算法有:RSA、Elgamal、Rabin、D-H、ECC(椭圆曲线加密算法)等。
加密解密中的安全隐患
B有两个密钥,一个叫公钥Public Key,一个叫私钥Private Key,B的公钥是公之于众的,所有需要的人都可以获得公钥,但B的私钥是自己私有的。密钥用来加密信息,将一段可以理解阅读的明文信息,用密钥进行加密,变成一段‘乱码。因此,只有持有正确密钥的人,才能重新将这段加密后的信息,也就是‘乱码’,恢复成可以理解阅读的真实信息。B的两个密钥,公钥和私钥都可以将信息进行加密,并且能用对应的密钥将信息解码,也就是说,如果用B的公钥将信息加密,那么可以并且只可以用B的私钥将信息解码,反之,如果用B的私钥将信息加密,那么可以且只可以用Bob的公钥将信息解码!所以B就可以利用自己的公钥和私钥进行信息的加密传输!
比如,A想要和B进行通信,考虑到信息的安全性,A可以利用B公之于众的公钥对所要传输的信息进行加密。这样,B收到信息后,就可以用自己的私钥对信息进行解码。假设此时有人窃取了A传给B的信息,但是由于没有B的私钥,无法对信息进行解码,所以即使窃取了信息,也无法阅读理解。
虽然别人无法获取消息的具体内容(没有私钥去解密),但是却可以对信息进行篡改,破坏原有的信息,这样B收到被篡改的信息之后,再用自己的私钥进行解码,就会与A本来想要传达的信息出现不一致,相当于破坏了A和B的信息传输.
数字签名和数字证书
上面的问题可以用数字签名和数字证书解决。接收方接收到信息之后,可以判断信息是否被破坏过,如果没有被破坏,就可以正确的解码,如果被破坏,就直接丢弃。
数字签名是如何实现对完整性保证的呢?关键技术就是哈希技术(hash)。
首先,B先对将要传输的信息进行hash,得到一串独一无二的字符,通常把hash之后的内容称为信息摘要(message digest)。因为hash往往是不可逆的,所以无法根据hash后的内容推断出hash前的原文。而且不同的原文的hash结果差异也是巨大甚至毫无规律的。
然后,B同时将hash后的信息摘要,用自己的私钥进行加密,这样就保证只有B的公钥才能对信息摘要进行正确的解码,保证了信息摘要一定是来自B的,也就是起到了一个独一无二的签名的作用,加密后的信息摘要实际就是数字签名的内容。
最后,B再将数字签名附加到原文信息的后面,这样就形成了一个完整的带数字签名的信息报文,发送给C
C接收到信息之后,先利用B的公钥对数字签名进行解码,得到信息摘要,如果成功解码,就说明数字签名是来自B的,因为数字签名是B利用自己的私钥进行加密的,只有B的公钥可以进行解密。然后,C将信息原文进行hash得到自己hash的信息摘要,再与之前解码数字签名得到的信息摘要进行对比,如果相同,就说明原文信息是完整的,没有被篡改,反之,则确认信息被破坏了。
现在利用公钥和私钥以及数字签名,我们可以保证信息传输过程中的私密性和完整性。但还存在一个问题,就是公钥分发的问题,即如何保证B的公钥被正确的分发给了A或者C等人呢?(如何E劫取了B发给C的公钥,然后私自伪造了一个假的公钥并加上B的名字,发给了C,这样就导致C永远实际上就是在跟E通信),现在的问题就是,C如何确认收到的公钥真的是B的公钥,而不是别人伪造的!
数字证书,实际上就是引入了一个独立的第三方机制,公安局作为一个独立的第三方,给我们每个人创建了一个身份证,当我们需要验证身份证的真伪的时候,我们只需要找这个独立的第三方提供的真伪鉴别服务就可以验证身份证的真伪。
所以,相似的我们解决公钥分发问题的思路也就是引入一个独立的权威的第三方机构。假设现在有一个数字证书的权威认证中心,这个中心会给B创建一个数字证书,这个数字证书包括了B的一些信息以及B的公钥。那么,此时想要跟B进行通信的人,就可以检查B的数字证书,然后向权威的数字证书的认证中心,去认证这是不是真实的B的数字证书,如果是,就可以从数字证书中获取到B的公钥,然后进行安全的通信。如果在数字证书的认证中心中查询不到信息,那么就说明这样的通信方是不安全的,是不值得信任的!另一方面,数字证书除了解决了公钥分发和身份认证的问题,还加强了安全性。
现在的https协议就是基于非对称加密技术的,首先客户端往服务器发起加密请求,于是服务器会用自己的私钥加密网页,然后连同自己的数字证书(这个证书里包含证书颁发机构做的签名),然后客户端浏览器会查看操作系统认证的受信任的证书颁发机构查找公钥,验证证书是否被篡改,如果被篡改会发出警告。如果查找不到受信任的证书颁发机构,浏览器也会发出警告。
B和C的现在的通信过程
现在B想要和C进行通信,首先就要告知C自己的公钥,B先向C发送自己的数字证书,C收到数字证书后,会向权威的数字证书认证中心进行认证,确认是否是B的数字证书。(这个认证的过程,实际上也是通过公钥和私钥的机制,C会根据数字证书的类别,查找发布这个数字证书的中心的公钥,然后用相应的公钥对证书进行家解码,如果能正确解码则说明这个数字证书确实是此中心颁布的,然后根据解码后的信息验证是否是B的数字证书,最后从解码后的信息中,获取B的公钥)。
然后,C可以利用B的公钥对B的数字签名进行解码,验证是否是B的数字签名,如果能正确解码,就说明数字签名是由B的私钥进行加密的。然后就进行完整性的验证,将信息原文进行hash,得到信息摘要,并与数字签名解码后得到的信息摘要进行对比,如果一致,就说明信息是完整的没有被篡改的!
RSA加密
RSA加密是一种非对称加密。可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对一极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。
RSA加密、签名
加密和签名都是为了安全性考虑,但略有不同。常有人问加密和签名是用私钥还是公钥?其实都是对加密和签名的作用有所混淆。简单的说,加密是为了防止信息被泄露,而签名是为了防止信息被篡改,在实际应用中,要根据情况使用,也可以同时使用加密和签名,比如A和B都有一套自己的公钥和私钥,当A要给B发送消息时,先用B的公钥对消息加密,再对加密的消息使用A的私钥加签名,达到既不泄露也不被篡改,更能保证消息的安全性,总结:公钥加密、私钥解密、私钥签名、公钥验签。
RSA的代码
使用RSA进行加密解密
package com.pingan.anhui.utils;import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;import javax.crypto.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Enumeration;public class RSAUtil {private static Logger log = LoggerFactory.getLogger(RSAUtil.class);private PublicKey publicKey; // 对方公钥private PrivateKey privateKey; // 己方私钥private BouncyCastleProvider provider = new BouncyCastleProvider();/*** RSA最大加密明文大小*/// private static final int MAX_ENCRYPT_BLOCK = 117;/*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;/*** 签名算法*/private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; //SHA1withRSA MD5withRSA/*** 根据公私钥字符串构造加解密对象* * @param publicKeyStr* @param privateKeyStr*/public RSAUtil(String publicKeyStr, String privateKeyStr) {if (StringUtils.isNotBlank(publicKeyStr)) {this.publicKey = string2PublicKey(publicKeyStr);}if (StringUtils.isNotBlank(privateKeyStr)) {this.privateKey = string2PrivateKey(privateKeyStr);}}/*** 根据公钥证书路径及私钥证书路径和私钥证书密码构造加密解密对象* * @param publicKeyPath 公钥证书路径* @param privateKeyPath 私钥证书路径* @param privateKeyPwd 私钥证书密码*/public RSAUtil(String publicKeyPath, String privateKeyPath, String privateKeyPwd) {try {if (StringUtils.isNotBlank(publicKeyPath)) {this.publicKey = getPublicKeyFromX509(publicKeyPath);}if (StringUtils.isNotBlank(privateKeyPath)) {this.privateKey = getPrivateKey(privateKeyPath, privateKeyPwd);}} catch (Exception e) {log.error("RSA init exception:{}", e.getMessage(), e);throw new RuntimeException(String.format("RSA init exception:[%s]", e.getMessage()), e);}}/*** RSA公钥加密** @param rawData* @param charset* @return*/public String encryptByRSA(String rawData, String charset) {try {Cipher encodeCipher = Cipher.getInstance(publicKey.getAlgorithm(), provider);encodeCipher.init(Cipher.ENCRYPT_MODE, publicKey);int blockSize = encodeCipher.getBlockSize();// 127byte[] data = rawData.getBytes(charset);int data_length = data.length;// 明文数据大小int outputSize = encodeCipher.getOutputSize(data_length);// 输出缓冲区大小// 计算出块的数量int blocksSize = (data_length + blockSize - 1) / blockSize;byte[] raw = new byte[outputSize * blocksSize];int i = 0;while (data_length - i * blockSize > 0) {if (data_length - i * blockSize > blockSize) {encodeCipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);} else {encodeCipher.doFinal(data, i * blockSize, data_length - i * blockSize, raw, i * outputSize);}i++;}return Base64.encode(raw);} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | UnsupportedEncodingException| ShortBufferException | IllegalBlockSizeException | BadPaddingException e) {log.error("RSA.encryptByRSA 公钥对数据{}使用字符集{}加密失败", rawData, charset, e);throw new RuntimeException(String.format("公钥对数据[%s]使用字符集[%s]加密失败", rawData, charset), e);}}/*** 功能:私钥RSA解密 作者: 澈力格尔很 创建日期:2019-7-16* * @param encodeData* @param charset* @return*/public String decryptByRSA(String encodeData, String charset) {try (ByteArrayOutputStream bout = new ByteArrayOutputStream(MAX_DECRYPT_BLOCK)) {Cipher decodeCipher = Cipher.getInstance(privateKey.getAlgorithm(), provider);decodeCipher.init(Cipher.DECRYPT_MODE, privateKey);int blockSize = decodeCipher.getBlockSize();int j = 0;byte[] raw = Base64.decode(encodeData);while (raw.length - j * blockSize > 0) {bout.write(decodeCipher.doFinal(raw, j * blockSize, blockSize));j++;}byte[] decryptedData = bout.toByteArray();return new String(decryptedData, charset);} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException| BadPaddingException | IOException e) {log.error("RSA.decryptByRSA 私钥对数据{}使用字符集{}加密失败", encodeData, charset, e);throw new RuntimeException(String.format("私钥对数据[%s]使用字符集[%s]解密失败", encodeData, charset), e);}}/*** 使用私钥签名* * @param rawData* @param charset* @return*/public String sign(String rawData, String charset) {try {Signature signSignature = Signature.getInstance(SIGNATURE_ALGORITHM);signSignature.initSign(privateKey);signSignature.update(rawData.getBytes(charset));return Base64.encode(signSignature.sign());} catch (Exception e) {log.error("RSA.sign 使用私钥对数据{}进行{}签名失败", rawData, charset, e);throw new RuntimeException(String.format("使用私钥对数据[%s]进行[%s]签名失败", rawData, charset), e);}}/*** 功能:公钥验签* * @param rawData* @param sign* @param charset* @return*/public boolean verify(String rawData, String sign, String charset) {try {Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);signature.initVerify(publicKey);signature.update(rawData.getBytes(charset));return signature.verify(Base64.decode(sign));} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException e) {log.error("RSA.verify 公钥使用签名串{}对数据{}进行{}验签失败", sign, rawData, charset, e);throw new RuntimeException(String.format("公钥使用签名串[%s]对数据[%s]进行[%s]验签失败", sign, rawData, charset), e);}}/***密钥转成字符串* * @param key* @return*/public String keyToString(Key key) {try {return Base64.encode(key.getEncoded());} catch (Exception e) {log.error("RSA.keyToString 输出密钥{}字符串失败", key.getFormat(), e);throw new RuntimeException(String.format("输出密钥[%s]字符串失败", key.getFormat()), e);}}/*** 功能:公钥字符串转公钥* * @param publicKeyStr* @return*/public PublicKey string2PublicKey(String publicKeyStr) {PublicKey publicKey = null;X509EncodedKeySpec bobPubKeySpec = null;try {bobPubKeySpec = new X509EncodedKeySpec(Base64.decode(publicKeyStr));// RSA对称加密算法KeyFactory keyFactory = KeyFactory.getInstance("RSA");// 取公钥匙对象publicKey = keyFactory.generatePublic(bobPubKeySpec);} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {log.error("RSA.string2PublicKey 公钥{}加载失败", publicKeyStr, e);throw new RuntimeException(String.format("公钥[%s]加载失败", publicKeyStr), e);}return publicKey;}/*** 功能: 私钥字符串转私钥* * @param privateKyeStr* @return*/public PrivateKey string2PrivateKey(String privateKyeStr) {PrivateKey privateKey = null;PKCS8EncodedKeySpec priPKCS8 = null;try {priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKyeStr));KeyFactory keyFactory = KeyFactory.getInstance("RSA");privateKey = keyFactory.generatePrivate(priPKCS8);} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {log.error("RSA.string2PrivateKey 私钥{}加载失败", privateKyeStr, e);throw new RuntimeException(String.format("私钥[%s]加载失败", privateKyeStr), e);}return privateKey;}/*** 功能: 根据路径加载公钥* * @param publicKeyPath* @return*/private PublicKey getPublicKeyFromX509(String publicKeyPath) {try (InputStream fin = getInputStream(publicKeyPath)) {CertificateFactory f = CertificateFactory.getInstance("X.509");X509Certificate certificate = (X509Certificate) f.generateCertificate(fin);return certificate.getPublicKey();} catch (IOException | CertificateException e) {log.error("RSA.getPublicKeyFromX509 加载公钥证书路径{}失败", publicKeyPath, e);throw new RuntimeException(String.format("加载公钥证书路径[%s]失败", publicKeyPath), e);}}private InputStream getInputStream(String keyFilePath) {try {return new ClassPathResource(keyFilePath).getInputStream();} catch (IOException e) {log.error("RSA.getInputStream 文件{}加载失败", keyFilePath, e);throw new RuntimeException(String.format("文件[%s]加载失败", keyFilePath), e);}}/*** 功能: 加载私钥证书* * @param privateKeyPath* @param password* @return*/private PrivateKey getPrivateKey(String privateKeyPath, String password) {try (InputStream is = getInputStream(privateKeyPath)) {KeyStore store = KeyStore.getInstance("pkcs12");char[] passwordChars = password.toCharArray();store.load(is, passwordChars);Enumeration<String> e = store.aliases();if (e.hasMoreElements()) {String alias = e.nextElement();return (PrivateKey) store.getKey(alias, passwordChars);}throw new RuntimeException(String.format("无法加载私钥证书路径[%s]及密码[%s],请核对证书文件及密码", privateKeyPath, password));} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException| UnrecoverableKeyException e) {log.error("RSA.getInputStream 加载私钥证书路径{}及密码{}失败", privateKeyPath, password, e);throw new RuntimeException(String.format("加载私钥证书路径[%s]及密码[%s]失败", privateKeyPath, password), e);}}
}
使用MD5Util进行签名和验签
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;/*** MD5加密 不可逆*/
public class MD5Util {private MD5Util() {throw new UnsupportedOperationException("cannot be instantiated");}/*** MD5加密*/public static String encryptMD5(byte[] data) throws NoSuchAlgorithmException {MessageDigest md5 = MessageDigest.getInstance("MD5");//md5.update(data);byte[] resultBytes = md5.digest(data);//.digest();String resultString = fromBytesToHex(resultBytes);return resultString;}public static String fromBytesToHex(byte[] resultBytes) {StringBuilder builder = new StringBuilder();for (int i = 0; i < resultBytes.length; i++) {if (Integer.toHexString(0xFF & resultBytes[i]).length() == 1) {builder.append("0").append(Integer.toHexString(0xFF & resultBytes[i]));} else {builder.append(Integer.toHexString(0xFF & resultBytes[i]));}}return builder.toString();}}
工具类:整合RSAUtil和MD5Utils提供方法
public class EncryptUtil {private static Logger logger =LoggerFactory.getLogger(RSAUtil.class);public static String signMsg(String xml,String charset) throws Exception {String infoSign = MD5Util.encryptMD5(Base64.str2Bytes(xml, charset));logger.debug("签名结果:{}",infoSign);return infoSign;}public static boolean verifySign(String infoContent,String infoSign,String charset){try {String innerMd5 = MD5Util.encryptMD5(Base64.str2Bytes(infoContent, charset));return innerMd5.equals(infoSign);} catch (Exception e) {logger.error("验签异常!", e);}return false;}public static String encryptByRSA(String infoContent ,RSAUtil rsa,String charset ) throws Exception {String ecnryptData = rsa.encryptByRSA(infoContent, charset);logger.debug("加密结果:{}",ecnryptData);return ecnryptData;}
//public static String decryptByRSA(String infoContent ,RSAUtil rsa,String charset) throws Exception {String InfoContext = rsa.decryptByRSA(infoContent, charset);logger.debug("解密结果:{}",InfoContext);return InfoContext;}}
使用方法:发送请求的时候,对请求进行加密+签名,响应的时候解密并验签
//对请求进行加密和签名
public Request buildRequest(EarlyRepaymentTrialRequest trialRequest) throws Exception {final Request request = new Request();request.setRequestSeqNo(buildRequestNo(SlConfigure.getCorporateCode()));request.setCorporateCode(SlConfigure.getCorporateCode());request.setInterfaceId(PREPAY_TRIAL);request.setProductType(SlConfigure.getProductType());final String requestJson = objectMapper.writeValueAsString(trialRequest);log.info("call shilong request param : requestJson= {}", requestJson);final String infoContent = EncryptUtil.encryptByRSA(requestJson, buildRsaUtil(), CHARSET);final String infoSign = EncryptUtil.signMsg(requestJson, CHARSET);log.info("sign result : {}", infoSign);request.setInfoContent(infoContent);request.setInfoSign(infoSign);request.setBankCode(SlConfigure.getBankCode());return request;}//对响应进行解密和验签
public EarlyRepaymentTrialResponse parseResponse(Response response) throws Exception {final EarlyRepaymentTrialResponse trialResponse = new EarlyRepaymentTrialResponse();if (SUCCESS_CODE.equals(response.getRespCode())) {final String infoContent = response.getInfoContent();final String infoSign = response.getInfoSign();final String decrypt = EncryptUtil.decryptByRSA(infoContent, buildRsaUtil(), CHARSET);if (!EncryptUtil.verifySign(decrypt,infoSign,CHARSET)) {log.error("Valid sign : {} failure : {}", infoSign, decrypt);return trialResponse;}final EarlyRepayment earlyRepayment = objectMapper.readValue(decrypt, EarlyRepayment.class);if (SUCCESS_CODE.equals(earlyRepayment.getRetCode())) {trialResponse.setEarlyRepayment(earlyRepayment);trialResponse.setErrorCode(earlyRepayment.getRetCode());trialResponse.setErrorMsg(earlyRepayment.getRetMsg());}else if (FAILURE_CODE.equals(earlyRepayment.getRetCode())) {trialResponse.setErrorCode(earlyRepayment.getRetCode());trialResponse.setErrorMsg(earlyRepayment.getRetMsg());}}else {trialResponse.setErrorCode(response.getRespCode());trialResponse.setErrorMsg(response.getRespMsg());}return trialResponse;}
PS:RSA加密对明文的长度有所限制,规定需加密的明文最大长度=密钥长度-11(单位是字节,即byte),所以在加密和解密的过程中需要分块进行。而密钥默认是1024位,即1024位/8位-11=128-11=117字节。所以默认加密前的明文最大长度117字节,解密密文最大长度为128字。那么为啥两者相差11字节呢?是因为RSA加密使用到了填充模式(padding),即内容不足117字节时会自动填满,用到填充模式自然会占用一定的字节,而且这部分字节也是参与加密的。
密钥长度的设置就是上面例子的第32行。可自行调整,当然非对称加密随着密钥变长,安全性上升的同时性能也会有所下降。
PS:其他的RSAUtils的方法
import java.io.ByteArrayOutputStream;
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.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;public class TestRSA {/*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;/*** 获取密钥对* * @return 密钥对*/public static KeyPair getKeyPair() throws Exception {KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");generator.initialize(1024);return generator.generateKeyPair();}/*** 获取私钥* * @param privateKey 私钥字符串* @return*/public static PrivateKey getPrivateKey(String privateKey) throws Exception {KeyFactory keyFactory = KeyFactory.getInstance("RSA");byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);return keyFactory.generatePrivate(keySpec);}/*** 获取公钥* * @param publicKey 公钥字符串* @return*/public static PublicKey getPublicKey(String publicKey) throws Exception {KeyFactory keyFactory = KeyFactory.getInstance("RSA");byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);return keyFactory.generatePublic(keySpec);}/*** RSA加密* * @param data 待加密数据* @param publicKey 公钥* @return*/public static String encrypt(String data, PublicKey publicKey) throws Exception {Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE, publicKey);int inputLen = data.getBytes().length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offset = 0;byte[] cache;int i = 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为标准转化成字符串// 加密后的字符串return new String(Base64.encodeBase64String(encryptedData));}/*** RSA解密* * @param data 待解密数据* @param privateKey 私钥* @return*/public static String decrypt(String data, PrivateKey privateKey) throws Exception {Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, privateKey);byte[] dataBytes = Base64.decodeBase64(data);int inputLen = dataBytes.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offset = 0;byte[] cache;int i = 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();// 解密后的内容 return new String(decryptedData, "UTF-8");}/*** 签名* * @param data 待签名数据* @param privateKey 私钥* @return 签名*/public static String sign(String data, PrivateKey privateKey) throws Exception {byte[] keyBytes = privateKey.getEncoded();PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance("RSA");PrivateKey key = keyFactory.generatePrivate(keySpec);Signature signature = Signature.getInstance("MD5withRSA");signature.initSign(key);signature.update(data.getBytes());return new String(Base64.encodeBase64(signature.sign()));}/*** 验签* * @param srcData 原始字符串* @param publicKey 公钥* @param sign 签名* @return 是否验签通过*/public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {byte[] keyBytes = publicKey.getEncoded();X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance("RSA");PublicKey key = keyFactory.generatePublic(keySpec);Signature signature = Signature.getInstance("MD5withRSA");signature.initVerify(key);signature.update(srcData.getBytes());return signature.verify(Base64.decodeBase64(sign.getBytes()));}public static void main(String[] args) {try {// 生成密钥对KeyPair keyPair = getKeyPair();String privateKey = new String(Base64.encodeBase64(keyPair.getPrivate().getEncoded()));String publicKey = new String(Base64.encodeBase64(keyPair.getPublic().getEncoded()));System.out.println("私钥:" + privateKey);System.out.println("公钥:" + publicKey);// RSA加密String data = "待加密的文字内容";String encryptData = encrypt(data, getPublicKey(publicKey));System.out.println("加密后内容:" + encryptData);// RSA解密String decryptData = decrypt(encryptData, getPrivateKey(privateKey));System.out.println("解密后内容:" + decryptData);// RSA签名String sign = sign(data, getPrivateKey(privateKey));// RSA验签boolean result = verify(data, getPublicKey(publicKey), sign);System.out.print("验签结果:" + result);} catch (Exception e) {e.printStackTrace();System.out.print("加解密异常");}}
}