`
zeng_84_long
  • 浏览: 58029 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

java数字签名(签名生成,用证书验证签名)

阅读更多

(转载序:网上找的好文章,一篇就把我找了几天的所有东西都概括进来了,真是非常感谢作者:李素科  其实在找资料的过程当中,主要没解决的问题在于如何获得KeyStore文件中的PrivateKey,本来查jsdk 1.4 api文档就可以知道了,但是居然从上到下看了2遍,没有发现这个方法:load()  .......)
                                                                                                                                                   
证书(Certificate,也称public-key certificate)是用某种签名算法对某些内容(比如公钥)进行数字签名后得到的、可以用来当成信任关系中介的数字凭证。证书发行机构通过发行证书告知证书使用者或实体其公钥(public-key)以及其它一些辅助信息。证书在电子商务安全交易中有着广泛的应用,证书发行机构也称CA(Certificate Authority)。

应用证书



证书在公钥加密应用中的作用是保证公钥在某些可信的机构发布,其在协议SSL、电子交易协议SET等方面有重要的应用。图1显示了一个最简单的证书应用方法:



 

图1 证书应用方法



证书的应用步骤是:

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

(2) CA用自己的私钥和A的公钥生成A的证书,证书内包括CA的数字签名。签名对象包括需要在证书中说明的内容,比如A的公钥、时间戳、序列号等,为了简化这里不妨假设证书中只有三项内容:A的公钥PKA、时间戳TIME1、序列号IDA。那么CA发送给A的简单证书凭证可表达为:CertA=Eca[TIME1,IDA,PKA];

(3) B同样把自己的公钥PKB送到CA;

(4) B得到CA发布的证书CertB;

(5) A告知B证书CertA;

(6) B告知A证书CertB。

A、B各自得到对方证书后,利用从CA得到的公钥(在CA的自签证书中)验证彼此对方的证书是否有效,如果有效,那么就得到了彼此的公钥。利用对方的公钥,可以加密数据,也可以用来验证对方的数字签名。

本文为了方便说明,并没有使用从CA获得的证书,而是通信双方各自产生自签证书,也就是说图1的A和B并没有经过CA,不过前提是A和B之间是互相拥有对方的证书。

证书的内容和意义如表1所示(这里以通用X .509证书格式为例)。

表1 证书内容和意义



 

<ccid_nobr></ccid_nobr>
证书内容 意义
Version 告诉这个X.509证书是哪个版本的,目前有v1、V2、v3
Serial Number 由证书分发机构设置证书的序列号
Signature Algorithm Identifier 证书采用什么样的签名算法
Issuer Name 证书发行者名,也就是给这个证书签名的机构名
Validity Period 证书有效时间范围
Subject Name 被证书发行机构签名后的公钥拥有者或实体的名字,采用X.500协议,在Internet上的标志是惟一的。例如:CN=Java,OU=Infosec,O=Infosec Lab,C=CN表示一个subject name。



对证书的详细定义及其应用相关的各种协议,这里不加详细说明,详细细节请查看RFC2450、RFC2510、RFC2511、RFC2527、RFC2528、RFC2559、RFC2560、RFC2585、RFC2587等文档。

生成自签证书



个人或机构可以从信任的证书分发机构申请得到证书,比如说,可以从http://ca.pku.edu.cn 得到一个属于个人的证书。这里可以利用J2SDK的安全工具keytool手工产生自签证书,所谓自签证书是指证书中的“Subject Name”和“Issuer Name”相同的证书。

下面产生一个自签证书。安装完J2SDK(这里用的是J2SDK1.4)后,在J2SDK安装目录的bin目录下,有一个keytool的可执行程序。利用keytool产生自签证书的步骤如下:

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

<ccid_nobr></ccid_nobr>

<ccid_code></ccid_code>输入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
输入<testkeypair>的主密码 (如果和 keystore 密码相同,按回车):


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

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>keytool -selfcert -alias testkeypair -dname "CN=Li, OU=InfosecLab, O=InfosecLab 
Group, L=Beijing, ST=Beijing, C=CN" 
输入keystore密码:  abc123


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

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>keytool -export -rfc -alias testkeypair -file mycert.crt  
输入keystore密码:  abc123
保存在文件中的认证 <mycert.crt>


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

读取证书



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

<ccid_nobr></ccid_nobr>

<ccid_code></ccid_code>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;
  /* 三个Panel用来显示证书内容*/
  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);
  /* 读取证书常规信息 */
  Read_Normal(panelNormal);
  /* 读取证书文件字符串表示内容 */
  Read_Bin(panelAll);
  /* 读取证原始Base64编码形式的证书文件 */
  Read_Raw(panelBase64);
  tabbedPane.setSelectedIndex(0);
  setLayout(new GridLayout(1, 1)); 
  add(tabbedPane);
 }
  /*以下是定义的Read_Normal(),Read_Bin(),Read_Raw()以及main() 
  这里省略...   */  
}


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

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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;
  /* 以下类似,这里省略 
  Field=x509certificate.getNotBefore().toString();得到开始有效日期
  Field=x509certificate. getNotAfter().toString();得到截止日期
  Field=x509certificate.getSerialNumber().toString(16);得到序列号
  Field=x509certificate.getIssuerDN().getName();得到发行者名
  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这个函数。其中CertificateFactory.generateCertificate() 这个函数可以从证书标准编码(RFC1421定义)中解出可读信息。Read_Bin函数代码如下:

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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;	
}


如果要得到原始证书编码后的信息,则可用如下代码:

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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;	
}


最后用这个小程序看一看刚才生成的证书mycert.crt内容,把文件名写入main()中:

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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所示,所有信息和Base64的显示内容,这里不再列举。



 

图2 证书mycert.crt的内容显示
 
现在已经读取了证书的一些内容,那么怎样使用证书呢?我们可以假设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加密密钥,如果要在程序中指定,可以设置为:

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


加密函数写成:

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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文件的路径,函数代码如下:

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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();
 }
}


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

<ccid_nobr></ccid_nobr>
<ccid_code></ccid_code>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)要比这个复杂多了。

 

分享到:
评论
1 楼 sutongxuevip 2016-07-14  
貌似很深奥的样子。。。

相关推荐

    java数字签名(签名生成,用证书验证签名)[汇编].pdf

    java数字签名(签名生成,用证书验证签名)[汇编].pdf

    java数字签名(签名生成,用证书验证签名).doc

    在Java中,数字签名通常涉及到两个主要过程:签名生成和签名验证。在这个过程中,证书(Certificate)扮演了关键角色,它包含了公钥以及相关的身份信息,并由证书发行机构(CA,Certificate Authority)进行签名,...

    java数字签名(签名生成,用证书验证签名)[定义].pdf

    2. CA 用自己的私钥和 A 的公钥生成 A 的证书,证书内包括 CA 的数字签名。 3. B 同样把自己的公钥 PKB 送到 CA; 4. B 得到 CA 发布的证书 CertB; 5. A 告知 B 证书 CertA; 6. B 告知 A 证书 CertB。 A 和 B ...

    Java 数字签名、数字证书生成源码.zip

    这份"Java 数字签名、数字证书生成源码"的压缩包提供了实现这些功能的代码示例,对于理解和应用Java安全机制非常有帮助。 数字签名是一种用于验证电子信息完整性和发送者身份的技术。它结合了非对称加密算法(如RSA...

    java 国密算法实现包含SM2 SM3 SM4和数字签名、数字证书的验证

    在实际应用中,这些国密算法和相关功能通常会结合使用,例如,使用SM2生成公私钥对,用SM3计算消息的哈希值,然后用SM2的私钥进行数字签名,最后用SM2的公钥和SM3的哈希值验证签名。同时,数字证书可以用来封装和...

    Java 数字签名、数字证书生成源码.rar

    压缩包内的"codefans.net"可能是一个示例代码或者一个指向其他资源的链接,这可能包含如何使用Java的`KeyPairGenerator`生成密钥对,使用`Signature`进行签名,以及如何处理和验证数字证书的完整流程。 通过学习并...

    Java 数字签名、数字证书生成源码

    Java 数字签名和数字证书是网络安全领域中的重要概念,它们在确保数据的完整性和认证信息来源的可靠性方面发挥着关键作用。在这个Java源码包中,我们可能找到了用于生成数字签名和数字证书的相关代码,这对于理解和...

    Java 数字签名、数字证书生成源码.7z

    (3)接收方用公钥验证签名。核心类包括`Signature`,它提供了创建和验证数字签名的功能。`KeyPairGenerator`用于生成密钥对,而`KeyPair`包含私钥和公钥。 2. **数字证书** 数字证书是包含公开密钥及其拥有者信息的...

    java代码生成数字证书

    在此,我们将深入探讨这些主题,以便理解如何在Java环境中生成和使用数字证书。 1. **数字证书**:数字证书是一种电子文档,用于验证网络上的实体身份。它包含了公钥、实体的标识信息(如域名或个人姓名)、证书...

    java生成及验证android签名文件源码及生成签名文件

    生成Android签名文件通常使用Java的`keytool`命令行工具,它包含在Java Development Kit (JDK) 中。以下是基本的生成流程: 1. 运行`keytool -genkeypair`命令,指定输出的keystore文件名、密码以及开发者信息(如...

    Java实现浏览器CA证书的签名和验证

    在Java中,可以使用keytool工具来生成自签名证书,或者生成CSR(证书签名请求)提交给CA机构获得正规的数字证书。在实现数字证书的签名和验证过程中,Java提供了一系列用于操作X.509证书的API,如java.security.cert...

    数字签名、数字证书分析与Java实现.rar_java 签名 证书_数字签名 rsa_证书_证书解密_证书验证

    在描述中提到的“创建数字证书”,这通常涉及使用KeyPairGenerator生成密钥对,然后使用CertificateFactory和相关数据(如密钥对、主体和颁发者信息)创建自签名证书。这个过程在Java中可以通过java.security....

    Certificate_java_java数字签名_java签名证书_数字证书_

    这确保了只有拥有私钥的人才能创建有效的签名,而任何人都可以使用公钥来验证签名。 在Java中处理这些概念时,你需要了解以下关键知识点: 1. **密钥对生成**:使用`java.security.KeyPairGenerator`生成公钥和...

    Java 数字签名和验证

    在Java中,我们可以使用`java.security.Signature`类的`initSign(PrivateKey)`方法初始化签名对象,并使用`update(byte[])`方法处理要签名的数据,最后用`sign()`方法生成签名。对应的验证过程是使用`initVerify...

    PDF数字签名(ITEXT+keytool)

    在本项目中,我们使用了Java的ITEXT库来处理PDF文档,以及keytool工具来生成数字证书。以下是关于这些知识点的详细说明: 1. **PDF数字签名**:PDF数字签名是一种用于验证PDF文档未经修改的机制。它使用非对称加密...

    java源码:Java 数字签名、数字证书生成源码.rar

    在实际应用中,这些源码示例可能包括了生成和验证数字签名以及数字证书的具体代码,对于学习和理解Java安全机制非常有帮助。通过阅读和运行这些源码,开发者可以更好地掌握如何在实际项目中应用这些安全功能。 总之...

    用Java数字签名提供XML安全.doc

    数字签名的基本概念是利用公钥和私钥体系,发送者使用私钥对XML文档进行签名,接收者则使用发送者的公钥验证签名。这种方式能够证明数据源自可信的源头,并且在传输过程中未被篡改。数字签名不仅提供了数据完整性,...

Global site tag (gtag.js) - Google Analytics