by fleshwound ()
公司主营业务:成都做网站、成都网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出博白免费做网站回馈大家。
(注:这是我们的完整设计中的一部分,其它有些部分尚要求保密,希望这个拙文能给做J2EE项目的兄弟们带来点帮助,有任何关于JAVA安全和密码学理论和应用的问题可以来我们的论坛:)
近年来,随着互连网和计算机的普及,电子商务和电子政务成为当今社会生活的重要组成部分,以网上订购和网上在线支付的为主要功能的网店系统(Web Shop System)是目前电子商务的热门技术。
JAVA以它“一次编译,处处运行”的神奇魅力和强大的安全技术支持,很快成为WEB信息系统开发的首选语言,而J2EE就是为了WEB应用开发而诞生的。目前J2EE的应用大部份都是多层结构的, 良好的分层可以带来很多好处,例如可以使得代码结构清晰,方便组件复用,可以快速适应应用的新需求。同时,JAVA还提供了强大的安全技术(例如:JCA,HTTPS,JSSA等)。对于电子商务系统而言,系统平台的安全性和效率是其中的核心问题,而这些正好是J2EE及其相关技术的强项。
0 系统中所要使用的API及其特点介绍
该系统中主要使用的技术和特点如下:
(1)EJB :主要是作为J2EE中间层,完成商业逻辑。目前主要有三种类型的EJB: 会话 Bean (Session Bean)、实体Bean (Entity Bean)、消息驱动的Bean(MDB);
(2)JAAS:在J2EE 中用于处理认证和授权服务,进行资源控制;
(3)JSP和Java Servlets:用于J2EE的表示层,生成用户界面;
(4)JDBC:用于数据库(资源层)的连接和与数据库进行交互;
(5)JNDI:Java命名和目录接口,该API实际上是用来访问J2EE的所有资源;
(6)JMS:Java消息传输服务,配合MDB使用。
1 Session的安全问题与解决方案
在项目中,保存Session一般有两种方法,一是分别放在客户端,一是集中放在服务器端。在客户端保存Session是指将Session的状态串行化,然后嵌入到返回给客户的HTML页面中。当Session中的信息很少时,这样实现比较容易,另外这种方法还消除了跨越多个服务器复制状态的问题。
但是在客户端保存Session状态时,必须考虑到由此带来的安全问题,因为黑客可能通过嗅探攻击(Sniffer)获取敏感信息。为了不让敏感信息数据暴露,解决的方法是对数据进行加密或者使用HTTPS,采用SSL技术。
如果是要保存大量Session状态的应用,最好的方法是将Session状态统一放在服务器端。当状态被保存在服务器上时,不会有客户端Session管理的大小和类型限制。此外,还避免了由此带来的安全问题,而且也不会遇到由于在每个请求间传送Session状态带来的性能影响,但是对服务器的性能要求比较高。网店系统的安全性要求较高,因此Session还是集中放在中间层服务器端,同时对客户端到服务器端采用SSL连接。
2客户端的缓存安全设计
大部分顾客使用的WEB浏览器将浏览过的页面缓存在磁盘上,这样我们浏览网页的时候不需要重新向服务器发出HTTP请求,对于普通的网页不存在安全问题。但是对于需要保密的WEB应用,会带来安全隐患和泄漏隐私,因此对于客户端缓存,也必须做适当的处理。最好的方法就是禁止使用缓存,但是对于大部分顾客而言,要求在客户端不用缓存是不现实的,因此我们必须在中间层解决该问题,方法是采用Servlet过滤器技术。该技术是Servlet2.3以后才出现的,在J2EE中的应用很广泛。要使用该技术,需要执行以下步骤:
(1) 编写一个Servlet过滤器,实现javax.servlet.Filter接口;
(2) 修改Web.xml文件,使容器知道过滤器在什么时候被调用。
Javax.servlet.Filter主要有3个方法:
(1)init(FilterConfig cfg) :当开始使用 servlet 过滤器服务时,容器调用此方法一次。传送给此方法的 FilterConfig 参数包含 servlet 过滤器的初始化参数;
(2)destroy() :当不再使用 servlet 过滤器服务时,容器调用此方法;
(3)doFilter(ServletRequest req, ServletResponse res, FilterChain chain): 容器为每个映射至此过滤器的 servlet 请求调用此方法,然后才调用该 servlet 本身。传送至此方法的 FilterChain 参数可用来调用过滤器链中的下一个过滤器。当链中的最后一个过滤器调用 chain.doFilter() 方法时,将运行最初请求的 servlet。因此,所有过滤器都应该调用 chain.doFilter() 方法。如果过滤器代码中的附加认证检查导致故障,则不需要将原始 servlet 实例化。在这种情况下,不需要调用 chain.doFilter() 方法,相反,可将其重定向至其它一些错误页面。
如果 servlet 映射至许多 servlet 过滤器,则按照应用程序的部署描述符(web.xml)中的先后出现的次序来调用 servlet 过滤器。这一部分的主要代码如下:
//要引入的类库
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.*;
//设置servlet过滤代码段
public class CacheFilter implements Filter {
protected FilterConfig filterConfig;
private String cachetp;
//初始化
public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig = filterConfig;
cachetp=config.getInitParameter("CacheControlType");
if (cachetp==null)
{
throw new ServletException("没有定义Cache控制类型");
}
}
//
public void destroy()
{
this.filterConfig = null;
}
//执行过滤器部分
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
throws IOException, ServletException {
if (response instanceof HttpServletResponse )
{
HttpServletResponse resp=(HttpServletResponse) response;
resp.addHeader("Cache-Control",cachetp);
}
else
{
throw new ServletException("非法相应!");
}
chain.doFilter(request, response);
}
}
以下是在Web.xml中添加的对应的内容
CacheFilter
CacheFilter
Cache filter
CacheControlType
no-store
CacheFilter
/cachecontrol
3视图访问的安全设置
所有用户都必须登陆,只有登陆才可以看到用户的角色和权限相对应的视图。因此一个重要的问题就是如何防止一个视图或者部分的视图被一个未被授权的客户直接访问。
在一些情况下,资源被限制为完全不允许某些用户访问,例如:管理后台就不应该让普通顾客会员访问。有几个方法可以做到这一点。一个方法是加入应用逻辑到处理控制器或者视图的程序中,禁止某些用户访问。另一个方案是设置运行时的系统,对于一些资源,仅允许经由另一个应用资源内部调用。在这种情形,对于这些资源的访问必须被通过另一个表现层的应用资源进行,例如一个servlet控制器。对于这些受限制的资源不允许通过浏览器直接调用。
在J2EE中,可以利用Web容器中内置的安全技术来进行角色访问资源的控制。根据最新版本的servlet和EJB规范,安全限制在web.xml的配置描述文件中描述,我们可以通过配置web.xml来控制角色访问,修改配置描述文件web.xml就可以达到快速修改安全策略的目的。
安全限制允许使用编程的方法根据用户的角色来控制访问。资源可以被某些角色的用户访问,同时禁止其它的角色访问。另外,某个视图的一部分也可以根据用户的角色来限制其访问。如果某些资源完全不允许来自于浏览器的直接访问,那么这些资源可以配置只允许一些特殊的安全角色访问,而这些安全角色不分配给任何一个用户。这样只要不分配这个安全角色,那么以这种方式配置的资源将禁止所有的浏览器直接访问。下面一个例子就是web.xml配置文件的一部分,它定义了一个安全的角色以限制直接的浏览器访问。角色的名字是“vip”,受限制资源的名字是specialgood1.jsp、specialgood2.jsp、specialgood3.jsp和bookinfo.jsp。除非一个用户或者组被分配到“vip”角色,否则这些客户都不可以直接访问这些JSP页面。不过,由于内部的请求并不受这些安全的限制,一个初始时由某servlet控制器处理的请求将会导向到这些受限制的页面,这样它们就可以间接访问这些JSP页面。
<security-constraint>
<web-resource-collection>
<web-resource-name>specialgood </web-resource-name>
<description>special good infomation</description>
<url-pattern>/shop/jsp/a1/specialgood1.jsp</url-pattern>
<url-pattern>/shop/jsp/a1/specialgood2.jsp</url-pattern>
<url-pattern>/shop/jsp/a1/specialgood3.jsp</url-pattern>
<url-pattern>/shop/jsp/a1/bookinfo.jsp</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>vip</role-name>
</auth-constraint>
</security-constraint>
3 各层次间的耦合问题与解决策略
表现层的数据结构,例如HttpServletRequest,应该被限制在表现层上。如果将这些细节放到其它层(主要是业务逻辑层)中,将大大降低了代码的的重用性,令代码变得复杂,并且增加了层间的耦合。解决方法一个常用方法是不让表现层的数据结构和商业层共享,而是拷贝相关的状态到一个更常见的数据结构中再共享。你也可以选择由表现层数据结构中将相关的状态分离出来,作为独立的参数共享。另外在域对象暴露表现层的数据结构,如果将诸如HttpServletRequest的请求处理数据结构和域对象共享,这样做也会增加了应用中两个不同方面的耦合。域对象应该是可重用的组件,如果它们的实现依赖协议或者层相关的细节,它们可重用性就很差,同时维护和调试高耦合的应用更加困难。成熟的解决方案是不通过传送一个HttpServletRequest对象作为一个参数,而是拷贝request对象的状态到一个更为常用的数据结构中,并且将这个对象共享给域对象。你也可以选择由HttpServletRequest对象中将相关的状态分离出来,并且将每一个的状态作为一个独立的参数提供给域对象。
4 EJB的安全设计与控制
EJB的执行过程一般是这样的:(1)客户端通过JNDI检索Home对象的引用;(2)JNDI返回Home对象的引用;(3)请求创建一个新的EJB对象;(4)创建EJB对象;(5)返回EJB对象;(6)调用商务方法;(7)调用Enterprise Bean.引起EJB的安全问题原因主要存在三个方面:
(1)用包嗅探器(Packet Sniffer)获取用户凭证信息并直接调用会话Bean;(2)对实体Bean进行未授权访问;(3)对消息驱动的Bean的无效访问(发布恶意或者虚假的消息).
以上安全问题可导致客户端或者服务端欺骗攻击和DDOS攻击。解决问题(1)的方法是使用JAVA中SSL技术来保护通讯,解决(2)的方法是对于实体Bean全部采用本地接口或者采用JAAS(文献[1]),对于(1)和(2),我们可以同时采取以下措施:让容器完成认证并传输用户凭证信息,另外使用声明性或者程序设计的安全验证角色。对于问题(3),J2EE并没有提供一个很好的方案,我们的解决方案是采用数字签名技术来保证信息来自可信任的源。该方法的结合代码简要说明如下,消息采用JMS传递:
//客户端,要用到消息发送者的私钥进行签名
...
message.setString("userid",userid);
message.setString("useritem",useritem);
message.setInt("usersn",serialnum);//包含一个序列号
message.setString("usercertid",certid);
String signature=getSignature(userid+":"+useritem+":"+serialnum+":"+certid);
//进行签名,其中getSignature为签名函数,要用到消息发送者的私钥进行签名,具体密码学技术可参考文献[2];
message.setString("signature",signature);
sendmessage(message);//发送信息
...
//服务器端
String checkstr=userid+":"+message.getString("useritem")+":"+
message.getInt("usersn")+":"+usercertid;
boolean b_check=checkSignature(checkstr,msg.getString("signature"),
usercertid,userid);
//进行验证,其中checkSignature为验证函数,要用到消息发送者的公钥进行验证,具体密码学技术可参考文献[2];
5 CA中心与证书的生成
前面我们已经提出在客户端要使用HTTPS和SSL,因此要建立一个自己的CA中心来管理分发证书,加强客户端到中间层服务器端通讯的安全性.建立CA中心的第一步是利用JAVA工具包中的Keytool生成一个X509证书,然后将该证书交由权威CA中心Vertsign签名,再将该证书设置为根证书,建立自己的CA.每次有新用户注册交易的时候,都必须签发一个用户独一无二的证书,关键的过程是如何签发证书.签发证书的过程如下:
(1)从中间层CA服务器的密钥库中读取CA的证书:
FileInputStream in=new FileInputStream(ShopCAstorename);
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass);
java.security.cert.Certificate c1=ks.getCertificate(alias);
(2)获得CA的私钥:
PrivateKey caprk=(PrivateKey)ks.getKey(alias,cakeypass);
(3)从CA的证书中提取签发者信息:
byte[] encod1=c1.getEncoded();
X509CertImpl shopcimp1=new X509CertImpl(encod1);
X509CertInfo shopcinfo1=(X509CertInfo)shopcimp1.get(X509CertImpl.NAME+
"."+X509CertImpl.INFO);
X500Name issuer=(X500Name)shopcinfo1.get(X509CertInfo.SUBJECT+
"."+CertificateIssuerName.DN_NAME);
(4)获取待签发的证书相关信息,与(3)类似;
(5)设置新证书的有效期、序列号、签发者和签名算法:
//设置新证书有效期为1年
Date begindate =new Date();
Date enddate =new Date(begindate.getTime()+3000*24*360*60*1000L); CertificateValidity cv=new CertificateValidity(begindate,enddate);
cinfo2.set(X509CertInfo.VALIDITY,cv);
//设置新证书序列号
int sn=(int)(begindate.getTime()/1000);
CertificateSerialNumber csn=new CertificateSerialNumber(sn);
cinfo2.set(X509CertInfo.SERIAL_NUMBER,csn);
//设置新证书签发者
cinfo2.set(X509CertInfo.ISSUER+"."+
CertificateIssuerName.DN_NAME,issuer);
//设置新证书算法
AlgorithmId algorithm =
new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
cinfo2.set(CertificateAlgorithmId.NAME+
"."+CertificateAlgorithmId.ALGORITHM, algorithm);
(6)创建证书并签发:
// 创建证书
X509CertImpl newcert=new X509CertImpl(cinfo2);
// 签名
newcert.sign(caprk,"MD5WithRSA");
(7)将新证书提供给注册用户,并提示安装,一般的做法是在用户注册成功后系统立即返回一个证书对象给中间层某个Servlet,由其返回给用户。
参考文献
[1]沈耀,陈昊鹏,李新颜.EJB容器中基于JAAS 的安全机制的实现.[J]:计算机应用与软件 2004.9 16~18
[2](美)Jess Garms著,庞南等译. Java安全性编程指南[M].北京:电子工业出版社 2002
[3]
[4] 蔡剑,景楠. Java 网络程序设计:J2EE(含1.4最新功能)[M].北京: 清华大学出版社 2003
[5](美)John Bell Tony Loton. Java Servlets 2.3编程指南[M].北京: 电子工业出版社 2002
[6](美)Joseph J.Bambara等著,刘堃等译. J2EE技术内幕[M].北京:机械工业出版社 2002
[7](美)Li Gong著.JAVA 2平台安全技术——结构、API设计和实现[M].北京: 机械工业出版社 2000
[8](英)Danny Ayers等著,曾国平等译. Java服务器高级编程[M].北京:机械工业出版社 2005
[9]
[10]
1.权限的设置:审核人员看不到操作模块;操作人员看不到审核模块
2.记录表中应该有一个状态的字段:操作人员录入信息后该条记录应该为待审核(或者是提交审核)状态,(如果输入了二级密码,状态直接为审核通过);审核模块中直接查询状态为待审核(或提交审核)的记录,同意后更新该记录的状态为审核通过;
3.你至少需要以下几个action:
①操作员提交数据的action(包括修改,可以公用这个action)
②审核员查询数据的action
③审核员更新记录状态的action
(这里没考虑权限的问题,如果考虑权限问题,你的工作量还有很多很多)
一:需要包含的包
import java.security.*;
import java.io.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import sun.security.x509.*
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
二:从文件中读取证书
用keytool将.keystore中的证书写入文件中,然后从该文件中读取证书信息
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in=new FileInputStream("out.csr");
Certificate c=cf.generateCertificate(in);
String s=c.toString();
三:从密钥库中直接读取证书
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
java.security.cert.Certificate c=ks.getCertificate(alias);//alias为条目的别名
四:JAVA程序中显示证书指定信息
System.out.println("输出证书信息:\n"+c.toString());
System.out.println("版本号:"+t.getVersion());
System.out.println("序列号:"+t.getSerialNumber().toString(16));
System.out.println("主体名:"+t.getSubjectDN());
System.out.println("签发者:"+t.getIssuerDN());
System.out.println("有效期:"+t.getNotBefore());
System.out.println("签名算法:"+t.getSigAlgName());
byte [] sig=t.getSignature();//签名值
PublicKey pk=t.getPublicKey();
byte [] pkenc=pk.getEncoded();
System.out.println("公钥");
for(int i=0;ipkenc.length;i++)System.out.print(pkenc[i]+",");
五:JAVA程序列出密钥库所有条目
String pass="123456";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,pass.toCharArray());
Enumeration e=ks.aliases();
while(e.hasMoreElements())
java.security.cert.Certificate c=ks.getCertificate((String)e.nextElement());
六:JAVA程序修改密钥库口令
String oldpass="123456";
String newpass="654321";
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,oldpass.toCharArray());
in.close();
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,newpass.toCharArray());
output.close();
七:JAVA程序修改密钥库条目的口令及添加条目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
Certificate [] cchain=ks.getCertificate(alias);获取别名对应条目的证书链
PrivateKey pk=(PrivateKey)ks.getKey(alias,oldkeypass.toCharArray());获取别名对应条目的私钥
ks.setKeyEntry(alias,pk,newkeypass.toCharArray(),cchain);向密钥库中添加条目
第一个参数指定所添加条目的别名,假如使用已存在别名将覆盖已存在条目,使用新别名将增加一个新条目,第二个参数为条目的私钥,第三个为设置的新口令,第四个为该私钥的公钥的证书链
FileOutputStream output=new FileOutputStream("another");
ks.store(output,storepass.toCharArray())将keystore对象内容写入新文件
八:JAVA程序检验别名和删除条目
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
ks.containsAlias("sage");检验条目是否在密钥库中,存在返回true
ks.deleteEntry("sage");删除别名对应的条目
FileOutputStream output=new FileOutputStream(".keystore");
ks.store(output,storepass.toCharArray())将keystore对象内容写入文件,条目删除成功
九:JAVA程序签发数字证书
(1)从密钥库中读取CA的证书
FileInputStream in=new FileInputStream(".keystore");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in,storepass.toCharArray());
java.security.cert.Certificate c1=ks.getCertificate("caroot");
(2)从密钥库中读取CA的私钥
PrivateKey caprk=(PrivateKey)ks.getKey(alias,cakeypass.toCharArray());
(3)从CA的证书中提取签发者的信息
byte[] encod1=c1.getEncoded(); 提取CA证书的编码
X509CertImpl cimp1=new X509CertImpl(encod1); 用该编码创建X509CertImpl类型对象
X509CertInfo cinfo1=(X509CertInfo)cimp1.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 获取X509CertInfo对象
X500Name issuer=(X500Name)cinfo1.get(X509CertInfo.SUBJECT+"."+CertificateIssuerName.DN_NAME); 获取X509Name类型的签发者信息
(4)获取待签发的证书
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("user.csr");
java.security.cert.Certificate c2=cf.generateCertificate(in);
(5)从待签发的证书中提取证书信息
byte [] encod2=c2.getEncoded();
X509CertImpl cimp2=new X509CertImpl(encod2); 用该编码创建X509CertImpl类型对象
X509CertInfo cinfo2=(X509CertInfo)cimp2.get(X509CertImpl.NAME+"."+X509CertImpl.INFO); 获取X509CertInfo对象
(6)设置新证书有效期
Date begindate=new Date(); 获取当前时间
Date enddate=new Date(begindate.getTime()+3000*24*60*60*1000L); 有效期为3000天
CertificateValidity cv=new CertificateValidity(begindate,enddate); 创建对象
cinfo2.set(X509CertInfo.VALIDITY,cv); 设置有效期
(7)设置新证书序列号
int sn=(int)(begindate.getTime()/1000); 以当前时间为序列号
CertificateSerialNumber csn=new CertificateSerialNumber(sn);
cinfo2.set(X509CertInfo.SERIAL_NUMBER,csn);
(8)设置新证书签发者
cinfo2.set(X509CertInfo.ISSUER+"."+CertificateIssuerName.DN_NAME,issuer);应用第三步的结果
(9)设置新证书签名算法信息
AlgorithmId algorithm=new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
cinfo2.set(CertificateAlgorithmId.NAME+"."+CertificateAlgorithmId.ALGORITHM,algorithm);
(10)创建证书并使用CA的私钥对其签名
X509CertImpl newcert=new X509CertImpl(cinfo2);
newcert.sign(caprk,"MD5WithRSA"); 使用CA私钥对其签名
(11)将新证书写入密钥库
ks.setCertificateEntry("lf_signed",newcert);
FileOutputStream out=new FileOutputStream("newstore");
ks.store(out,"newpass".toCharArray()); 这里是写入了新的密钥库,也可以使用第七条来增加条目
十:数字证书的检验
(1)验证证书的有效期
(a)获取X509Certificate类型对象
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in1=new FileInputStream("aa.crt");
java.security.cert.Certificate c1=cf.generateCertificate(in1);
X509Certificate t=(X509Certificate)c1;
in2.close();
(b)获取日期
Date TimeNow=new Date();
(c)检验有效性
try{
t.checkValidity(TimeNow);
System.out.println("OK");
}catch(CertificateExpiredException e){ //过期
System.out.println("Expired");
System.out.println(e.getMessage());
}catch((CertificateNotYetValidException e){ //尚未生效
System.out.println("Too early");
System.out.println(e.getMessage());}
(2)验证证书签名的有效性
(a)获取CA证书
CertificateFactory cf=CertificateFactory.getInstance("X.509");
FileInputStream in2=new FileInputStream("caroot.crt");
java.security.cert.Certificate cac=cf.generateCertificate(in2);
in2.close();
(c)获取CA的公钥
PublicKey pbk=cac.getPublicKey();
(b)获取待检验的证书(上步已经获取了,就是C1)
(c)检验证书
boolean pass=false;
try{
c1.verify(pbk);
pass=true;
}catch(Exception e){
pass=false;
System.out.println(e);
}
通过JAVA来读取数字证书的方法获取20位指纹:
CARead.java文件代码:
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);
}
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();//得到开始有效日期
CA_ItemData[3][0] = "开始有效日期";
CA_ItemData[3][1] = Field;
Field=x509certificate. getNotAfter().toString();//得到截止日期
CA_ItemData[4][0] = "截止日期";
CA_ItemData[4][1] = Field;
Field=x509certificate.getSerialNumber().toString(16);//得到序列号
CA_ItemData[5][0] = "序列号";
CA_ItemData[5][1] = Field;
Field=x509certificate.getIssuerDN().getName();//得到发行者名
CA_ItemData[6][0] = "发行者名";
CA_ItemData[6][1] = Field;
Field=x509certificate.getSigAlgName();//得到签名算法
CA_ItemData[7][0] = "签名算法";
CA_ItemData[7][1] = Field;
Field=x509certificate.getPublicKey().getAlgorithm();//得到公钥算法
CA_ItemData[8][0] = "公钥算法";
CA_ItemData[8][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;
}
//读取二进制指纹文件
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();
X509Certificate cert=null;
//遍历得到所有的证书属性
if (bais.available() 0)
{
cert = (X509Certificate) certificatefactory .generateCertificate(bais);
Cert_EditorPane.setText(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();
StringBuffer strBuffer =new StringBuffer();
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++) {
strBuffer.append(buf[i]);
}
in.close();
Cert_EditorPane.setText(strBuffer.toString());
Cert_EditorPane.disable();
JScrollPane edit_scroll = new JScrollPane(Cert_EditorPane);
panel.add(edit_scroll);
} catch (Exception exception) {
exception.printStackTrace();
return -1;
}
return 0;
}
}
openssl genrsa -des3 -out server.key 1024运行时会提示输入密码,此密码用于加密key文件(参数des3便是指加密算法,当然也可以选用其他你认为安全的算法.),以后每当需读取此文件(通过openssl提供的命令或API)都需输入口令.如果觉得不方便,也可以..
PKI 目前使用最多的非对称算法是 RSA。对于基于 RSA 的 PKI 而言,CA 应有一对 RSA 的公私钥对,私钥是 CA 的生命,严格保密,而公钥则发布给使用方。CA 签发一张证书的话,主要是使用 CA 的 RSA 私钥对证书进行签名,并将签名结果保存在证书当中。使用者通过 CA 发布的公钥来验证证书中的签名值,就可以确定该证书是否是由该 CA 签发的。自己要做的就是从证书中提取签名数据和用于签名的原始数据,再使用 CA 的公钥验证这个签名就可以了。