视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Java 证书认证
2025-09-29 18:12:26 责编:小OO
文档
JAVA 证书认证

证书(Certificate,也称public-key certificate)是用某种签名算法对某些内容(比如公钥)进行数字签名后得到的、可以用来当成信任关系中介的数字凭证。证书发行机构通过发行证书告知证书使用者或实体其公钥(public-key)以及其它一些辅助信息。证书在电子商务安全交易中有着广泛的应用,证书发行机构也称 CA(Certificate Authority)。

生成自签证书

第一步,用-genkey命令选项,产生公私密钥对。在控制台界面输入:keytool -genkey -alias testkeypair -keyalg RSA -keysize 1024 -sigalg MD5withRSA。这里的-alias表示使用这对公私密钥产生新的keystore入口的别名(keystore是用来存放管理密钥对和证书链的,缺省位置是在使用者主目录下,以.keystore为名的隐藏文件,当然也可指定某个路径存放.keystore文件);-keyalg是产生公私钥对所用的算法,这里是RSA;-keysize定义密钥的长度;-sigalg是签名算法,选择MD5withRSA,即用RSA签名,然后用MD5哈希算法摘要。接下来,系统会提示进行一些输入:

输入keystore密码:   abc123

您的名字与姓氏是什么?

   [Unknown]:   Li

您的组织单位名称是什么?

   [Unknown]:   InfosecLab

您的组织名称是什么?

   [Unknown]:   InfosecLab Group

您所在的城市或区域名称是什么?

   [Unknown]:   Beijing

您所在的州或省份名称是什么?

   [Unknown]:   Beijing

该单位的两字母国家代码是什么

   [Unknown]:   CN

CN=Li, OU=InfosecLab, O=InfosecLab Group, L=Beijing, ST=Beijing, C=CN 正确吗?

[否]

:   y

输入的主密码 (如果和 keystore 密码相同,按回车):

第二步,产生自签证书,输入以下命令:

keytool -selfcert -alias testkeypair -dname "CN=Li, OU=InfosecLab, O=InfosecLab 

Group, L=Beijing, ST=Beijing, C=CN" 

输入keystore密码:   abc123

第三步,导出自签证书,由上面两步产生的证书,已经存放在以“testkeypair”为别名的keystore入口了,如果使用其文件,必须导出证书。输入:

keytool -export -rfc -alias testkeypair -file mycert.crt   

输入keystore密码:   abc123

保存在文件中的认证 

这样,就得到了一个自签的证书mycert.crt。注意,选项rfc是把证书输出为RFC1421定义的、用Base最终编码的格式。 

 

读取证书

Java为安全应用提供了丰富的API,J2SDK1.4 的JSSE (JavaTM Secure Socket Extension) 包括javax.security.certificate包,并且提供对证书的操作方法。而对证书的读操作,只用 java.security.cert. CertificateFactory和java.security.cert.X509Certificate就可以了。下面是读取证书内容的部分代码:

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 = {"

证书字段标记内容" };

public CARead(String CertName) {

   CA_Name=CertName;

   

   JTabbedPane tabbedPane = new JTabbedPane();

   JPanel panelNormal = new JPanel();

   tabbedPane.addTab("普通信息", panelNormal);    

   JPanel panelAll=new JPanel();

   panelAll.setLayout(new BorderLayout());

   tabbedPane.addTab("所有信息",panelAll);

   JPanel panelBase=new JPanel();

   panelBase.setLayout(new BorderLayout());

   tabbedPane.addTab("Base编码信息",panelBase);

   

   Read_Normal(panelNormal);

   

   Read_Bin(panelAll);

   

   Read_Raw(panelBase);

   tabbedPane.setSelectedIndex(0);

   setLayout(new GridLayout(1, 1)); 

   add(tabbedPane);

}

      

}

定义证书信息的读取函数如下:

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]="标题";

   CA_ItemData[2][1]=Field;

   

   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这个函数。其中CertificateFactory.generateCertificate() 这个函数可以从证书标准编码(RFC1421定义)中解出可读信息。Read_Bin函数代码如下:

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   {    

    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; 

}

最后用这个小程序看一看刚才生成的证书mycert.crt内容,把文件名写入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的内容显示如图2所示,所有信息和Base的显示内容,这里不再列举。 

 

 

图2 证书mycert.crt的内容显示

来源:(http://blog.sina.com.cn/s/blog_5ec49e1d0100cp55.html) - JAVA 证书认证_周超_新浪博客 

现在已经读取了证书的一些内容,那么怎样使用证书呢?我们可以假设A和B要共享一个绝密的文件F,B信任并拥有A的证书,也就是说B拥有A的公钥。那么A通过A和B共知的加密算法(对称密钥算法,比如DES算法)先加密文件F,然后对加密后的F进行签名和散列摘要(比如MD5算法,目的是保证文件的完整性),然后把F发送到B。B收到文件后,先用A的证书中的公钥验证签名,然后再用通过共知的加密算法解密,就可以得到原文件了。这里使用的数字签名,可以保证B得到的文件,就是A的,A不能否认其不拥有文件F,因为只有A拥有可以让A的公钥验证其签名的私钥,同时这里使用DES算法加密,使得文件有保密性。

使用DES算法的加密解密函数类似,这里不对加密算法做进一步讨论,详细请看J2SDK的JSE部分内容,加密签名、解密验证文件结构见图3。

图3 加密签名、解密验证文件结构图

加密函数中的desKeyData存放DES加密密钥,如果要在程序中指定,可以设置为:

static byte[] desKeyData = { (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, 

(byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08 };

加密函数写成:

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就是加密后的内容,outFileName保存加密后文件的文件名。把cdes.init(Cipher.ENCRYPT_MODE, secretKey)换成cdes.init(Cipher.DECRYPT_MODE, secretKey)就是解密文件了。

文件加密后就要对文件签名,保证A发送到B的文件不可伪造。下面是用存放在.keystore中的私钥进行签名的函数,签名使用的摘要算法是MD5。其中 sigText是被签名内容的输入数组,outFileName是保存签名后输出文件的名称,KeyPassword是读取Keystore使用的密码,KeyStorePath是存放.keystore文件的路径,函数代码如下:

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    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();

}

}

验证签名需要存放签名文件和被签名的文件以及证书,其中,updateData存放被签名文件的内容,sigedText存放得到的签名内容,CertName是证书名。验证签名代码如下:

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产生两个自签的签名证书,或者到某个CA去申请两个证书。用Java编写加密和验证程序,上述例子只是一个非常简单的证书应用,实际协议对证书的使用(比如SSL)要比这个复杂多了。下载本文

显示全文
专题