<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    JAVA 數(shù)字簽名


                                                                                                                                                        來源:賽迪網(wǎng)
                                                                                                                                                       作者:李素科

    (轉(zhuǎn)載序:網(wǎng)上找的好文章,一篇就把我找了幾天的所有東西都概括進(jìn)來了,真是非常感謝作者:李素科  其實(shí)在找資料的過程當(dāng)中,主要沒解決的問題在于如何獲得KeyStore文件中的PrivateKey,本來查jsdk 1.4 api文檔就可以知道了,但是居然從上到下看了2遍,沒有發(fā)現(xiàn)這個(gè)方法:load()  .......)
                                                                                                                                                       
    證書(Certificate,也稱public-key certificate)是用某種簽名算法對某些內(nèi)容(比如公鑰)進(jìn)行數(shù)字簽名后得到的、可以用來當(dāng)成信任關(guān)系中介的數(shù)字憑證。證書發(fā)行機(jī)構(gòu)通過發(fā)行證書告知證書使用者或?qū)嶓w其公鑰(public-key)以及其它一些輔助信息。證書在電子商務(wù)安全交易中有著廣泛的應(yīng)用,證書發(fā)行機(jī)構(gòu)也稱CA(Certificate Authority)。

    應(yīng)用證書



    證書在公鑰加密應(yīng)用中的作用是保證公鑰在某些可信的機(jī)構(gòu)發(fā)布,其在協(xié)議SSL、電子交易協(xié)議SET等方面有重要的應(yīng)用。圖1顯示了一個(gè)最簡單的證書應(yīng)用方法:



    圖1 證書應(yīng)用方法



    證書的應(yīng)用步驟是:

    (1) A把自己的公鑰PKA送到CA(Certificate Authority);

    (2) CA用自己的私鑰和A的公鑰生成A的證書,證書內(nèi)包括CA的數(shù)字簽名。簽名對象包括需要在證書中說明的內(nèi)容,比如A的公鑰、時(shí)間戳、序列號(hào)等,為了簡化這里不妨假設(shè)證書中只有三項(xiàng)內(nèi)容:A的公鑰PKA、時(shí)間戳TIME1、序列號(hào)IDA。那么CA發(fā)送給A的簡單證書憑證可表達(dá)為:CertA=Eca[TIME1,IDA,PKA];

    (3) B同樣把自己的公鑰PKB送到CA;

    (4) B得到CA發(fā)布的證書CertB;

    (5) A告知B證書CertA;

    (6) B告知A證書CertB。

    A、B各自得到對方證書后,利用從CA得到的公鑰(在CA的自簽證書中)驗(yàn)證彼此對方的證書是否有效,如果有效,那么就得到了彼此的公鑰。利用對方的公鑰,可以加密數(shù)據(jù),也可以用來驗(yàn)證對方的數(shù)字簽名。

    本文為了方便說明,并沒有使用從CA獲得的證書,而是通信雙方各自產(chǎn)生自簽證書,也就是說圖1的A和B并沒有經(jīng)過CA,不過前提是A和B之間是互相擁有對方的證書。

    證書的內(nèi)容和意義如表1所示(這里以通用X .509證書格式為例)。

    表1 證書內(nèi)容和意義


    證書內(nèi)容 意義
    Version 告訴這個(gè)X.509證書是哪個(gè)版本的,目前有v1、V2、v3
    Serial Number 由證書分發(fā)機(jī)構(gòu)設(shè)置證書的序列號(hào)
    Signature Algorithm Identifier 證書采用什么樣的簽名算法
    Issuer Name 證書發(fā)行者名,也就是給這個(gè)證書簽名的機(jī)構(gòu)名
    Validity Period 證書有效時(shí)間范圍
    Subject Name 被證書發(fā)行機(jī)構(gòu)簽名后的公鑰擁有者或?qū)嶓w的名字,采用X.500協(xié)議,在Internet上的標(biāo)志是惟一的。例如:CN=Java,OU=Infosec,O=Infosec Lab,C=CN表示一個(gè)subject name。



    對證書的詳細(xì)定義及其應(yīng)用相關(guān)的各種協(xié)議,這里不加詳細(xì)說明,詳細(xì)細(xì)節(jié)請查看RFC2450、RFC2510、RFC2511、RFC2527、RFC2528、RFC2559、RFC2560、RFC2585、RFC2587等文檔。

    生成自簽證書



    個(gè)人或機(jī)構(gòu)可以從信任的證書分發(fā)機(jī)構(gòu)申請得到證書,比如說,可以從http://ca.pku.edu.cn 得到一個(gè)屬于個(gè)人的證書。這里可以利用J2SDK的安全工具keytool手工產(chǎn)生自簽證書,所謂自簽證書是指證書中的“Subject Name”和“Issuer Name”相同的證書。

    下面產(chǎn)生一個(gè)自簽證書。安裝完J2SDK(這里用的是J2SDK1.4)后,在J2SDK安裝目錄的bin目錄下,有一個(gè)keytool的可執(zhí)行程序。利用keytool產(chǎn)生自簽證書的步驟如下:

    第一步,用-genkey命令選項(xiàng),產(chǎn)生公私密鑰對。在控制臺(tái)界面輸入:keytool -genkey -alias testkeypair -keyalg RSA -keysize 1024 -sigalg MD5withRSA。這里的-alias表示使用這對公私密鑰產(chǎn)生新的keystore入口的別名(keystore是用來存放管理密鑰對和證書鏈的,缺省位置是在使用者主目錄下,以.keystore為名的隱藏文件,當(dāng)然也可指定某個(gè)路徑存放.keystore文件);-keyalg是產(chǎn)生公私鑰對所用的算法,這里是RSA;-keysize定義密鑰的長度;-sigalg是簽名算法,選擇MD5withRSA,即用RSA簽名,然后用MD5哈希算法摘要。接下來,系統(tǒng)會(huì)提示進(jìn)行一些輸入:

    輸入keystore密碼:  abc123
                您的名字與姓氏是什么?
                [Unknown]:  Li
                您的組織單位名稱是什么?
                [Unknown]:  InfosecLab
                您的組織名稱是什么?
                [Unknown]:  InfosecLab Group
                您所在的城市或區(qū)域名稱是什么?
                [Unknown]:  Beijing
                您所在的州或省份名稱是什么?
                [Unknown]:  Beijing
                該單位的兩字母國家代碼是什么
                [Unknown]:  CN
                CN=Li, OU=InfosecLab, O=InfosecLab Group, L=Beijing, ST=Beijing, C=CN 正確嗎?
                [否]:  y
                輸入<testkeypair>的主密碼 (如果和 keystore 密碼相同,按回車):


    第二步,產(chǎn)生自簽證書,輸入以下命令:

    keytool -selfcert -alias testkeypair -dname "CN=Li, OU=InfosecLab, O=InfosecLab
                Group, L=Beijing, ST=Beijing, C=CN"
                輸入keystore密碼:  abc123


    第三步,導(dǎo)出自簽證書,由上面兩步產(chǎn)生的證書,已經(jīng)存放在以“testkeypair”為別名的keystore入口了,如果使用其文件,必須導(dǎo)出證書。輸入:

    keytool -export -rfc -alias testkeypair -file mycert.crt
                輸入keystore密碼:  abc123
                保存在文件中的認(rèn)證 <mycert.crt>


    這樣,就得到了一個(gè)自簽的證書mycert.crt。注意,選項(xiàng)rfc是把證書輸出為RFC1421定義的、用Base64最終編碼的格式。

    讀取證書



    Java為安全應(yīng)用提供了豐富的API,J2SDK1.4 的JSSE (JavaTM Secure Socket Extension) 包括javax.security.certificate包,并且提供對證書的操作方法。而對證書的讀操作,只用java.security.cert. CertificateFactory和java.security.cert.X509Certificate就可以了。下面是讀取證書內(nèi)容的部分代碼:

    import javax.swing.*;
                import java.awt.*;
                import java.awt.event.*;
                import javax.swing.table.*;
                import java.security.cert.CertificateFactory;
                import java.security.cert.X509Certificate;
                import java.io.*;
                public class CARead extends JPanel {
                private String CA_Name;
                private String CA_ItemData[][] = new String[9][2];
                private String[] columnNames = {"證書字段標(biāo)記","內(nèi)容" };
                public CARead(String CertName) {
                CA_Name=CertName;
                /* 三個(gè)Panel用來顯示證書內(nèi)容*/
                JTabbedPane tabbedPane = new JTabbedPane();
                JPanel panelNormal = new JPanel();
                tabbedPane.addTab("普通信息", panelNormal);
                JPanel panelAll=new JPanel();
                panelAll.setLayout(new BorderLayout());
                tabbedPane.addTab("所有信息",panelAll);
                JPanel panelBase64=new JPanel();
                panelBase64.setLayout(new BorderLayout());
                tabbedPane.addTab("Base64編碼信息",panelBase64);
                /* 讀取證書常規(guī)信息 */
                Read_Normal(panelNormal);
                /* 讀取證書文件字符串表示內(nèi)容 */
                Read_Bin(panelAll);
                /* 讀取證原始Base64編碼形式的證書文件 */
                Read_Raw(panelBase64);
                tabbedPane.setSelectedIndex(0);
                setLayout(new GridLayout(1, 1));
                add(tabbedPane);
                }
                /*以下是定義的Read_Normal(),Read_Bin(),Read_Raw()以及main()
                這里省略...   */
                }


    定義證書信息的讀取函數(shù)如下:

    private int Read_Normal(JPanel panel){
                String Field;
                try{
                CertificateFactory certificate_factory=CertificateFactory.getInstance("X.509");
                FileInputStream file_inputstream=new FileInputStream(CA_Name);
                X509Certificate
                x509certificate=(X509Certificate)certificate_factory.generateCertificate
                (file_inputstream);
                Field=x509certificate.getType();
                CA_ItemData[0][0]="類型";
                CA_ItemData[0][1]=Field;
                Field=Integer.toString(x509certificate.getVersion());
                CA_ItemData[1][0]="版本";
                CA_ItemData[1][1]=Field;
                Field=x509certificate.getSubjectDN().getName();
                CA_ItemData[2][0]="標(biāo)題";
                CA_ItemData[2][1]=Field;
                /* 以下類似,這里省略
                Field=x509certificate.getNotBefore().toString();得到開始有效日期
                Field=x509certificate. getNotAfter().toString();得到截止日期
                Field=x509certificate.getSerialNumber().toString(16);得到序列號(hào)
                Field=x509certificate.getIssuerDN().getName();得到發(fā)行者名
                Field=x509certificate.getSigAlgName();得到簽名算法
                Field=x509certificate.getPublicKey().getAlgorithm();得到公鑰算法 */
                file_inputstream.close();
                final JTable table = new JTable(CA_ItemData, columnNames);
                TableColumn tc=null;
                tc = table.getColumnModel().getColumn(1);
                tc.setPreferredWidth(600);
                panel.add(table);
                }catch(Exception exception){
                exception.printStackTrace();
                return -1;
                }
                return 0;
                }


    如果以字符串形式讀取證書,加入下面Read_Bin這個(gè)函數(shù)。其中CertificateFactory.generateCertificate() 這個(gè)函數(shù)可以從證書標(biāo)準(zhǔn)編碼(RFC1421定義)中解出可讀信息。Read_Bin函數(shù)代碼如下:

    private int Read_Bin(JPanel panel){
                try{
                FileInputStream file_inputstream=new FileInputStream(CA_Name);
                DataInputStream data_inputstream=new DataInputStream(file_inputstream);
                CertificateFactory certificatefactory=CertificateFactory.getInstance("X.509");
                byte[] bytes=new byte[data_inputstream.available()];
                data_inputstream.readFully(bytes);
                ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
                JEditorPane Cert_EditorPane;
                Cert_EditorPane=new JEditorPane();
                while(bais.available()>0){
                X509Certificate
                Cert=(X509Certificate)certificatefactory.generateCertificate(bais);
                Cert_EditorPane.setText(Cert_EditorPane.getText()+Cert.toString());
                }
                Cert_EditorPane.disable();
                JScrollPane edit_scroll=new JScrollPane(Cert_EditorPane);
                panel.add(edit_scroll);
                file_inputstream.close();
                data_inputstream.close();
                }catch( Exception exception){
                exception.printStackTrace();
                return -1;
                }
                return 0;
                }


    如果要得到原始證書編碼后的信息,則可用如下代碼:

    private int Read_Raw(JPanel panel){
                try{
                JEditorPane Cert_EditorPane=new JEditorPane();
                String CertText=null;
                File inputFile = new File(CA_Name);
                FileReader in = new FileReader(inputFile);
                char[] buf=new char[2000];
                int len=in.read(buf,0,2000);
                for(int i=1;i<len;i++)
                {
                CertText=CertText+buf[i];
                }
                in.close();
                Cert_EditorPane.setText(CertText);
                Cert_EditorPane.disable();
                JScrollPane edit_scroll=new JScrollPane(Cert_EditorPane);
                panel.add(edit_scroll);
                }catch( Exception exception){
                exception.printStackTrace();
                return -1;
                }
                return 0;
                }


    最后用這個(gè)小程序看一看剛才生成的證書mycert.crt內(nèi)容,把文件名寫入main()中:

    public static void main(String[] args) {
                JFrame frame = new JFrame("證書閱讀器");
                frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {System.exit(0);}
                });
                frame.getContentPane().add(new CARead("mycert.crt"),BorderLayout.CENTER);
                frame.setSize(700, 425);
                frame.setVisible(true);
                }


    證書mycert.crt的內(nèi)容顯示如圖2所示,所有信息和Base64的顯示內(nèi)容,這里不再列舉。



     

    圖2 證書mycert.crt的內(nèi)容顯示
     
    現(xiàn)在已經(jīng)讀取了證書的一些內(nèi)容,那么怎樣使用證書呢?我們可以假設(shè)A和B要共享一個(gè)絕密的文件F,B信任并擁有A的證書,也就是說B擁有A的公鑰。那么A通過A和B共知的加密算法(對稱密鑰算法,比如DES算法)先加密文件F,然后對加密后的F進(jìn)行簽名和散列摘要(比如MD5算法,目的是保證文件的完整性),然后把F發(fā)送到B。B收到文件后,先用A的證書中的公鑰驗(yàn)證簽名,然后再用通過共知的加密算法解密,就可以得到原文件了。這里使用的數(shù)字簽名,可以保證B得到的文件,就是A的,A不能否認(rèn)其不擁有文件F,因?yàn)橹挥蠥擁有可以讓A的公鑰驗(yàn)證其簽名的私鑰,同時(shí)這里使用DES算法加密,使得文件有保密性。

    使用DES算法的加密解密函數(shù)類似,這里不對加密算法做進(jìn)一步討論,詳細(xì)請看J2SDK的JSE部分內(nèi)容,加密簽名、解密驗(yàn)證文件結(jié)構(gòu)見圖3。



    圖3 加密簽名、解密驗(yàn)證文件結(jié)構(gòu)圖


    加密函數(shù)中的desKeyData存放DES加密密鑰,如果要在程序中指定,可以設(shè)置為:

    static byte[] desKeyData = { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04,
                (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08 };


    加密函數(shù)寫成:

    public static void crypt(byte[] cipherText,String outFileName){
                try{
                DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
                SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
                SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
                Cipher cdes = Cipher.getInstance("DES");
                cdes.init(Cipher.ENCRYPT_MODE, secretKey);
                byte[] ct = cdes.doFinal(cipherText);
                try{
                FileOutputStream out=new FileOutputStream(outFileName);
                out.write(ct);
                out.close();
                }catch(IOException e){
                e.printStackTrace();
                }
                }catch (Exception e){
                e.printStackTrace();
                }
                }


    其中ct就是加密后的內(nèi)容,outFileName保存加密后文件的文件名。把cdes.init(Cipher.ENCRYPT_MODE, secretKey)換成cdes.init(Cipher.DECRYPT_MODE, secretKey)就是解密文件了。

    文件加密后就要對文件簽名,保證A發(fā)送到B的文件不可偽造。下面是用存放在.keystore中的私鑰進(jìn)行簽名的函數(shù),簽名使用的摘要算法是MD5。其中sigText是被簽名內(nèi)容的輸入數(shù)組,outFileName是保存簽名后輸出文件的名稱,KeyPassword是讀取Keystore使用的密碼,KeyStorePath是存放.keystore文件的路徑,函數(shù)代碼如下:

    public static void sig(byte[] sigText, String outFileName,String
                KeyPassword,String KeyStorePath){
                char[] kpass;
                int i;
                try{
                KeyStore ks = KeyStore.getInstance("JKS");
                FileInputStream ksfis = new FileInputStream(KeyStorePath);
                BufferedInputStream ksbufin = new BufferedInputStream(ksfis);
                kpass=new char[KeyPassword.length()];
                for(i=0;i<KeyPassword.length();i++)
                kpass[i]=KeyPassword.charAt(i);
                ks.load(ksbufin, kpass);
                PrivateKey priv = (PrivateKey) ks.getKey(KeystoreAlias,kpass );
                Signature rsa=Signature.getInstance("MD5withRSA");
                rsa.initSign(priv);
                rsa.update(sigText);
                byte[] sig=rsa.sign();
                System.out.println("sig is done");
                try{
                FileOutputStream out=new FileOutputStream(outFileName);
                out.write(sig);
                out.close();
                }catch(IOException e){
                e.printStackTrace();
                }
                }catch(Exception e){
                e.printStackTrace();
                }
                }


    驗(yàn)證簽名需要存放簽名文件和被簽名的文件以及證書,其中,updateData存放被簽名文件的內(nèi)容,sigedText存放得到的簽名內(nèi)容,CertName是證書名。驗(yàn)證簽名代碼如下:

    public static void veriSig(byte[] updateData, byte[] sigedText){
                try{
                CertificateFactory
                certificatefactory=CertificateFactory.getInstance("X.509");
                FileInputStream fin=new FileInputStream(CertName);
                X509Certificate
                certificate=(X509Certificate)certificatefactory.generateCertificate(fin);
                PublicKey pub = certificate.getPublicKey();
                Signature rsa=Signature.getInstance("MD5withRSA");
                rsa.initVerify(pub);
                rsa.update(updateData);
                boolean verifies=rsa.verify(sigedText);
                System.out.println("verified "+verifies);
                if(verifies){
                System.out.println("Verify is done!");
                }else{
                System.out.println("verify is not successful");
                }
                }catch(Exception e){
                e.printStackTrace();
                }
                }


    可以用keytool產(chǎn)生兩個(gè)自簽的簽名證書,或者到某個(gè)CA去申請兩個(gè)證書。用Java編寫加密和驗(yàn)證程序,上述例子只是一個(gè)非常簡單的證書應(yīng)用,實(shí)際協(xié)議對證書的使用(比如SSL)要比這個(gè)復(fù)雜多了。
    posted on 2008-10-07 10:26 aisoft 閱讀(879) 評(píng)論(0)  編輯  收藏 所屬分類: J2EE開發(fā)技術(shù)
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    隨筆檔案(2)

    文章分類(12)

    文章檔案(12)

    收藏夾

    搜索

    •  

    最新評(píng)論

    主站蜘蛛池模板: 午夜影院免费观看| 国产一区二区三区在线免费观看| 无码人妻精品中文字幕免费东京热| 久久国产色AV免费看| 情人伊人久久综合亚洲| 久久er国产精品免费观看8| MM131亚洲国产美女久久| 一级一级一级毛片免费毛片| 亚洲成aⅴ人片久青草影院| 国产午夜亚洲精品国产| 国产精品成人免费一区二区| 99久久婷婷国产综合亚洲| 大学生一级毛片免费看| 成年丰满熟妇午夜免费视频| 亚洲人成77777在线播放网站不卡| 亚洲Av无码乱码在线观看性色 | 免费永久在线观看黄网站| 美女被cao网站免费看在线看| 在线亚洲精品自拍| 成人电影在线免费观看| 亚洲欧洲日韩国产综合在线二区| 久久国产乱子伦精品免费一| 亚洲男人的天堂在线播放| 国产成人在线观看免费网站| 疯狂做受xxxx高潮视频免费| 亚洲精品无码成人片在线观看| 国产免费久久精品99久久| 亚洲一区电影在线观看| 成人在线免费观看| 无码精品国产一区二区三区免费 | 亚洲午夜福利AV一区二区无码| 大地资源网高清在线观看免费| 在线观看日本亚洲一区| 亚洲国产aⅴ综合网| h视频在线免费看| 亚洲AV色欲色欲WWW| 亚洲综合伊人久久大杳蕉| 老司机永久免费网站在线观看| 特级毛片爽www免费版| 亚洲AV日韩精品久久久久| 精品久久久久国产免费|