参考:http://netsecurity.51cto.com/art/201108/287971.htm
对称加密和非对称加密。
对称加密中加密和解密用同一个密钥,如 AES/DES。密钥一旦被泄露,那么整体加密信息都将被破解。
非对称加密是生成一个密钥对即私钥和公钥,私钥加密的信息,只有对应的公钥才能解密;反之,公钥加密的信息,只能由对应的私钥才能解密。一定要保护好私钥。从性能上来说,非对称加密相对于对称加密要慢很多,所以一般只用于少量数据的加密,如银行三要素、登录密码等信息。
数字签名
数字签名的意义在于,对传输过来的数据进行校验,确保数据在传输过程中不被修改。提交之前对明文信息通过hash算法获取摘要,然后进行加密即为数字签名。
数字证书
数字证书的意义在于,确保公钥没被篡改。数字证书主要包括:证书的发布机构,证书的有效期,公钥,证书所有者,签名所使用的的算法。
数字证书需要由权威的公司来提供,证书中心CA为公钥做认证,证书中心会对甲方的公钥和一些相关信息加密,生成数字证书。这样甲方在给乙方发送信息时,只需在签名的同时附上数字证书就可以了。乙方在收到信息后,使用CA的公钥解开数字证书,就可以拿到甲方的公钥,然后就证明数字签名是不是甲方的。https就是应用数字证书的一个示例。
数据交互流程分析
1、user向server发送一个通信请求
user->server:你好
2、server向user发送自己的数字证书。证书中有一个公钥用来加密信息,私钥由server持有
server->user:你好,我是server,这里是我的数字证书
3、user收到server的证书后,首先验证这个数字证书到底是不是server的,数字证书有没有什么问题,数字证书如果检查没有问题,就说明数字证书中的公钥确实是server的。检查数字证书后,user会发送一个随机的字符串给server用私钥去加密(检查数字证书,后文介绍)
user->server:向我证明你就是server,这是一个随机字符串
4、server把加密的结果返回给user。
server->user:{一个随机字符串(信息摘要加密)}(用私钥进行RSA加密)
5、user用公钥解密这个返回结果,如果解密结果与之前生成的随机字符串一致,那说明对方确实是私钥的持有者,或者说对方确实是server。 验证server的身份后,user生成一个对称加密算法和密钥,用于后面的通信的加密和解密。这个对称加密算法和密钥,user会用公钥加密后发送给server,别人截获了也没用,因为只有server手中有可以解密的私钥。这样,后面server和user就都可以用对称加密算法来加密和解密通信内容了。
server->user:{OK,已经收到你发来的对称加密算法和密钥!有什么可以帮到你的?}(用密钥进行对称加密)
user->server:{我的用户名是test,密码是123321,我要登录}(用密钥进行对称加密)
server->user:{你好,你已登录成功}(用密钥进行对称加密)
/**
* RSA 工具类。提供加密,解密,生成密钥对等方法。
* 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
*
*/
public class RSAUtils {
public static final String WEB_MODULUS = "abcb0c959f370bcaae9dff448826ccec64616debac81b220d3302296c1f7cc845b83c449c0ae35bfe49869d0924466de5dfdf416998516e95bd4ec0fdfb825eb3c1b2864dad0786f35675f5cf63fbea271cb1079330ef84dab8941d140aa57f93c853e0518051e5751e78512667d8047b82e7e3adae0bf3dcb95c6a7852763db";
public static final String WEB_PRIVATEEXPONENT = "528eb47fd3ab3348beb8c0d44f94dc380bb916266e190c321b6927fa1f8d60dda273c389398db0eb3c3cb1ac2ec049e6247cd6b70e158d200c6ef8bc42110c5d103cd5e5c51ce265f20dbd967231a505b5f179701e7b5e5e6d59530a9f291464c3101a18e7755fc9dfbefc091c52e51528d47a7b87a6f2349fb8c2a9ffc5f4d9";
/**
* * 生成密钥对
* @return KeyPair
* @throws Exception
*/
public static KeyPair generateKeyPair(File cfgFile) throws Exception {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
final int KEY_SIZE = 1024;// 没什么好说的了,这个值关系到块加密的大小,可以更改,但是不要太大,否则效率会低
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
saveKeyPair(keyPair, cfgFile);
return keyPair;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* getKeyPair
* @param cfgFile
* @return
* @throws Exception
*/
public static KeyPair getKeyPair(File cfgFile) throws Exception {
FileInputStream fis = new FileInputStream(cfgFile);
ObjectInputStream oos = new ObjectInputStream(fis);
KeyPair kp = (KeyPair) oos.readObject();
oos.close();
fis.close();
return kp;
}
/**
* saveKeyPair
* @param kp
* @param cfgFile
* @throws Exception
*/
public static void saveKeyPair(KeyPair kp, File cfgFile) throws Exception {
FileOutputStream fos = new FileOutputStream(cfgFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//生成密钥
oos.writeObject(kp);
oos.close();
fos.close();
}
/**
* * 生成公钥
* @param modulus
* @param publicExponent
* @return RSAPublicKey
* @throws Exception
*/
public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
try {
return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* * 生成私钥
* @param modulus
* @param privateExponent
* @return RSAPrivateKey
* @throws Exception
*/
public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) throws Exception {
KeyFactory keyFac = null;
try {
keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (NoSuchAlgorithmException ex) {
throw new Exception(ex.getMessage());
}
RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));
try {
return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
} catch (InvalidKeySpecException ex) {
throw new Exception(ex.getMessage());
}
}
/**
* * 加密
* @param pk 加密的密钥
* @param data 待加密的明文数据
* @return 加密后的数据
* @throws Exception
*/
public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();// 获得加密块大小,如:加密前数据为128个byte,而key_size=1024
// 加密块大小为127
// byte,加密后为128个byte;因此共有2个加密块,第一个127
// byte第二个为1个byte
int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize) {
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
} else {
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
}
// 这里面doUpdate方法不可用,查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
// ByteArrayOutputStream中,而最后doFinal的时候才将所有的byte[]进行加密,可是到了此时加密块大小很可能已经超出了
// OutputSize所以只好用dofinal方法。
i++;
}
return raw;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* 解密
* @param pk 解密的密钥
* @param raw 已经加密的数据
* @return 解密后的明文
* @throws Exception
*/
@SuppressWarnings("static-access")
public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(cipher.DECRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();
ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
int j = 0;
while (raw.length - j * blockSize > 0) {
bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
j++;
}
return bout.toByteArray();
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
/**
* hexStringToBytes
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (null == hexString || "".equals(hexString)) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
String result = "3bcf5f398f5017b66d3d8df2db3d88c9be06f8397b28b6f3b6e430d5abd84b3644d601b477c4292b53d4c28f17b9864803bdd8c93a2c2a4e9b277f89c5c648de96da9cc91d1cdab3e3adcdb837f0f0fbd6be8b6c2e36d735f8aa1674439b11ed3520303cef5c2c886363c63db70b844c95c8284b12bdb48b43ade84b707627a2";
System.out.println("原文加密后为:");
System.out.println(result);
byte[] en_result = hexStringToBytes(result);//new BigInteger(result, 16).toByteArray();
String modulus = WEB_MODULUS;
String privateExponent = WEB_PRIVATEEXPONENT;
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) RSAUtils.generateRSAPrivateKey(new BigInteger(modulus, 16).toByteArray(), new BigInteger(privateExponent, 16).toByteArray());
byte[] de_result = RSAUtils.decrypt(rsaPrivateKey, en_result);
System.out.println("还原密文:");
StringBuffer sb = new StringBuffer();
sb.append(new String(de_result));
System.out.println(sb.reverse().toString().substring(13));
}
}
<!DOCTYPE html>
<html>
<head>
<!-- <base href=""></base> -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="jquery-1.8.3.js"></script>
<script src="jsencrypt.min.js"></script>
<script src="Barrett.js"></script>
<script src="BigInt.js"></script>
<script type="text/javascript">
function webencryptAES(str){
if(!str) return '';
setMaxDigits(130);
var PublicExponent ="10001";
var modulus ="abcb0c959f370bcaae9dff448826ccec64616debac81b220d3302296c1f7cc845b83c449c0ae35bfe49869d0924466de5dfdf416998516e95bd4ec0fdfb825eb3c1b2864dad0786f35675f5cf63fbea271cb1079330ef84dab8941d140aa57f93c853e0518051e5751e78512667d8047b82e7e3adae0bf3dcb95c6a7852763db";
var key = new RSAKeyPair(PublicExponent,"",modulus);
var ramdomTxt= String(new Date().getTime());
if(ramdomTxt.length>8){
ramdomTxt= ramdomTxt.substring(ramdomTxt.length-8);
}else if(ramdomTxt.length<8){
var lStr='';
for(var i=0;i<8-ramdomTxt.length;i++){
lStr+='0';
}
ramdomTxt = lStr+ramdomTxt;
}
ramdomTxt+=String(Math.ceil(Math.random()*100000));
return encryptedString(key,ramdomTxt+String(str));
}
</script>
<script type="text/javascript">
$(function(){
$("#getPwtBtn").click(function(){
var pwdText = $('#pwdText').val();
var test = webencryptAES(pwdText);
$("#resultPwd").text(test);
});
});
</script>
</body>
<body>
<div id="" class="">
<label>密码:</label>
<input id="pwdText" type="text"></input>
<button id="getPwtBtn">获取密文</button>
</div>
<div id="" class="" style="width:60%">
<textfield id="resultPwd"></textfield>
</div>
</body>
</html>
1、通过代码能看出来,公钥长度明显小于私钥
2、遵循:公钥加密-私钥解密,私钥加密-公钥解密的原则
3、公钥和私钥肯定是完全不同
参考:https://blog.youkuaiyun.com/chay_chan/article/details/58605605