非对称加密之RSA是怎么加密的
对称加密和非对称加密
在说 RSA 之前,我们得先来说说这个什么事对称加密,什么又是非对称加密?
对称加密 指的就是加密和解密使用同一个秘钥,所以叫对称加密。对称加密只有一个秘钥,作为私钥。
非对称加密 指的是:加密和解密使用不同的秘钥,一把作为公开的公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。
那么对称加密和非对称加密之间又有什么区别呢? 对称加密中加密和解密使用的秘钥是同一个;非对称加密中采用两个密钥,一般使用公钥进行加密,私钥进行解密。 对称加密解密的速度比较快,非对称加密和解密花费的时间长、速度相对较慢。 对称加密的安全性相对较低,非对称加密的安全性较高。
今天我们来讲的就是非对称加密中的 RSA 加密。 RSA加密是什么?
RSA加密是一种非对称加密。可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。
通常情况下个人保存私钥,公钥是公开的(可能同时多人持有)。
虽然私钥是根据公钥决定的, 但是,我们是没有办法根据公钥来推算出私钥来的。
为提高保密强度,RSA密钥至少为500位长。这就使加密的计算量很大。为减少计算量,在传送信息时,常采用传统加密方法与公开密钥加密方法相结合的方式,即信息采用改进的DES或IDEA对话密钥加密,然后使用RSA密钥加密对话密钥和信息摘要。对方收到信息后,用不同的密钥解密并可核对信息摘要 RSA的加密过程
RSA的加密过程其实并不复杂,
(1)A生成一对密钥(公钥和私钥),私钥不公开,A自己保留。公钥为公开的,任何人可以获取。
(2)A传递自己的公钥给B,B用A的公钥对消息进行加密。
(3)A接收到B加密的消息,利用A自己的私钥对消息进行解密。
在这个过程中,只有2次传递过程,第一次是A传递公钥给B,第二次是B传递加密消息给A,即使都被其他人截获,也没有危险性,因为只有A的私钥才能对消息进行解密,防止了消息内容的泄露。
但是大家有没有想过,如果我们的消息被截获了,虽然没有被解密出来,但是如果说我们的公钥被拦截,然后将假指令进行加密,然后传递给A,这不就凉凉了?那数据是不是就不能称之为安全了?
不,RSA还有签名的过程。
签名过程如下:
(1)A生成一对密钥(公钥和私钥),私钥不公开,A自己保留。公钥为公开的,任何人可以获取。
(2)A用自己的私钥对消息加签,形成签名,并将加签的消息和消息本身一起传递给B。
(3)B收到消息后,在获取A的公钥进行验签,如果验签出来的内容与消息本身一致,证明消息是A回复的。
但是问题又来了,虽然截获的消息不能被篡改,但是消息的内容可以利用公钥验签来获得,并不能防止泄露。
那么应该怎么用呢?
其实这就显的并不是很好理解了 我们是不是可以这么设计:
A和B都有一套自己的公钥和私钥,当A要给B发送消息时,先用B的公钥对消息加密,再对加密的消息使用A的私钥加签名,达到既不泄露也不被篡改,更能保证消息的安全性。
那么 Java 代码怎么实现 RSA 的呢?代码如下: 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("加解密异常"); } } }
同样,当我们看到 RSA 的 Java实现的时候,我们就看到了他的缺点,上来就先定义最大加密明文大小和最大解密密文大小,那么这个 117 是怎么来的?
Java 默认的 RSA 加密实现不允许明文长度超过密钥长度减去 11(单位是字节,也就是 byte)。也就是说,如果我们定义的密钥(我们可以通过 java.security.KeyPairGenerator.initialize(int keysize ) 来定义密钥长度)长度为 1024(单位是位,也就是 bit),生成的密钥长度就是 1024位 / 8位/字节 = 128字节,那么我们需要加密的明文长度不能超过 128字节 -11 字节 = 117字节。也就是说,我们最大能将 117 字节长度的明文进行加密,否则会出问题( javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes 的异常)。
那么我们使用 RSA 的时候应该注意什么内容呢?
1.加密的系统不要具备解密的功能,否则 RSA 可能不太合适,
因为这样即使黑客攻破了加密系统,他拿到的也只是一堆无法破解的密文数据。
2.生成密文的长度和明文长度无关,但明文长度不能超过密钥长度
不管明文长度是多少,RSA 生成的密文长度总是固定的。但是明文长度不能超过密钥长度。
也就是上面说的那个117字节数,不然就只能等着出现异常了。
吉林市四大名山都叫什么?龙潭山在吉林省吉林市东部,与小白山朱雀山及小团子山合称吉林四大名山。风景幽美,是个度假胜地。龙潭山是以位于西北角山麓一个称为龙潭的秀丽水池而得名。龙潭约长五十三公尺,约宽二十六公尺
河北有哪些三本学校?菜鸟来谈谈吧,欢迎大家关注菜鸟!O现在已经慢慢取消三本院校或者独立院校这种说法了。现在的福独立院校要么转成民办的二本院校,要么成为职业类的技术学院。总之已经开始在取消了,大家都可以
对方手机号加不上微信是为什么?有对方的手机号,但却无法通过搜索手机号添加对方的微信,可能的原因有一下几种1。对方的微信没有绑定手机号最早注册微信的时候,笔者记得直接用QQ号登录即可,时间有点久了,记得不是很确切
为什么阴阳师的小小黑可以万攻满暴代替满暴高暴伤?其他式神不可以吗?哪个收益更高?小小黑万攻好还是高爆伤好?前提条件肯定是带针女御魂,针女御魂是暴击有40概率触发造成对方生命上限10,不超过攻击的120。黑童子大招特性生命低于70,2段99群攻生命低于40,3段
生理需求与心理需求哪个重要还是都重要?人活在世上,是会有各种需求,各种需求都得到满足,那才叫活得痛苦!首先是物质需求,人要穿衣吃饭,无饭吃,要饿死,无衣穿,要冻死,所以,人把物质摆第一。什么是精神需求?非物质外的一种需
马尚如果恢复理想的话,能否赶上第三阶段广东的比赛?马尚如果恢复理想的话,能否赶上第三阶段广东的比赛?先要搞清楚马尚布鲁克斯是什么伤病,马尚是跟腱断裂,是篮球运动员的治命伤,这和重伤要想恢复的好,也是凤毛麟角,很少很少,何况马尚布鲁
怎么做能让一台用了8年的低配电脑保持流畅不卡顿?我家的电脑是2008年自己组装的,现在还在服役中,去年升级过一次硬件,现在运行挺流畅的。下文具体说一说。升级内存电脑运行速率的主要瓶颈是内存,无法匹配CPU的运行速率和越来越大的软
有哪些化妆品和护肤品,你知道好用,但是用过之后才发现竟然这么好用?买东西最怕一头热!这边刚看完别人的推荐,那边就点开了某宝下单,等东西到了就后悔作为一个某宝VIP包年大户,我买东西必看人家的评论和使用体验!尤其是商品下面的评论,至少翻三页,只要差
农村的空巢老人没有退休金,儿女出外打工,生活怎么解决?感谢邀请农村的空巢老人没有退休金,儿女外出打工,生活确实比较困难,幸亏国家对年满六十岁的老年人有照顾补贴,还有点承包地,不需要交农业税,国家还可以补贴,儿女们再给一点生活费用,这样
为什么汽车过度保养也会对车不好?车主们都知道车要三分修,七分养,日常用车中,保养必不可少。不过很多车主因为爱车心切,难免落入过度保养的陷阱。那么,有哪些需要避免的车辆过度保养误区呢?机油频繁更换机油更换要根据车辆
怎么评价粉身碎骨全不怕,要留清白在人间的于谦?德高望重我认为于谦是一个大英雄。在瓦剌进犯北京,明英宗在土木堡被俘虏,二十多万明军精锐丧失殆尽。瓦剌首领也先认为抓住明英宗是奇货可居,以英宗做人质要挟明朝各地守城将领就范,很多城池