RSA加密算法java
RSA公钥加密算法是1977年由Ron Rivest, Adi Shamir 和Leonard Adleman一块儿提出,RSA就是他们三人姓氏开头字母拼在一块儿组成的。RSA是目前最有影响力和最经常使用的公钥加密算法,它可以抵抗到目前为止已知的绝大多数密码攻击,至今未被彻底攻破。目前已被ISO推荐为公钥数据加密标准。算法
RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操做。RSA是被研究得最普遍的公钥算法,从提出到现今的三十多年里,经历了各类攻击的考验,逐渐为人们接受,截止2017年被广泛认为是最优秀的公钥方案之一。数组
RSA公开密钥密码体制安全
所谓的公开密钥密码体制就是使用不一样的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。网络
在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是须要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK。函数
根据密钥的使用方法,能够将密码分为对称密码和公钥密码工具
对称密码:加密和解密使用同一种密钥的方式编码
公钥密码:加密和解密使用不一样的密码的方式,所以公钥密码一般也称为非对称密码。加密
RSA算法实现过程spa
RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,所以能够将乘积公开做为加密密钥,即公钥,而两个大素数组合成私钥。
公钥是可发布的供任何人使用,私钥则为本身全部,供解密之用。
解密者拥有私钥,而且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送到解密者,解密者用私钥解密将密文解码为明文。
示例:
以甲要把信息发给乙为例,
首先肯定角色:甲为加密者,乙为解密者。
而后由乙随机肯定一个KEY,称之为密匙,将这个KEY始终保存在机器B中而不发出来;
接着由这个 KEY计算出另外一个KEY,称之为公匙。这个公钥的特性是几乎不可能经过它自身计算出生成它的私钥。
接下来经过网络把这个公钥传给甲,甲收到公钥后,利用公钥对信息加密,并把密文经过网络发送到乙,
最后乙利用已知的私钥,就对密文进行解码了。
以上就是RSA算法的工做流程。
RSA算法具体实现过程以下:
一、随意选择两个大的质数p和q,p不等于q,计算N=pq。
二、根据欧拉函数,不大于N且与N互质的整数個数為(p-1)(q-1)。
三、选择一个整数e与(p-1)(q-1)互质,而且e小于(p-1)(q-1)。
四、用如下这个公式计算d:d× e ≡ 1 (mod (p-1)(q-1))。
五、将p和q的记录销毁。
注:(N,e)是公钥,(N,d)是私钥。
RSA算法的应用
RSA的公钥和私钥是由KeyPairGenerator生成的,获取KeyPairGenerator的实例后还须要设置其密钥位数。
设置密钥位数越高,加密过程越安全,通常使用1024位。
以下代码:
java代码:
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA); // 密钥位数 keyPairGen.initialize(1024);
公钥和私钥能够经过KeyPairGenerator执行generateKeyPair()后生成密钥对KeyPair,经过KeyPair.getPublic()和KeyPair.getPrivate()来获取。
以下代码:
java代码:
// 动态生成密钥对,这是当前最耗时的操做,通常要2s以上。 KeyPair keyPair = keyPairGen.generateKeyPair(); // 公钥 PublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 私钥 PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); byte[] publicKeyData = publicKey.getEncoded(); byte[] privateKeyData = publicKey.getEncoded();
公钥和私钥都有它们本身独特的比特编码,能够经过getEncoded()方法获取,返回类型为byte[]。
经过byte[]能够再度将公钥或私钥还原出来。
具体代码以下:
java代码:
// 经过公钥byte[]将公钥还原,适用于RSA算法 public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException,InvalidKeySpecException { X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(“RSA”); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; } // 经过私钥byte[]将公钥还原,适用于RSA算法 public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,InvalidKeySpecException { PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(“RSA”); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; }
在上面讲到的RSA算法实现过程当中提到(N,e)是公钥,(N,d)是私钥。既然已经获取到了PublicKey和PrivateKey了,那如何取到N、e、d这三个值呢。
要取到这三个值,首先要将PublicKey和PrivateKey强制转换成RSAPublicKey和RSAPrivateKey。
共同的N值能够经过getModulus()获取。
执行RSAPublicKey.getPublicExponent()能够获取到公钥中的e值,
执行RSAPrivateKey.getPrivateExponent()能够获取私钥中的d值。
这三者返回类型都是BigInteger。代码以下:
java代码:
// 打印公钥信息 public static void printPublicKeyInfo(PublicKey key){ RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; Log.d(MainActivity.TAG, “RSAPublicKey:”); Log.d(MainActivity.TAG, “Modulus.length=” + rsaPublicKey.getModulus().bitLength()); Log.d(MainActivity.TAG, “Modulus=” + rsaPublicKey.getModulus().toString()); Log.d(MainActivity.TAG, “PublicExponent.length=” + rsaPublicKey.getPublicExponent().bitLength()); Log.d(MainActivity.TAG, “PublicExponent=” + rsaPublicKey.getPublicExponent().toString()); } // 打印私钥信息 public static void printPublicKeyInfo(PrivateKey key){ RSAPrivateKey rsaPublicKey = (RSAPrivateKey) privateKey; Log.d(MainActivity.TAG, “RSAPrivateKey:”); Log.d(MainActivity.TAG, “Modulus.length=” + rsaPrivateKey.getModulus().bitLength()); Log.d(MainActivity.TAG, “Modulus=” + rsaPrivateKey.getModulus().toString()); Log.d(MainActivity.TAG, “PublicExponent.length=” + rsaPrivateKey.getPrivateExponent().bitLength()); Log.d(MainActivity.TAG, “PublicExponent=” + rsaPrivateKey.getPrivateExponent().toString()); }
因为程序中动态生成KeyPair对明文加密后生成的密文是不可测的,因此在实际开发中一般在生成一个KeyPair后将公钥和私钥的N、e、d这三个特征值记录下来,在真实的开发中使用这三个特征值再去将PublicKey和PrivateKey还原出来。
还原方法以下:
java代码:
// 使用N、e值还原公钥 public static PublicKey getPublicKey(String modulus, String publicExponent) throws NoSuchAlgorithmException, InvalidKeySpecException { BigInteger bigIntModulus = new BigInteger(modulus); BigInteger bigIntPrivateExponent = new BigInteger(publicExponent); RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent); KeyFactory keyFactory = KeyFactory.getInstance(“RSA”); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; } // 使用N、d值还原公钥 public static PrivateKey getPrivateKey(String modulus, String privateExponent) throws NoSuchAlgorithmException, InvalidKeySpecException { BigInteger bigIntModulus = new BigInteger(modulus); BigInteger bigIntPrivateExponent = new BigInteger(privateExponent); RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(bigIntModulus, bigIntPrivateExponent); KeyFactory keyFactory = KeyFactory.getInstance(“RSA”); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; }
公钥和私钥都具有后,就可使用加解密的工具类javax.crypto.Cipher对明文和密文进行处理了。
与全部的引擎类同样,能够经过调用Cipher类中的getInstance(String transformation)静态工厂方法获得Cipher对象。
该方法中的参数描述了由指定输入产生输出所进行的操做或操做集合,
能够是下列两种形式之一:
“algorithm/mode/padding”或“algorithm”。
例以下面的例子就是有效的transformation形式:”DES/CBC/PKCS5Padding”或”DES”。
若是没有指定模式或填充方式,就使用特定提供者指定的默认模式或默认填充方式。
Cipher的加密和解密方式所调用的方法和过程都同样,只是传参不一样的区别。以下代码:
java代码:
// 编码前设定编码方式及密钥 cipher.init(mode, key); // 传入编码数据并返回编码结果 byte[] dataResult = cipher.doFinal(input);
Cipher.init(mode, key)方法中MODE指加密或解密模式,值为Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE,参数key在加密时传入PublicKey,在解密时以PrivateKey传入。
Cipher.doFinal(byte[] data)则是将待编码数据传入后并返回编码结果。
为了将编码结果转为可读字符串,一般最后还使用BASE 64算法对最终的byte[]数据编码后显示给开发者。