加密
2016-05-12 19:38
323 查看
转自http://m.oschina.net/blog/309771
六月依 发布于 2年前,共有 2 条评论
说明
1.java生成的公私钥格式为 pkcs8, 而openssl默认生成的公私钥格式为 pkcs1,两者的密钥实际上是不能直接互用的
2.java采用的rsa默认补齐方式是pkcs1, 因此互用的时候需要将openssl中的补齐方式设置为RSA_PKCS1_PADDING
3.rsa加密中,加密数据长度有限制,不能超过密钥长度-11, 如密钥为1024位,则最长的加密数据位117字节; 加密后的密文长度总是为密钥的一半,即1024位的密文为512位
RSA加密常用的填充方式有下面3种:
1.RSA_PKCS1_PADDING 填充模式,最常用的模式
要求:
输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割, 然后填充
输出:和modulus一样长
根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节
2.RSA_PKCS1_OAEP_PADDING
输入:RSA_size(rsa) – 41
输出:和modulus一样长
3.for RSA_NO_PADDING 不填充
输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充
输出:和modulus一样长
其中,这里可以选择rsa,也就默认了采用pkcs1补齐方式,也可以设置为其他的,如
另外加密的数据是byte格式,即无符号字符,因此常见的算法是利用base64编码,将byte格式的转为String,因此这里也涉及到java与openssl的base64编码相互转换的问题
两者的输出格式是不同的,主要体现在换行的位置上,对openssl而言,base64编码后的换行主要是每64个出现一个换行;而java则是每76个字节出现一个换行,如果实际去测试,两者的base64编码解码是不能互用的。对于此,可以参考下面c的算法中的base64编码算法,屏蔽了换行的出现,这种情况下,java是可以解码openssl编码的结果的;相反,在openssl中,去掉换行后,也是可以处理java采用base64编码后的结果
Openssl的rsa算法
采用openssl的rsa算法实现公私钥加解密,这里由于项目需求,公私钥是使用java生成的,因此需要在密钥的首行和最后一行添加标记,两者对比如下:
c# java iOS php 联调参考git.oschina
java与openssl的rsa算法互用
六月依 发布于 2年前,共有 2 条评论说明
1.java生成的公私钥格式为 pkcs8, 而openssl默认生成的公私钥格式为 pkcs1,两者的密钥实际上是不能直接互用的2.java采用的rsa默认补齐方式是pkcs1, 因此互用的时候需要将openssl中的补齐方式设置为RSA_PKCS1_PADDING
3.rsa加密中,加密数据长度有限制,不能超过密钥长度-11, 如密钥为1024位,则最长的加密数据位117字节; 加密后的密文长度总是为密钥的一半,即1024位的密文为512位
RSA加密常用的填充方式有下面3种:
1.RSA_PKCS1_PADDING 填充模式,最常用的模式
要求:
输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割, 然后填充
输出:和modulus一样长
根据这个要求,对于512bit的密钥, block length = 512/8 – 11 = 53 字节
2.RSA_PKCS1_OAEP_PADDING
输入:RSA_size(rsa) – 41
输出:和modulus一样长
3.for RSA_NO_PADDING 不填充
输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充
输出:和modulus一样长
Java的rsa封装
代码如下,从网上copy下来的,在此源码中,需要注意的是cipher = Cipher.getInstance("RSA")
其中,这里可以选择rsa,也就默认了采用pkcs1补齐方式,也可以设置为其他的,如
cipher = Cipher.getInstance("RSA/ECB/NoPadding")
另外加密的数据是byte格式,即无符号字符,因此常见的算法是利用base64编码,将byte格式的转为String,因此这里也涉及到java与openssl的base64编码相互转换的问题
两者的输出格式是不同的,主要体现在换行的位置上,对openssl而言,base64编码后的换行主要是每64个出现一个换行;而java则是每76个字节出现一个换行,如果实际去测试,两者的base64编码解码是不能互用的。对于此,可以参考下面c的算法中的base64编码算法,屏蔽了换行的出现,这种情况下,java是可以解码openssl编码的结果的;相反,在openssl中,去掉换行后,也是可以处理java采用base64编码后的结果
import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * RSA算法,实现数据的加密解密。 * @author ShaoJiang * */ class RSAUtil { private static Cipher cipher; static{ try { cipher = Cipher.getInstance("RSA"); // RSA/ECB/NoPadding } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } } /** * 生成密钥对 * @param filePath 生成密钥的路径 * @return */ public static Map<String,String> generateKeyPair(String filePath){ try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // 密钥位数 keyPairGen.initialize(1024); // 密钥对 KeyPair keyPair = keyPairGen.generateKeyPair(); // 公钥 PublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 私钥 PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); //得到公钥字符串 String publicKeyString = getKeyString(publicKey); //得到私钥字符串 String privateKeyString = getKeyString(privateKey); //将密钥对写入到文件 FileWriter pubfw = new FileWriter(filePath+"/publicKey.keystore"); FileWriter prifw = new FileWriter(filePath+"/privateKey.keystore"); BufferedWriter pubbw = new BufferedWriter(pubfw); BufferedWriter pribw = new BufferedWriter(prifw); pubbw.write(publicKeyString); pribw.write(privateKeyString); pubbw.flush(); pubbw.close(); pubfw.close(); pribw.flush(); pribw.close(); prifw.close(); //将生成的密钥对返回 Map<String,String> map = new HashMap<String,String>(); map.put("publicKey",publicKeyString); map.put("privateKey",privateKeyString); return map; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 得到公钥 * * @param key * 密钥字符串(经过base64编码) * @throws Exception */ public static PublicKey getPublicKey(String key) throws Exception { byte[] keyBytes; keyBytes = (new BASE64Decoder()).decodeBuffer(key); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(keySpec); return publicKey; } /** * 得到私钥 * * @param key * 密钥字符串(经过base64编码) * @throws Exception */ public static PrivateKey getPrivateKey(String key) throws Exception { byte[] keyBytes; keyBytes = (new BASE64Decoder()).decodeBuffer(key); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey; } /** * 得到密钥字符串(经过base64编码) * * @return */ public static String getKeyString(Key key) throws Exception { byte[] keyBytes = key.getEncoded(); String s = (new BASE64Encoder()).encode(keyBytes); return s; } /** * 使用公钥对明文进行加密,返回BASE64编码的字符串 * @param publicKey * @param plainText * @return */ public static String encrypt(PublicKey publicKey,String plainText){ try { cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] enBytes = cipher.doFinal(plainText.getBytes()); return (new BASE64Encoder()).encode(enBytes); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * 使用keystore对明文进行加密 * @param publicKeystore 公钥文件路径 * @param plainText 明文 * @return */ public static String encrypt(String publicKeystore,String plainText){ try { FileReader fr = new FileReader(publicKeystore); BufferedReader br = new BufferedReader(fr); String publicKeyString=""; String str; while((str=br.readLine())!=null){ publicKeyString+=str; } br.close(); fr.close(); cipher.init(Cipher.ENCRYPT_MODE,getPublicKey(publicKeyString)); byte[] enBytes = cipher.doFinal(plainText.getBytes()); return (new BASE64Encoder()).encode(enBytes); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 使用私钥对明文密文进行解密 * @param privateKey * @param enStr * @return */ public static String decrypt(PrivateKey privateKey,String enStr){ try { cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] deBytes = cipher.doFinal((new BASE64Decoder()).decodeBuffer(enStr)); return new String(deBytes); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 使用keystore对密文进行解密 * @param privateKeystore 私钥路径 * @param enStr 密文 * @return */ public static String decrypt(String privateKeystore,String enStr){ try { FileReader fr = new FileReader(privateKeystore); BufferedReader br = new BufferedReader(fr); String privateKeyString=""; String str; while((str=br.readLine())!=null){ privateKeyString+=str; } br.close(); fr.close(); cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyString)); byte[] deBytes = cipher.doFinal((new BASE64Decoder()).decodeBuffer(enStr)); return new String(deBytes); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } } public class Hello{ // 将 s 进行 BASE64 编码 public static String getBASE64(String s) { if (s == null) return null; return (new sun.misc.BASE64Encoder()).encode( s.getBytes() ); } // 将 BASE64 编码的字符串 s 进行解码 public static String getFromBASE64(String s) { if (s == null) return null; BASE64Decoder decoder = new BASE64Decoder(); try { byte[] b = decoder.decodeBuffer(s); return new String(b); } catch (Exception e) { return null; } } public static void testBASE64() { String s = "1234567887654321123456788765432112345678876543211234567887654321123456788765432112345678876543211234567887654321123456788765432112345678876543211234567887654321"; System.out.println("length=" + s.length()); String ans = getBASE64(s); System.out.println("length=" + ans.length()); System.out.println(ans); String temp = "MTIzNDU2Nzg4NzY1NDMyMTEyMzQ1Njc4ODc2NTQzMjExMjM0NTY3ODg3NjU0MzIxMTIzNDU2Nzg4NzY1NDMyMTEyMzQ1Njc4ODc2NTQzMjE="; String res = getFromBASE64(temp); System.out.println(res); System.out.println("----------------------------------------"); } public static void main(String[] args) { RSAUtil util = new RSAUtil(); // util.generateKeyPair("key"); String cp = "M04d2l9MyDiUUfAQ32FdphesAQJHZUk0dEsYQcU06IJo/RCF311GtJXBK1FhapITIvjkpsiz9NR25AGEFPdz4bs2o5/F0QIj5yFA+biLxgcFrDpd5gSWI1F8V7wbsl06tNLNOVihFfzl8xWbHMVqPhY3tj8Vu/QHEPPnx7mvHlc="; // cp保存的是由openssl加密后的结果(包括了对密文的base64编码) String cipher = util.encrypt("key/privateKey.keystore", "hello world"); String recover = util.decrypt("key/publicKey.keystore", cp); System.out.println("cipher=" + cipher); System.out.println("recover=" + recover); } }
Openssl的rsa算法
采用openssl的rsa算法实现公私钥加解密,这里由于项目需求,公私钥是使用java生成的,因此需要在密钥的首行和最后一行添加标记,两者对比如下:--------java private key-------(本行不存在) MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKNQ3j+Ov9THQTMbNw+MwLOGRDwR qkiH0QffSLBh1Nv6JHBp7yC07b+Nj0lMiBOjoMpk98W3RTdDuRcJ/ojM0OMPZWNiOdvrtJ7I/Haw DiryMLqWR5gBCC0g+ODs2Cxr1ig5idNaX0bHy9ufSZpFd378OZLBVuOEBtQ7lnFhp/DxAgMBAAEC gYEAiOBLFPvAHUZOh9XEZPzzuMLMR4gKrrVd5PonguTLsP5KMgknCMN7C3NKZwWEeulF5ruOyh/b HAFfjEatFNyMKJnUhHwShvvSqncbZMBy9xmaA/FF0BUD285Vb+PhuPnL678KhEcKD3k99A+ym4qW K9abeiJypAfvnleopBFV+c0CQQDcdUrMdCVCdUIxenIjz67Qp6672iBC2Ni5psow2SZZ9FTC31Z5 7jDowqrvsABbY7XbV8g1LUX/U6JLylXiz7MrAkEAvaU16YtUCdVe4s6saNLnQCfi2ykVdn5ovy3R ZSn1gzQhy+//GFAhtUTjWO46zBicuNbJ1bNcaiF7DF4evU2OUwJBAJC14WqBlu+f3tpxoLjHG95V I4qlrOWUR/bdd/1GnTkQ/A6K4c3Zv3saRXBeXG2g7v/TCFV3qWnncTJE18mxQs8CQQCPVxNDvaK8 eADrD2lzJY3SiRNHsWrekDcd4US24RsHm1y8J185gj+oIjRwoEfiweubLgWKN7JvKXU26OD1Fmyv AkBR63yGMoIMo4AxLnkcr16j9WLBgQ/NXakPHcO2iMtFUsix3SNh96AkxZ+Fs87Ys70RuJOTtA/S llTOdaKm5PVqpc --------openssl private key-------(本行不存在) -----BEGIN RSA PRIVATE KEY-----(存在) MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKIpKYdHzzjEaugD55bBNlV+iJ60 w+C2fecv2ptenw7lUqDRiXTQsMXUiEBPKB5MLtJhLgB9yiph+lIN/lv+7GcPHaEjLwvh0ZnwXiQ2 sbL9sCzvU9r/2otJJ+2NrEf0PA4wEocrSiEv3at7oD3PCVps8nht3fzuPYfCxWhkBXeXAgMBAAEC gYAm/FZrm6wKLc618RpgeBsj+sPFxBfDkpw6VJrt95Wt8xwmZbnyR6n4JdyL+XvfrNkk4xA4NgXA osWXrJ2WaHdU/J0C1NnVzwMSISojR3+w5nsY5xyfdhPCNthlWnlmYTXJNOaQUGVO3oR1q0be+drt uQUV6yvsw/JDftskprqToQJBAOvObGbnz36YgdClJCVHZj3vFDlblAOiOFzIPq0hx72FpYcj/Ho3 3o5bAfZnHPMvOhbMaeY1xpm423f78qV2A+MCQQCwDDWM0uxvTA2XbpQGtVupZbqh/iZQ2eMYp8FJ t4aa/brUesXZurqsHWcrM6HrWp7gA6DzYeSSy6cCeVV6dlO9AkBaW7JMSH3JO/Valz1fj5mNGAT+ 6amxLsM8QntJ6ozYgdLpExAXYTtF/3ekDOUaBrnSrKfT0D13AFARDRlw7+UTAkAO7hokhUbT8Yk9 jH01THAnn58CWgF53eCqmJ+F1vCteFM97eOVoYZvTlcPjONwiFDtAqHFwkmWQXs6dHvRVPOpAkEA jalQvMpQM7Ej3s2oK386LVrH/La0hZZ6Fn4bgCJ26YxB3AGN5J8uHLjU5oBUMxDefc0OraDdyStJ pAh2vYzETw== -----END RSA PRIVATE KEY-----
c# java iOS php 联调参考git.oschina
相关文章推荐
- 添加LogCat 至 eclipse --2
- Codeforces Round #352 (Div. 2)C. Recycling Bottles
- 【计算机视觉】OpenCV的最近邻开源库FLANN
- OpenGl 学习笔记 01
- MFC中Dialog无法输入中文的问题
- DIRECTORY_SEPARATOR
- 0512 操作系统进程调度实验
- BZOJ4554 [Tjoi2016&Heoi2016]游戏
- 求自定类型元素的平均
- poj 1064 Cable master(二分搜索)
- 【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交
- UzysAssetsPickerController中文化
- Android Studio编译NDK app gradle配置
- sdutoj 3362 数据结构实验之图论六:村村通公路(最小生成树(裸的))
- (经典)POJ-3280 回文串DP
- appium for python client scroll to view(not in current screen)
- Partition-方案二:通过 Insert with a subquery 方法
- Android Json解析工具类
- 基于内容的图像检索引擎(以图搜图)
- Android学习笔记五:文件夹创建