证书(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 证书内容和意义

证书内容 意义
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哈希算法摘要。接下来,系统会提示进行一些输入:

输入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 密码相同,按回车):

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

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>

这样,就得到了一个自签的证书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就可以了。下面是读取证书内容的部分代码:

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() 这里省略...   */
}

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

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函数代码如下:

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;
}

最后用这个小程序看一看刚才生成的证书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所示,所有信息和Base64的显示内容,这里不再列举。

现在已经读取了证书的一些内容,那么怎样使用证书呢?我们可以假设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<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是证书名。验证签名代码如下:

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

X509Certificate相关推荐

  1. WCF分布式安全开发实践(6):传输安全模式之自定义X509Certificate证书验证

    今天继续WCF分布式安全开发实践(6):传输安全模式之自定义X509Certificate证书验证.本文介绍的内容主要是:主要是传输安全模式的UserNamePassword身份验证方式,基于WSHt ...

  2. ca根证书校验 java_JAVA-Android-根据CA证书验证X509Certificate(颁发者证书)

    最后能够使用以下过程验证证书.我希望这对别人有帮助-- public void validateCertificate() throws Exception { try { String issuer ...

  3. php x509certificate,ssl - .NET中的X509Certificate2和X509Certificate有什么区别?

    对于那些想要阅读证书并使用它来进行身份验证的人,只需创建一个X509Certificate2并在其构造函数中传递X509Certificate. 对于已签名的程序集(exe),代码将是这样的代码,为简 ...

  4. 在okhttp3,WebView中忽略HTTPS证书校验

    在APP开发过程中,后台使用的可能是自签的Https证书,如果不忽略证书校验,会出现Trust anchor for certification path not found的错误 Okhttp3忽略 ...

  5. java sslsocket程序_JAVA与C++进行sslsocket通信,JAVA做服务端或客户端

    前几天有位网友问我关于Unity3D里面使用Protobuf的方法,一时有事拖到现在才写这篇文章,不好意思哈. 本文测试环境: 系统:WINDOWS 7(第3.6步).OS X 10.9(第4步) 软 ...

  6. mui 微信支付 与springMVC服务器交互

    昨天搞定了微信支付,没有想象中的难,主要是官方的demo不全好多东西要自己琢磨,mui端的就不写了支付宝的有了一模一样.上java端的首先是jar包 一个是用来解析xml文件 一个是用来解析json ...

  7. 关于 OpenIdConnect 认证启用 HTTPS 回调 RedirectUri 不生效问题

    在搭建 IdentityServer 服务端后,我们尝试使用了 OIDC(OpenID Connect) 的中间件来代替了原先的 Session 系统认证方式,起初采用的是 HTTP 协议,一切都没有 ...

  8. 微信授权获取用户的openid和支付宝授权获取用户的userid

    为什么80%的码农都做不了架构师?>>>    当一请求一个链接或者是扫描二维码时,会请求后台方法,当然对于微信和支付宝来说,大多数时候是扫 码 一.首先说微信: 1.首先会判断请求 ...

  9. 读取Cert格式证书的密钥

    不想存储Cert证书内容,只想存储证书密钥,可通过以下2種方式实现 一.通過java读取证书的密钥出来: 1 package com.zat.ucop.service.util; 2 3 import ...

最新文章

  1. 报错——StackOverflowError
  2. 利用Python绘制萌萌哒的皮卡丘
  3. UTF-8和BOM的一些说明
  4. python使用ctypes模块下的windll.LoadLibrary报OSError: [WinError 193] % 不是有效的 Win32 应用程序...
  5. js学习总结----柯里化函数
  6. 企业使用开源软件的风险
  7. Linux基本目录结构
  8. Kafka与RocketMQ的对比分析
  9. node-sass安装报错node-sass@4.12.0 postinstall: `node scripts/build.js`
  10. php和python-php跟python
  11. SQL SERVER 2008数据库管理与维护总结
  12. 黑客入门教程(非常详细)从零基础入门到精通,看完这一篇就够了。
  13. 国内首款性能最稳定ISO 14443B身份证读卡器芯片FSV9523国产替代MFRC523 国产NFC芯片 不缺货 性价比高 可提供软硬件DEMO
  14. json.stringify php,JSON.stringify()用法介绍
  15. 继“世界性别平等大会”圆满召开后,喀拉拉邦首席部长为“性别平等园区”揭幕
  16. UE 简单存档读档功能
  17. Python NLPIR(中科院汉语分词系统)的使用 十五分钟快速入门与完全掌握
  18. Redis用来干嘛的?
  19. PX4模块设计之三十五:MulticopterAttitudeControl模块
  20. windows安装python环境、conda常用命令

热门文章

  1. 用python将多张图片拼接成一张
  2. 可扩展java游戏框架实践之java飞机大战
  3. c++ private、protect、public、virtual详细说明***
  4. 《Federated_Machine_Learning:Concept_and_Applications》精读
  5. 盘点:史上最全数据挖掘方法!我火速收藏!
  6. inputStream 和FileInputStream 转换
  7. windows环境下netcat的安装及使用
  8. 雨林木风 Ghost XP SP2 精简版 Y2.0
  9. [pyecharts学习笔记]——页面组件 Page(顺序多图,将多个图将汇总到一个页面)
  10. Openssl移植到ARM开发板