本文出處:http://blog.csdn.net/chaijunkun/article/details/7275632,轉(zhuǎn)載請注明。由于本人不定期會整理相關(guān)博文,會對相應(yīng)內(nèi)容作出完善。因此強烈建議在原始出處查看此文。
?
RSA是什么:RSA公鑰加密算法是 1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美國麻省理工學(xué)院)開發(fā)的。RSA取名來自開發(fā)他們?nèi)叩拿?。RSA是目前最有影響力的公鑰加密算法,它能夠 抵抗到目前為止已知的所有密碼攻擊,已被ISO推薦為公鑰數(shù)據(jù)加密標(biāo)準。目前該加密方式廣泛用于網(wǎng)上銀行、數(shù)字簽名等場合。RSA算法基于一個十分簡單的 數(shù)論事實:將兩個大素數(shù)相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰。
OpenSSL是什 么:眾多的密碼算法、公鑰基礎(chǔ)設(shè)施標(biāo)準以及SSL協(xié)議,或許這些有趣的功能會讓你產(chǎn)生實現(xiàn)所有這些算法和標(biāo)準的想法。果真如此,在對你表示敬佩的同時,還 是忍不住提醒你:這是一個令人望而生畏的過程。這個工作不再是簡單的讀懂幾本密碼學(xué)專著和協(xié)議文檔那么簡單,而是要理解所有這些算法、標(biāo)準和協(xié)議文檔的每 一個細節(jié),并用你可能很熟悉的C語言字符一個一個去實現(xiàn)這些定義和過程。我們不知道你將需要多少時間來完成這項有趣而可怕的工作,但肯定不是一年兩年的問 題。OpenSSL就是由Eric A. Young和Tim J. Hudson兩位絕世大好人自1995年就開始編寫的集合眾多安全算法的算法集合。通過命令或者開發(fā)庫,我們可以輕松實現(xiàn)標(biāo)準的公開算法應(yīng)用。
?
我的一個假設(shè)應(yīng)用背景:
隨 著移動互聯(lián)網(wǎng)的普及,為移動設(shè)備開發(fā)的應(yīng)用也層出不窮。這些應(yīng)用往往伴隨著用戶注冊與密碼驗證的功能?!本W(wǎng)絡(luò)傳輸“、”應(yīng)用程序日志訪問“中的安全性都存 在著隱患。密碼作為用戶的敏感數(shù)據(jù),特別需要開發(fā)者在應(yīng)用上線之前做好安全防范。處理不當(dāng),可能會造成諸如商業(yè)競爭對手的惡意攻擊、第三方合作商的訴訟等 問題。
?
RSA算法雖然有這么多好處,但是在網(wǎng)上找不到一個完整的例子來說明如何操作。下面我就來介紹一下:
一、使用OpenSSL來生成私鑰和公鑰
我使用的是Linux系統(tǒng),已經(jīng)安裝了OpenSSL軟件包,此時請驗證你的機器上已經(jīng)安裝了OpenSSL,運行命令應(yīng)當(dāng)出現(xiàn)如下信息:
[plain] view plaincopy
[root@chaijunkun?~]#?openssl?version?-a?? OpenSSL?1.0.0-fips?29?Mar?2010?? built?on:?Wed?Jan?25?02:17:15?GMT?2012?? platform:?linux-x86_64?? options:??bn(64,64)?md2(int)?rc4(16x,int)?des(idx,cisc,16,int)?blowfish(idx)??? compiler:?gcc?-fPIC?-DOPENSSL_PIC?-DZLIB?-DOPENSSL_THREADS?-D_REENTRANT?-DDSO_DLFCN?-DHAVE_DLFCN_H?-DKRB5_MIT?-m64?-DL_ENDIAN?-DTERMIO?-Wall?-O2?-g?-pipe?-Wall?-Wp,-D_FORTIFY_SOURCE=2?-fexceptions?-fstack-protector?--param=ssp-buffer-size=4?-m64?-mtune=generic?-Wa,--noexecstack?-DMD32_REG_T=int?-DOPENSSL_IA32_SSE2?-DOPENSSL_BN_ASM_MONT?-DSHA1_ASM?-DSHA256_ASM?-DSHA512_ASM?-DMD5_ASM?-DAES_ASM?-DWHIRLPOOL_ASM?? OPENSSLDIR:?"/etc/pki/tls"?? engines:??aesni?dynamic??? 先來生成私鑰:
[plain] view plaincopy
[root@chaijunkun?~]#?openssl?genrsa?-out?rsa_private_key.pem?1024?? Generating?RSA?private?key,?1024?bit?long?modulus?? .......................++++++?? ..++++++?? e?is?65537?(0x10001)?? 這條命令讓openssl隨機生成了一份私鑰,加密長度是1024位。
加密長度是指理論上最大允許”被加密的信息“長度的限制,也就是明文的長度限制。隨著這個參數(shù)的增大(比方說2048),允許的明文長度也會增加,但同時也會造成計算復(fù)雜度的極速增長。一般推薦的長度就是1024位(128字節(jié))。 我們來看一下私鑰的內(nèi)容:
[plain] view plaincopy
[root@chaijunkun?~]#?cat?rsa_private_key.pem??? -----BEGIN?RSA?PRIVATE?KEY-----?? MIICWwIBAAKBgQChDzcjw/rWgFwnxunbKp7/4e8w/UmXx2jk6qEEn69t6N2R1i/L?? mcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRhtVx1uOH/2U378fscEESEG8XDq?? ll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNgXIlk3gdhnzh+uoEQywIDAQAB?? AoGAaeKk76CSsp7k90mwyWP18GhLZru+vEhfT9BpV67cGLg1owFbntFYQSPVsTFm?? U2lWn5HD/IcV+EGaj4fOLXdM43Kt4wyznoABSZCKKxs6uRciu8nQaFNUy4xVeOfX?? PHU2TE7vi4LDkw9df1fya+DScSLnaDAUN3OHB5jqGL+Ls5ECQQDUfuxXN3uqGYKk?? znrKj0j6pY27HRfROMeHgxbjnnApCQ71SzjqAM77R3wIlKfh935OIV0aQC4jQRB4?? iHYSLl9lAkEAwgh4jxxXeIAufMsgjOi3qpJqGvumKX0W96McpCwV3Fsew7W1/msi?? suTkJp5BBvjFvFwfMAHYlJdP7W+nEBWkbwJAYbz/eB5NAzA4pxVR5VmCd8cuKaJ4?? EgPLwsjI/mkhrb484xZ2VyuICIwYwNmfXpA3yDgQWsKqdgy3Rrl9lV8/AQJAcjLi?? IfigUr++nJxA8C4Xy0CZSoBJ76k710wdE1MPGr5WgQF1t+P+bCPjVAdYZm4Mkyv0?? /yBXBD16QVixjvnt6QJABli6Zx9GYRWnu6AKpDAHd8QjWOnnNfNLQHue4WepEvkm?? CysG+IBs2GgsXNtrzLWJLFx7VHmpqNTTC8yNmX1KFw==?? -----END?RSA?PRIVATE?KEY-----?? 內(nèi)容都是標(biāo)準的ASCII字符,開頭一行和結(jié)尾一行有明顯的標(biāo)記,真正的私鑰數(shù)據(jù)是中間的不規(guī)則字符。
2015 年3月24日補充:密鑰文件最終將數(shù)據(jù)通過Base64編碼進行存儲??梢钥吹缴鲜雒荑€文件內(nèi)容每一行的長度都很規(guī)律。這是由于RFC2045中規(guī) 定:The encoded output stream must be represented in lines of no more than 76 characters each。也就是說Base64編碼的數(shù)據(jù)每行最多不超過76字符,對于超長數(shù)據(jù)需要按行分割。
接下來根據(jù)私鑰生成公鑰:
[plain] view plaincopy
[root@chaijunkun?~]#?openssl?rsa?-in?rsa_private_key.pem?-out?rsa_public_key.pem?-pubout?? writing?RSA?key?? 再來看一下公鑰的內(nèi)容: [plain] view plaincopy
[root@chaijunkun?~]#?cat?rsa_public_ley.pem??? -----BEGIN?PUBLIC?KEY-----?? MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w?? /UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht?? Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg?? XIlk3gdhnzh+uoEQywIDAQAB?? -----END?PUBLIC?KEY-----?? 這時候的私鑰還不能直接被使用,需要進行PKCS#8編碼: [plain] view plaincopy
[root@chaijunkun?~]#?openssl?pkcs8?-topk8?-in?rsa_private_key.pem?-out?pkcs8_rsa_private_key.pem?-nocrypt?? 命令中指明了輸入私鑰文件為rsa_private_key.pem,輸出私鑰文件為pkcs8_rsa_private_key.pem,不采用任何二次加密(-nocrypt) 再來看一下,編碼后的私鑰文件是不是和之前的私鑰文件不同了:
[plain] view plaincopy
[root@chaijunkun?~]#?cat?pkcs8_rsa_private_key.pem??? -----BEGIN?PRIVATE?KEY-----?? MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG?? 6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4?? 1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA?? ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm?? u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j?? DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo?? MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ?? DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM?? 6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ?? FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA?? 2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX?? TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh?? Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo?? 1NMLzI2ZfUoX?? -----END?PRIVATE?KEY-----?? 至此,可用的密鑰對已經(jīng)生成好了,私鑰使用pkcs8_rsa_private_key.pem,公鑰采用rsa_public_key.pem。
2014年5月20日補充:最近又遇到RSA加密的需求了,而且對方要求只能使用第一步生成的未經(jīng)過PKCS#8編碼的私鑰文件。后來查看相關(guān)文獻得知第一步生成的私鑰文件編碼是PKCS#1格式,這種格式Java其實是支持的,只不過多寫兩行代碼而已:
[java] view plaincopy
RSAPrivateKeyStructure?asn1PrivKey?=?new?RSAPrivateKeyStructure((ASN1Sequence)?ASN1Sequence.fromByteArray(priKeyData));?? RSAPrivateKeySpec?rsaPrivKeySpec?=?new?RSAPrivateKeySpec(asn1PrivKey.getModulus(),?asn1PrivKey.getPrivateExponent());?? KeyFactory?keyFactory=?KeyFactory.getInstance("RSA");?? PrivateKey?priKey=?keyFactory.generatePrivate(rsaPrivKeySpec);?? 首先將PKCS#1的私鑰文件讀取出來(注意去掉減號開頭的注釋內(nèi)容),然后使用Base64解碼讀出的字符串,便得到priKeyData,也就是第一行代碼中的參數(shù)。最后一行得到了私鑰。接下來的用法就沒什么區(qū)別了。 參考文獻:https://community.oracle.com/thread/1529240?start=0&tstart=0
?
二、編寫Java代碼實際測試
2012 年2月23日補充:在標(biāo)準JDK中只是規(guī)定了JCE(JCE (Java Cryptography Extension)?是一組包,它們提供用于加密、密鑰生成和協(xié)商以及 Message Authentication Code(MAC)算法的框架和實現(xiàn)。它提供對對稱、不對稱、塊和流密碼的加密支持,它還支持安全流和密封的對象。)接口,但是內(nèi)部實現(xiàn)需要自己或者第三 方提供。因此我們這里使用bouncycastle的開源的JCE實現(xiàn)包,下載地址:http://bouncycastle.org /latest_releases.html,我使用的是bcprov-jdk16-146.jar,這是在JDK1.6環(huán)境下使用的。如果需要其他 JDK版本下的實現(xiàn),可以在之前的下載頁面中找到對應(yīng)版本。
下面來看一下我實現(xiàn)的代碼:
[java] view plaincopy
package?net.csdn.blog.chaijunkun;?? ?? import?java.io.BufferedReader;?? import?java.io.IOException;?? import?java.io.InputStream;?? import?java.io.InputStreamReader;?? import?java.security.InvalidKeyException;?? import?java.security.KeyFactory;?? import?java.security.KeyPair;?? import?java.security.KeyPairGenerator;?? import?java.security.NoSuchAlgorithmException;?? import?java.security.SecureRandom;?? import?java.security.interfaces.RSAPrivateKey;?? import?java.security.interfaces.RSAPublicKey;?? import?java.security.spec.InvalidKeySpecException;?? import?java.security.spec.PKCS8EncodedKeySpec;?? import?java.security.spec.X509EncodedKeySpec;?? ?? import?javax.crypto.BadPaddingException;?? import?javax.crypto.Cipher;?? import?javax.crypto.IllegalBlockSizeException;?? import?javax.crypto.NoSuchPaddingException;?? ?? import?org.bouncycastle.jce.provider.BouncyCastleProvider;?? ?? import?sun.misc.BASE64Decoder;?? ?? public?class?RSAEncrypt?{?? ?????? ????private?static?final?String?DEFAULT_PUBLIC_KEY=??? ????????"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w"?+?"\r"?+?? ????????"/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht"?+?"\r"?+?? ????????"Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg"?+?"\r"?+?? ????????"XIlk3gdhnzh+uoEQywIDAQAB"?+?"\r";?? ?????? ????private?static?final?String?DEFAULT_PRIVATE_KEY=?? ????????"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG"?+?"\r"?+?? ????????"6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4"?+?"\r"?+?? ????????"1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA"?+?"\r"?+?? ????????"ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm"?+?"\r"?+?? ????????"u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j"?+?"\r"?+?? ????????"DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo"?+?"\r"?+?? ????????"MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ"?+?"\r"?+?? ????????"DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM"?+?"\r"?+?? ????????"6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ"?+?"\r"?+?? ????????"FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA"?+?"\r"?+?? ????????"2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX"?+?"\r"?+?? ????????"TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh"?+?"\r"?+?? ????????"Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo"?+?"\r"?+?? ????????"1NMLzI2ZfUoX"?+?"\r";?? ?? ???? ????private?RSAPrivateKey?privateKey;?? ?? ???? ????private?RSAPublicKey?publicKey;?? ?????? ???? ????private?static?final?char[]?HEX_CHAR=?{'0',?'1',?'2',?'3',?'4',?'5',?'6',?'7',?'8',?'9',?'a',?'b',?'c',?'d',?'e',?'f'};?? ?????? ?? ???? ????public?RSAPrivateKey?getPrivateKey()?{?? ????????return?privateKey;?? ????}?? ?? ???? ????public?RSAPublicKey?getPublicKey()?{?? ????????return?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();?? ????}?? ?? ???? ????public?void?loadPublicKey(InputStream?in)?throws?Exception{?? ????????try?{?? ????????????BufferedReader?br=?new?BufferedReader(new?InputStreamReader(in));?? ????????????String?readLine=?null;?? ????????????StringBuilder?sb=?new?StringBuilder();?? ????????????while((readLine=?br.readLine())!=null){?? ????????????????if(readLine.charAt(0)=='-'){?? ????????????????????continue;?? ????????????????}else{?? ????????????????????sb.append(readLine);?? ????????????????????sb.append('\r');?? ????????????????}?? ????????????}?? ????????????loadPublicKey(sb.toString());?? ????????}?catch?(IOException?e)?{?? ????????????throw?new?Exception("公鑰數(shù)據(jù)流讀取錯誤");?? ????????}?catch?(NullPointerException?e)?{?? ????????????throw?new?Exception("公鑰輸入流為空");?? ????????}?? ????}?? ?? ?? ???? ????public?void?loadPublicKey(String?publicKeyStr)?throws?Exception{?? ????????try?{?? ????????????BASE64Decoder?base64Decoder=?new?BASE64Decoder();?? ????????????byte[]?buffer=?base64Decoder.decodeBuffer(publicKeyStr);?? ????????????KeyFactory?keyFactory=?KeyFactory.getInstance("RSA");?? ????????????X509EncodedKeySpec?keySpec=?new?X509EncodedKeySpec(buffer);?? ????????????this.publicKey=?(RSAPublicKey)?keyFactory.generatePublic(keySpec);?? ????????}?catch?(NoSuchAlgorithmException?e)?{?? ????????????throw?new?Exception("無此算法");?? ????????}?catch?(InvalidKeySpecException?e)?{?? ????????????throw?new?Exception("公鑰非法");?? ????????}?catch?(IOException?e)?{?? ????????????throw?new?Exception("公鑰數(shù)據(jù)內(nèi)容讀取錯誤");?? ????????}?catch?(NullPointerException?e)?{?? ????????????throw?new?Exception("公鑰數(shù)據(jù)為空");?? ????????}?? ????}?? ?? ???? ????public?void?loadPrivateKey(InputStream?in)?throws?Exception{?? ????????try?{?? ????????????BufferedReader?br=?new?BufferedReader(new?InputStreamReader(in));?? ????????????String?readLine=?null;?? ????????????StringBuilder?sb=?new?StringBuilder();?? ????????????while((readLine=?br.readLine())!=null){?? ????????????????if(readLine.charAt(0)=='-'){?? ????????????????????continue;?? ????????????????}else{?? ????????????????????sb.append(readLine);?? ????????????????????sb.append('\r');?? ????????????????}?? ????????????}?? ????????????loadPrivateKey(sb.toString());?? ????????}?catch?(IOException?e)?{?? ????????????throw?new?Exception("私鑰數(shù)據(jù)讀取錯誤");?? ????????}?catch?(NullPointerException?e)?{?? ????????????throw?new?Exception("私鑰輸入流為空");?? ????????}?? ????}?? ?? ????public?void?loadPrivateKey(String?privateKeyStr)?throws?Exception{?? ????????try?{?? ????????????BASE64Decoder?base64Decoder=?new?BASE64Decoder();?? ????????????byte[]?buffer=?base64Decoder.decodeBuffer(privateKeyStr);?? ????????????PKCS8EncodedKeySpec?keySpec=?new?PKCS8EncodedKeySpec(buffer);?? ????????????KeyFactory?keyFactory=?KeyFactory.getInstance("RSA");?? ????????????this.privateKey=?(RSAPrivateKey)?keyFactory.generatePrivate(keySpec);?? ????????}?catch?(NoSuchAlgorithmException?e)?{?? ????????????throw?new?Exception("無此算法");?? ????????}?catch?(InvalidKeySpecException?e)?{?? ????????????throw?new?Exception("私鑰非法");?? ????????}?catch?(IOException?e)?{?? ????????????throw?new?Exception("私鑰數(shù)據(jù)內(nèi)容讀取錯誤");?? ????????}?catch?(NullPointerException?e)?{?? ????????????throw?new?Exception("私鑰數(shù)據(jù)為空");?? ????????}?? ????}?? ?? ???? ????public?byte[]?encrypt(RSAPublicKey?publicKey,?byte[]?plainTextData)?throws?Exception{?? ????????if(publicKey==?null){?? ????????????throw?new?Exception("加密公鑰為空,?請設(shè)置");?? ????????}?? ????????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("明文數(shù)據(jù)已損壞");?? ????????}?? ????}?? ?? ???? ????public?byte[]?decrypt(RSAPrivateKey?privateKey,?byte[]?cipherData)?throws?Exception{?? ????????if?(privateKey==?null){?? ????????????throw?new?Exception("解密私鑰為空,?請設(shè)置");?? ????????}?? ????????Cipher?cipher=?null;?? ????????try?{?? ????????????cipher=?Cipher.getInstance("RSA",?new?BouncyCastleProvider());?? ????????????cipher.init(Cipher.DECRYPT_MODE,?privateKey);?? ????????????byte[]?output=?cipher.doFinal(cipherData);?? ????????????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("密文數(shù)據(jù)已損壞");?? ????????}????????? ????}?? ?? ?????? ???? ????public?static?String?byteArrayToString(byte[]?data){?? ????????StringBuilder?stringBuilder=?new?StringBuilder();?? ????????for?(int?i=0;?i<data.length;?i++){?? ???????????? ????????????stringBuilder.append(HEX_CHAR[(data[i]?&?0xf0)>>>?4]);?? ???????????? ????????????stringBuilder.append(HEX_CHAR[(data[i]?&?0x0f)]);?? ????????????if?(i<data.length-1){?? ????????????????stringBuilder.append('?');?? ????????????}?? ????????}?? ????????return?stringBuilder.toString();?? ????}?? ?? ?? ????public?static?void?main(String[]?args){?? ????????RSAEncrypt?rsaEncrypt=?new?RSAEncrypt();?? ???????? ?? ???????? ????????try?{?? ????????????rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY);?? ????????????System.out.println("加載公鑰成功");?? ????????}?catch?(Exception?e)?{?? ????????????System.err.println(e.getMessage());?? ????????????System.err.println("加載公鑰失敗");?? ????????}?? ?? ???????? ????????try?{?? ????????????rsaEncrypt.loadPrivateKey(RSAEncrypt.DEFAULT_PRIVATE_KEY);?? ????????????System.out.println("加載私鑰成功");?? ????????}?catch?(Exception?e)?{?? ????????????System.err.println(e.getMessage());?? ????????????System.err.println("加載私鑰失敗");?? ????????}?? ?? ???????? ????????String?encryptStr=?"Test?String?chaijunkun";?? ?? ????????try?{?? ???????????? ????????????byte[]?cipher?=?rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(),?encryptStr.getBytes());?? ???????????? ????????????byte[]?plainText?=?rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(),?cipher);?? ????????????System.out.println("密文長度:"+?cipher.length);?? ????????????System.out.println(RSAEncrypt.byteArrayToString(cipher));?? ????????????System.out.println("明文長度:"+?plainText.length);?? ????????????System.out.println(RSAEncrypt.byteArrayToString(plainText));?? ????????????System.out.println(new?String(plainText));?? ????????}?catch?(Exception?e)?{?? ????????????System.err.println(e.getMessage());?? ????????}?? ????}?? }?? 代碼中我提供了兩種加載公鑰和私鑰的方式。 按流來讀取:適合在Android應(yīng)用中按ID索引資源得到InputStream的方式;
按字符串來讀取:就像代碼中展示的那樣,將密鑰內(nèi)容按行存儲到靜態(tài)常量中,按String類型導(dǎo)入密鑰。
?
運行上面的代碼,會顯示如下信息: [plain] view plaincopy
加載公鑰成功?? 加載私鑰成功?? 密文長度:128?? 35?b4?6f?49?69?ae?a3?85?a2?a5?0d?45?75?00?23?23?e6?70?69?b4?59?ae?72?6f?6d?d3?43?e1?d3?44?85?eb?04?57?2c?46?3e?70?09?4d?e6?4c?83?50?c7?56?75?80?c7?e1?31?64?57?c8?e3?46?a7?ce?57?31?ac?cd?21?89?89?8f?c1?24?c1?22?0c?cb?70?6a?0d?fa?c9?38?80?ba?2e?e1?29?02?ed?45?9e?88?e9?23?09?87?af?ad?ab?ac?cb?61?03?3c?a1?81?56?a5?de?c4?79?aa?3e?48?ee?30?3d?bc?5b?47?50?75?9f?fd?22?87?9e?de?b1?f4?e8?b2?? 明文長度:22?? 54?65?73?74?20?53?74?72?69?6e?67?20?63?68?61?69?6a?75?6e?6b?75?6e?? Test?String?chaijunkun?? 在main函數(shù)中我注釋掉了”rsaEncrypt.genKeyPair()“,這個方法是用來隨機生成密鑰對的(只生成、使用,不存儲)。當(dāng)不使用文件密鑰時,可以將載入密鑰的代碼注釋,啟用本方法,也可以跑通代碼。 加載公鑰與加載私鑰的不同點在于公鑰加載時使用的是X509EncodedKeySpec(X509編碼的Key指令),私鑰加載時使用的是PKCS8EncodedKeySpec(PKCS#8編碼的Key指令)。
?
2012 年2月22日補充:在android軟件開發(fā)的過程中,發(fā)現(xiàn)上述代碼不能正常工作,主要原因在于sun.misc.BASE64Decoder類在 android開發(fā)包中不存在。因此需要特別在網(wǎng)上尋找rt.jar的源代碼,至于JDK的src.zip中的源代碼,這個只是JDK中的部分源代碼,上 述的幾個類的代碼都沒有。經(jīng)過尋找并添加,上述代碼在android應(yīng)用中能夠很好地工作。其中就包含這個類的對應(yīng)代碼。另外此類還依賴于 CEFormatException、CEStreamExhausted、CharacterDecoder和CharacterEncoder類和異 常定義。
?
2012 年2月23日補充:起初,我寫這篇文章是想不依賴于任何第三方包來實現(xiàn)RSA的加密與解密,然而后續(xù)遇到了問題。由于在加密方法encrypt和解密方法 decrypt中都要建立一個Cipher對象,這個對象只能通過getInstance來獲取實例。它有兩種:第一個是只指定算法,不指定提供者 Provider的;第二個是兩個都要指定的。起初沒有指定,代碼依然能夠跑通,但是你會發(fā)現(xiàn),每次加密的結(jié)果都不一樣。后來分析才知道Cipher對象 使用的公私鑰是內(nèi)部自己隨機生成的,不是代碼中指定的公私鑰。奇怪的是,這種不指定Provider的代碼能夠在android應(yīng)用中跑通,而且每次加密 的結(jié)果都相同。我想,android的SDK中除了系統(tǒng)的一些開發(fā)函數(shù)外,自己也實現(xiàn)了JDK的功能,可能在它自己的JDK中已經(jīng)提供了相應(yīng)的 Provider,才使得每次加密結(jié)果相同。當(dāng)我像網(wǎng)上的示例代碼那樣加入了bouncycastle的Provider后,果然每次加密的結(jié)果都相同了。
?
參考文獻:
RSA介紹:http://baike.baidu.com/view/7520.htm
OpenSSL介紹:http://baike.baidu.com/view/300712.htm
密鑰對生成:http://www.howforge.com/how-to-generate-key-pair-using-openssl
私鑰編碼格式轉(zhuǎn)換:http://shuany.iteye.com/blog/730910
JCE介紹:http://baike.baidu.com/view/1855103.htm
總結(jié)
以上是生活随笔 為你收集整理的Java中使用OpenSSL生成的RSA公私钥进行数据加解密 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。