背景
1977年,Ronald Rivest,Adi Shamir和Leonard Adleman提出了一种实现方案,即RSA;它后来变成了非对称密码方案中使用最广泛的一种。RSA也成为Rivest-Shamri-Adkeman算法,他是目前最广泛的一种非对称密钥方案,不过椭圆曲线和离散对数方案也逐渐普及。在实际中RSA应用广泛,但是我们却常用于:
@数据小片段的加密,尤其用于密钥传输
@数字签名,比如Internet上的数字证书。
写这篇文章的目的当前也是有自己记录的背景:使用Android手机从服务器上拿到Ukey的公钥-----Android手机使用公钥加密随机生成的密钥--------通过无线传送给Ukey------Ukey调用自己的私钥机密获取随机密钥,建立通信。
如果只是想测试java公私钥或者openssl,可以转到http://blog.csdn.net/chaijunkun/article/details/7275632,下面的内容就不适合了。
---------------------------------------分割----------------------------------------------------------------------
遇上的问题在java中如何装载Ukey生成的公钥,java环境下使用的是openssl密码环境,这里当然要感谢Eric A. Young和Tim J. Hudson是他们给我们带来了极大的方便,缺点是让我们看不见更多的底层,和RSA的运行机制。
首先说一下常规:
/**
* 随机生成密钥对,采用DER编码格式
* 一定要注意的是:publicKey生成的为162 bytes
* 这里的publicKey不仅仅包含了128密钥信息,还有编码和序列等信息
* 这个publicKey结构我查不到
*/
public void genKeyPair(){
KeyPairGenerator keyPairGen= null;
try {
keyPairGen= KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keyPairGen.initialize(1024, new SecureRandom());
KeyPair keyPair= keyPairGen.generateKeyPair();
this.privateKey= (RSAPrivateKey) keyPair.getPrivate();
this.publicKey= (RSAPublicKey) keyPair.getPublic();
System.out.println(publicKey.getEncoded().length+"*****");
System.out.println(AES.byteArrayToString(publicKey.getEncoded()));
}
下面就是加载公钥
/** * 从字节加载公钥 * @param publicKeyStr 公钥数据字符串 * @throws Exception 加载公钥时产生的异常 */ public void loadPublicKey1(byte[] bytepublicKey) throws Exception{ try { KeyFactory keyFactory= KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec= new X509EncodedKeySpec(bytepublicKey); this.publicKey= (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("公钥非法"); } catch (NullPointerException e) { throw new Exception("公钥数据为空"); } }这里可以看到使用keyFactory.generatePublic加载keySpec,这里的keySpec是X509格式,如果你bytepublicKey没有162个字节的话,那么在keyFactory.generatePublic注定会报出“公钥非法”的错误。但是我得ukey生成的公钥只有128 bytes也就注定我无法使用X509格式。
-----------------------------------分割------------------------------------------
首先说Ukey:
Ukey采用
RSA_GenKey(&kpub, &kpri, RSA_1024+RSA_E_0x10001+RSA_noCRT);1024位密钥,RSA算法,公钥类型10001 16进制,私钥类型不采用CRT。
加密出的数据:
01 02 03 04 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00加密后:
70 B2 86 5F 2E 1B C9 EC FC E1 40 30 0F 89 20 97 31 88 61 24 6B 42 9B 5D C4 78 FE D6 AA 1C 0C F6 24 59 F1 2F 73 41 53 C3 BB 15 AB C3 6C 8B 61 75 3E 27 06 4C E6 D2 BF B0 9C D5 AF 89 89 93 D0 9E FF EE 42 C1 36 B7 77 D9 6A 9D D8 74 20 31 FF 01 EB C5 CA D0 24 33 3E 9F 3B E9 28 6E C7 CE 51 A9 2F 40 DA DB 17 43 A7 52 39 8F E5 A4 57 AB 65 FC 93 22 89 47 DF 72 6A 34 83 6F 20 4A E7 53 68 15再来说android:
/** * 加载字节公钥信息 */ public void loadPublicKey(byte[] bytepublicKey) throws Exception{ try { System.out.println(bytepublicKey.length+"*****");//DER KeyFactory keyFactory= KeyFactory.getInstance("RSA"); RSAPublicKeySpec keySpec= new RSAPublicKeySpec(new BigInteger( bytepublicKey), new BigInteger("10001", 16)); this.publicKey= (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (NullPointerException e) { throw new Exception("公钥数据为空"); } }数据加密流程:
/** * 加密过程 * @param publicKey 公钥 * @param plainTextData 明文数据 * @return * @throws Exception 加密过程中的异常信息 */ public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception{ if(publicKey== null){ throw new Exception("加密公钥为空, 请设置"); } Cipher cipher= null; try { cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] output= cipher.doFinal(plainTextData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此加密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; }catch (InvalidKeyException e) { throw new Exception("加密公钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("明文长度非法"); } catch (BadPaddingException e) { throw new Exception("明文数据已损坏"); } }下面贴一下加密前的数据:
这里可以核对一下和Ukey加密出来的数据是一样的。在android上的关键点就是
RSAPublicKeySpec keySpec= new RSAPublicKeySpec(new BigInteger( bytepublicKey), new BigInteger("10001", 16));这里使用了RSAPublicKeySpec,公钥格式为10001 16进制,非常的关键。