Java安全系列-RSA加密
2017-11-16 11:08
211 查看
Java安全系列-RSA加密
在现在的信息安全体系中,加密已经成为了一个常识。在诸多场合下为了数据安全和可靠,很多情况下需要使用加密,如:密码体系、安全邮件、网络传输等等方面。在现有的加密算法中,大致可以分为两大类:对称加密和非对称加密。
在对称加密中,较为典型的算法为:DES、IDEA、AES等;
在非对称加密体系中,其中较为常用的算法有:RSA加密体系,ECC加密等。
在上述的加密过程中,都需要借助密钥。在对称加密算法体系中,其加密解密,需要使用相同的密钥进行加密和解密。在该过程中,产生密钥E,同时有明文数据P,为此采用相应的对称算法对其进行加密,会得到密文C。该过程则为对称加密过程。在加密后,若需要对数据进行解密,则同样需要使用密钥E,对密文C进行解密,得到明文P,该过程为解密过程。
在对称加密中,有:加密速度快、计算量小等优点,并可公开算法,只需做到密钥的管理,即可保证加密的安全性。但是在对称加密体系中,密钥是多种多样的,并且为了保证数据可靠,必须做到严格的密钥管理,会对用户造成负担,从而造成数据的不安全性。
在非对称加密体系中,需要协商产生密钥对:私钥-公钥,产生一对密钥后,公钥可以公开使用,交由客户端或用户使用,令客户在加密时,采用公钥进行加密,产生密文,交由私钥拥有者进行使用。拥有私钥的一方,接收到经过公钥加密的密文后,使用只有其保留的私钥对密文进行解密,从而可以得到明文。该过程则为普遍的“公钥加密,私钥解密”的过程体系。并且在非对称加密中,可以使用“私钥加密,公钥解密”的过程,来实现数字签名,因为私钥加密,可以实现不可否认性。由于在非对称加密体系中,只有一部分有权限的用户,可以保留私钥,其他用户只能保留公钥,公钥可以公开在网络上进行传输。因此用户通过使用私钥对数据进行加密,则一定能够保证该数据是由私钥持有者进行加密(签名),其他用户在接收到密文时,采用公钥进行解密,则一定能够确认信息发送者是唯一的。
RSA数字签名算法的过程为:A对明文m用解密变换作: s Dk (m)=md mod n,其中d,n为A的私人密钥,只有A才知道它;B收到A的签名后,用A的公钥和加密变换得到明文,因: Ek(s)= Ek(Dk (m))= (md)e mod n,又 de1 mod (n)即de=l(n)+1,根据欧拉定理m(n)=1 mod n,所以Ek(s)=ml(n)+1=[m(n)]em=m mod n.若明文m和签名s一起送给用户B,B可以确信信息确实是A发送的.同时A也不能否认送给这个信息,因为除了A本人外,其他任何人都无法由明文m产生s.因此RSA数字签名方案是可行的. —-引用至RSA算法和RSA数字签名算法的实现
在非对称加密体系中,其理论支撑为“大数分解“的数学难题,若数足够大,我们可以认定,该难题是无解的,从而保证加密的安全性。
其中在Linux的SSH连接中,可以配置免密钥登录,实际上,依然采用的是非对称加密体系,对连接进行加密。如以下过程:
A主机希望通过SSH免密钥连接到B主机;
在A主机中,产生密钥对,E1(私钥)、E2(公钥);
将E2分发到B主机中;
A主机连接B主机时,A主机发送连接请求到B主机,其中携带信息包括:IP地址、用户名等;
B主机接收到数据后,查询已知主机列表中是否有该IP地址和用户,若没有则提示用户是否要添加到已知列表。若有,则B主机会随机产生一个字符串S,用于身份认证,该字符串S使用E2公钥进行加密发送到主机A中;
主机A接受到加密后的随机字符串,主机A使用其私钥对其进行解密,得到明文数据P,发送到主机B中;
B接受该明文数据,对明文进行判断,若和产生的随机数一致,则连接成功;
图引用至:ssh免密码登录的原理
其他加密算法,如:MD5、SHA-1、SHA2等,则为Hash算法,该过程一般来说,是不可逆的。
Java实现RSA加密
准备工作:借助apache commons codec工具进行Base64编解码,实现密钥的编解码。在实现RSA时,通常分为2大步:
协商密钥对,并产生一对密钥;
利用公钥进行加密,利用私钥进行解密。
产生密钥可以使用如下代码:
package site.franksite.encrpt.rsaencrypt; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.util.Date; import org.apache.commons.codec.binary.Base64; /** * RSA密钥生成类 * @author frank * */ public class RSAKeyGenerator { private byte[] publicKeyEncoded; private byte[] privateKeyEncoded; private static final int KEY_LENGTH = 1024; public void generate() { try { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); SecureRandom secRandom = new SecureRandom(); secRandom.setSeed(new Date().getTime()); generator.initialize(KEY_LENGTH, secRandom); // 产生键值对 KeyPair pair = generator.generateKeyPair(); PublicKey pubKey = pair.getPublic(); PrivateKey priKey = pair.getPrivate(); // 加密公钥私钥 publicKeyEncoded = Base64.encodeBase64(pubKey.getEncoded()); privateKeyEncoded = Base64.encodeBase64(priKey.getEncoded()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } /** * @return the publicKeyEncoded */ public byte[] getPublicKeyEncoded() { if (null == publicKeyEncoded) { generate(); } return publicKeyEncoded; } /** * @return the privateKeyEncoded, encrypt by Base64 */ public byte[] getPrivateKeyEncoded() { if (null == privateKeyEncoded) { generate(); } return privateKeyEncoded; } }
在获得到密钥对时,我们需要将其保存,或持久化到数据库,或持久化到文件,供加密解密过程中使用。
在加密时,可以如下实现:
public byte[] encrypt(byte[] pubKey, byte[] data) { PublicKey key = restorePublicKey(pubKey); try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, key); return cipher.doFinal(data); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; }
在该代码中,首先将公钥字节码,还原为公钥实例,再调用Cipher对数据进行加密。
其中还原公钥代码为:
/** * 将公钥字节码转为公钥实例 * @param publicKey RSA公钥字节码 * @return 公钥实例 */ public PublicKey restorePublicKey(byte[] publicKey) { X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); try { KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPT_ALGORITHM); PublicKey pubKey = keyFactory.generatePublic(keySpec); return pubKey; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return null; }
加密后,密文将作为字节流的方式返回,我们可以对该二进制字节码文件采用Base64进行编码,产生Base64字符串,进行存储或转发。
同样的解密过程也分为上述过程,首先还原私钥的二进制流数据:
/** * 将私钥字节码转为私钥实例 * @param pivateKey RSA私钥字节码 * @return 私钥实例 */ public PrivateKey restorePrivateKey(byte[] pivateKey) { PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pivateKey); try { KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPT_ALGORITHM); PrivateKey key = keyFactory.generatePrivate(keySpec); return key; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } return null; }
在获得到该私钥实例后,使用该私钥进行解密:
public byte[] dencrypt(byte[] priKey, byte[] data) { // 还原私钥 PrivateKey privateKey = restorePrivateKey(priKey); try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; }
解密后的数据将以字节流的形式返回!
注:上述代码中的常量
CIPHER_ALGORITHM值为:”RSA/ECB/PKCS1Padding”,
ENCRYPT_ALGORITHM为:”RSA”
通过上述方式,即可完成加密及解密。
同样的,采用类似的过程,可以借助RSA实现数字签名:
public byte[] sign(byte[] priKey, byte[] data) { PrivateKey privateKey = restorePrivateKey(priKey); try { Signature sign = Signature.getInstance("MD5withRSA"); sign.initSign(privateKey); sign.update(data); return Base64.encodeBase64(sign.sign()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } return null; }
上述为签名过程。
public boolean verify(byte[] pubKey, byte[] data, byte[] originData) { PublicKey publicKey = restorePublicKey(pubKey); Signature sign; try { sign = Signature.getInstance("MD5withRSA"); sign.initVerify(publicKey); sign.update(originData); return sign.verify(data); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } return false; }
上述为签名验证过程。
项目下载
本项目实现了JAVA RSA加密及数字签名,有需要的用户可以到github检出或浏览:JAVA RSAEncrypt相关文章推荐
- Java安全系列-RSA登录表单加密
- Java安全架构____RSA加密配置第三方加密库问题_javax.crypto.BadPaddingException: Decryption error
- Java安全编程:RSA加密解密
- java安全(二)非对称加密RSA
- Java安全架构____RSA加密配置第三方加密库问题_javax.crypto.BadPaddingException: Decryption error
- java安全架构____RSA加密原理(1)
- java安全架构____RSA加密原理(2)
- 自学Java系列 笔记4 线程安全
- java利用Apache的commons-codec实现md系列加密
- php 和 java RSA 对称加密互通的问题
- RSA加密解密相关 前端js加密,服务端java解密
- java 实现RSA 加密解密工具类及其内部简单流程
- Java MD5校验与RSA加密
- PHP相关系列 - 兼容JAVA的PHP加密解密DES算法
- JAVA RSA非对称加密 XMLSignature/X509Certificate
- 【Java安全技术探索之路系列:J2ME安全架构】之一:Java ME安全架构开篇
- 利用OpenSSL库对Socket传输进行安全加密(RSA+AES)
- java加密技术--RSA加密
- java加密、解密技术系列:Base64
- java RSA非对称加密-解密(简洁明了)