基于自签名的
X.509
数字证书生成及验证
数字证书用于标志网络用户身份,在
Web
应用中,数字证书的应用十分广泛,如:安全电子邮件、访问安全站点、安全电子事务处理和安全电子交易等。
数字证书的格式一般采用
X.509
国际标准。目前,数字证书认证中心主要签发安全电子邮件证书、个人和企业身份证书、服务器证书以及代码签名证书等几种类型证书。
数字证书由证书机构签发,证书机构通常需经权威认证机构注册认证。在企业应用中,也常用企业自身作为发证机构(未经过认证)签发数字证书,证书的使用范围也常是企业内部,这样的证书就是所谓的“自签名”的。
数字证书采用公钥密码体制,每个用户拥有一把仅为本人所掌握的私有密钥
(
私钥
)
,用它进行解密和签名
;
同时拥有一把公共密钥
(
公钥
)
并可以对外公开,用于加密和验证签名。
下面介绍我们在项目中常用的自签名证书的生成及验证方法。为简单起见,我们假设所有网站用户使用同一个数字证书
clientCA
。
一、
服务器端
登录远程服务器,在服务器端生成证书并提供下载。服务器上应当安装
jdk1.5
以上,因为我们需要使用
jdk
自带的
keytool
工具,
keytool
工具位于
jdk
安装目录的
bin
目录下。
1、
生成密钥数据库及根证书
如果是第
1
次使用数字证书,那么很可能服务器上还没有密钥数据库。我们可以使用下列命令成密钥数据库(
keystore
)。
keytool -genkey -dname "CN=
发证人姓名
,OU=
发证人所属部门
,O=
发证人所属公司
,L=
昆明市
,ST=
云南省
,C=
中国
" -alias IPCCCA -keyalg RSA -keysize 1024 -keystore IPCCCA
-keypass
根证书密码
-storepass
库密码
运行脚本后,会在当前用户主目录(如:
C:/Documents and Settings/Administrator
目录,
window
系统)下生成密钥数据库文件
IPCCCA
。注意,需要设置两个密码,一个是根证书密码
keypass
,一个是库密码
storepass
,如果你不是很确定二者间的区别,最好两个都设置成一样。
2、
生成自签名证书
运行下列命令,生成一个自签名证书:
keytool -genkey -dname "CN=
发证人姓名
,OU=
发证人所属部门
,O=
发证人所属公司
,L=
昆明市
,ST=
云南省
,C=
中国
" -alias clientCA -keyalg RSA -keysize 1024 -keystore IPCCCA
-keypass 123456 -storepass
库密码
-validity 1
这个证书是一个自签名证书,该证书的别名为
clientCA
,存储在前面生成的那个密钥库文件
(IPCCCA)
中。这需要提供访问该库的库密码
(storepass)
,必须跟第
1
步中的一样。
Kepass
是该证书的公钥,验证证书时需要提供该密钥。
并且为了便于测试,我们把证书有效期设置为
1
天。这样每过一天,用户必须重新下载证书。
3、
查看密钥库
你可以用下列命令查看生成的两个密钥:
keytool -list -keystore IPCCCA –storepass
库密码
结果会列出两个密钥,类似如下:
您的
keystore
包含
2
输入
clientca, 2011-3-30, keyEntry,
认证指纹
(MD5)
:
10:B8:51:54:7B:1C:60:7C:89:E7:B6:8E:71:E5:E1:E7
ipccca, 2011-3-30, keyEntry,
认证指纹
(MD5)
:
C3:E3:7D:7C:9B:AA:05:84:92:AF:93:18:42:D2:1C:07
4、
提供证书下载
我们可以在服务器上放一个
servlet
,以提供自签名证书的下载:
private
static final long serialVersionUID = 1L;
//
有效期天数
private
static final int Max_Days = 1;
//
keystore
密码
private
static final char[] password = "ipcc@95598".toCharArray();
//
keystore
文件路径
private
static final String keystoreFilename = "C://Documents and
Settings//Administrator//IPCCCA";
//
证书文件名
private
static final String certFilename="client.cer";
//
证书别名
private
static final String alias = "clientCA";
private
KeyStore keystore;
private String sigAlgrithm;
//
读取
keystore
private
KeyStore loadKeystore(String keystorepath) {
KeyStore ks = null;
try {
FileInputStream
fIn = new FileInputStream(keystorepath);
ks =
KeyStore.getInstance("JKS");
ks.load(fIn,
password);
fIn.close();
return
ks;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return ks;
}
//
获得
CertInfo
private
X509CertInfo getCertInfo(Certificate c, String alias) {
X509CertInfo certInfo = null;
try {
//
从待签发的证书中提取证书信息
byte[]
encod2 = c.getEncoded();//
获取
证书内容(经过编码的字节)
X509CertImpl
cimp2 = new X509CertImpl(encod2);//
创建
X509CertImpl
象
sigAlgrithm=cimp2.getSigAlgName();
//
获取
X509CertInfo
对象
certInfo
= (X509CertInfo) cimp2.get(X509CertImpl.NAME
+
"." + X509CertImpl.INFO);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return certInfo;
}
//
修改有效期
private
void updateValidity(X509CertInfo cinfo, int days) {
//
获取当前时间
Date d1 = new Date();
//
有效期为当前日期后延
n
天
Date d2 = new Date(d1.getTime() + days * 24 *
60 * 60 * 1000L);
//
创建有效期对象
CertificateValidity cv = new
CertificateValidity(d1, d2);
try {
cinfo.set(X509CertInfo.VALIDITY,
cv);//
设置有效期
} catch (Exception e) {
e.printStackTrace();
}
}
//
存储证书
private
void saveCert(KeyStore ks, char[] storepass, String alias,
PrivateKey
pKey, char[] keypass, X509CertInfo cinfo,String algrithm) {
try {
X509CertImpl
cert = new X509CertImpl(cinfo);//
新建证书
cert.sign(pKey,
algrithm); //
使用
CA
私钥对其签名
//
获取别名对应条目的证书链
Certificate[]
chain = new Certificate[] { cert };
//
向密钥库中添加条目
,
使用已存在别名将覆盖已存在条目
ks.setKeyEntry(alias,
pKey, keypass, chain);
//
将
keystore
存储至文件
FileOutputStream
fOut = new FileOutputStream(keystoreFilename);
keystore.store(fOut,
password);
fOut.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//
导出证书
private
void exportCert(KeyStore ks,String alias,HttpServletResponse response){
try{
Certificate cert =
keystore.getCertificate(alias);
//
得到证书内容(以编码过的格式)
byte[] buf = cert.getEncoded();
//
写证书文件
response.setContentType("application/x-download");
response.addHeader("Content-Disposition",
"attachment;filename="
+ certFilename);
OutputStream out = response.getOutputStream();
out.write(buf);
out.close();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* @see HttpServlet#HttpServlet()
*/
public GetNewCert() {
super();
// TODO Auto-generated
constructor stub
}
/**
* @see
HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected
void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
try{
keystore
= loadKeystore(keystoreFilename); //
读取
keystore
Certificate
c = keystore.getCertificate(alias);//
读取证书
X509CertInfo
cinfo = getCertInfo(c, alias);//
获得证书的
CertInfo
updateValidity(cinfo,
Max_Days);//
修改证书有效期
//
从密钥库中读取
CA
的私钥
PrivateKey
pKey = (PrivateKey) keystore.getKey(alias, "123456"
.toCharArray());
//
将
keystore
存储至
keystore
文件
saveCert(keystore,
password, alias, pKey, "123456".toCharArray(),cinfo,sigAlgrithm);
exportCert(keystore,alias,response);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @see
HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected
void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
doGet(request,response);
}
}
这个
servlet
的作用是:
当用户请求该
serlvet
,从密钥库中提取
clientCA
证书,将证书有效期修改为当前日期到下一天。
也就是说,当用户客户端请求该
servlet
,即可获得一个新的证书,这个证书的有效期已经向后延了一天。
我们的目的是,用户每次登录后,检查用户下载的证书,如证书已过了有效期,则请求此
servlet
即可获得一个有效的新证书。
二、
客户端
客户端可能是任何设备,包括
pc
、移动终端。我们假设客户端是基于
Android1.6
以上的移动终端,则以下是
java
客户端的证书验证类
MyCertificate
:
public class MyCertificate {
private
static String tag="MyCertificate";
public
static Certificate readCert(File file){
Certificate c=null;
try{
CertificateFactory
cf = CertificateFactory.getInstance("X.509");
FileInputStream
in1 = new FileInputStream(file);
c =
cf.generateCertificate(in1);
in1.close();
} catch (Exception e) {
Log.e(tag,
e.toString());
}
return c;
}
//
验证证书的有效性
public
static boolean verifyCert(Certificate c){
PublicKey pbk=c.getPublicKey();
try{
c.verify(pbk);
return
true;
}catch(Exception e){
Log.e(tag,"Certificate
is invalid");
}
return false;
}
//
验证证书有效期
public
static int verifyCertValidity(Date date,Certificate c){
int i=0;
X509Certificate t=(X509Certificate)c;
try {//
有效
t.checkValidity(date);
} catch (CertificateExpiredException e) {//
过期
Log.e(tag,"Certificate
Expired");
i=-1;
} catch (CertificateNotYetValidException e) {//
尚未生效
Log.e(tag,"Certificate
Too early");
i=-2;
}
return i;
}
public
static boolean verify(Context ctx){
Activity act=(Activity)ctx;
boolean b=false;
//
检查证书文件是否存在
File file=new
File(Environment.getExternalStorageDirectory()+act.getString(R.string.CERT_DIR)+act.getString(R.string.CERT_FILE));
if(!file.exists()){
act.showDialog(1);
}else{
Date
d=new Date();//
取当前时间
Certificate
c=MyCertificate.readCert(file);//
读取证书文件
//
校验证书有效性
if(!MyCertificate.verifyCert(c)){
act.showDialog(0);//
无效证书
}else{
//
校验证书有效期
int i=MyCertificate.verifyCertValidity(d,c);
switch(i){
case 0://
有效
b=true;
break;
case -1://
过期
act.showDialog(2);
break;
case -2://
未生效
act.showDialog(3);
break;
}
}
}
return b;
}
}
在相关
activity
中可以这样使用它:
private void login(String acc, String pass)
{
String url =
this.getString(R.string.PORT_LOGIN_URL);
url = String.format(url, acc, pass);
// Log.i(tag,url);
MainLoginHandler handler = new
MainLoginHandler();
modules = SaxHelper.getModules(url, handler);
// Log.i(tag,systems.toString());
Log.i("modules:", "" +
modules);
if (modules != null) {
String
status = (String) modules.get("loginstatus");
if
("true".equals(status)) {//
登录成功
if (!verifyCert()) {
return;
}
Bundle bundle = new Bundle();
bundle.putSerializable("data",
(Serializable)
modules.get("modules"));
gotoActivity(main.class, bundle);
} else {
Toast.makeText(getBaseContext(), "
用户名或密码错误!
",
Toast.LENGTH_SHORT).show();
}
}
}
private boolean
verifyCert() {
return MyCertificate.verify(this);
}
//
创建
activity
托管对话框
protected
Dialog onCreateDialog(int id) {
Log.e("::::",
"showdialog!");
String msg = "";
switch (id) {
case 1:
msg =
"
证书未下载!请点击“是”以下载证书。
";
break;
case 2:
msg =
"
证书已过期!请点击“是”重新下载证书。
";
break;
case 3:
msg =
"
证书尚未生效!请等证书生效后再重新登录。
";
//
对于未生效的证书,无需重新下载,等证书生效即可
return
new AlertDialog.Builder(this)
.setMessage(msg)
.setNegativeButton("
是
",
new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();//
removeDialog(0);
移除对话框
}
}).create();
case 4:
return
new AlertDialog.Builder(this)
.setMessage("
位置源未设置!是否现住设置位置源?
")
.setPositiveButton("
是
",
new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
//
转至
GPS
设置界面
Intent
intent = new Intent(
Settings.ACTION_SECURITY_SETTINGS);
startActivityForResult(intent,
0);
}
})
.setNegativeButton("
不
",
new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();//
removeDialog(0);
移除对话框
}
}).create();
default:
msg =
"
无效的证书!请点击“是”重新下载证书。
";
}
return new
AlertDialog.Builder(this).setMessage(msg)
.setPositiveButton("
是
", new
DialogInterface.OnClickListener() {
public
void onClick(DialogInterface dialog, int which) {
//
开始下载证书
downloadCert();
}
})
.setNegativeButton("
不
", new
DialogInterface.OnClickListener() {
public
void onClick(DialogInterface dialog, int which) {
dialog.dismiss();// removeDialog(0);
移除对话框
}
}).create();
}
红色加粗部分的代码调用了
MyCertificate.verify()
。
onCreateDialog
方法则通过对话框方式返回证书校验的结果。
分享到:
相关推荐
java数字签名(签名生成,用证书验证签名)
本示例将从文件中读取数字证书,获取一个处理X.509证书的证书工厂,生成文件输入流,输入文件为c:/mycert.cer,生成数字签名: //产生RSA密钥对(myKeyPair) KeyPairGenerator myKeyGen= KeyPairGenerator....
java数字签名(签名生成,用证书验证签名)[汇编].pdf
摘要:JAVA源码,系统相关,数字签名,数字证书 Java 数字签名、数字证书的相关实例。... 数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂……
Java 生成数字签名,数字证书的实现代码,得到RSA密钥对,产生Signature对象,对用私钥对信息(info)签名.Signature mySig = Signature.getInstance("SHA1WithRSA"); //用指定算法产生签名对象 mySig.initSign...
java 国密算法实现,包含SM2 SM3 SM4和数字签名、数字证书的验证以及相应的说明文档
java数字签名(签名生成,用证书验证签名)[定义].pdf
可强制签名已过期数字签名,这是成品exe,若容查杀没毒
Java实现RSA加密解密,数字证书生成与验证,模拟两个端通信,AB双方通信,客户端A把需要传输的文件MD5值用自己的私钥生成数字签名,连同明文用服务端B的公钥加密后传送给服务端B,服务端B用私钥解密验证数字签名,并计算...
可靠,放心下载
AB双方通信,A把需要传输的文件MD5值用自己的私钥生成数字签名,连同明文用B的公钥加密后传送给B,B用私钥解密验证数字签名,并计算明文MD5值跟密文的MD5值比较
在一种改进的椭圆曲线数字签名算法的基础上,采用Shamir门限秘密共享方案和联合秘密共享技术设计了一种新的基于身份证书机制的无可信中心的(t,n)门限群签名方案,该方案由以下四个步骤组成:系统初始化阶段(确定...
第一章基础知识.....................................................................................................................10 1.1 对称算法.........................................................
第一章 基础知识.....................................................................................................................10 1.1 对称算法........................................................
1.数字证书显示界面:显示数字证书的详细信息,包括:证书信息、颁发给、颁发者、有效期、版本号、序列号、签名算法、有效期起始日期、有效期终止日期、主题、公钥、签名; 2.数字证书管理界面:新建条目或者数字...
1.数字证书显示界面:显示数字证书的详细信息,包括:证书信息、颁发给、颁发者、有效期、版本号、序列号、签名算法、有效期起始日期、有效期终止日期、主题、公钥、签名; 2.数字证书管理界面:新建条目或者数字...
JAVA中的XML安全与数字签名。 这是一个示例,演示了如何在 Java 中...Java 提供了用于生成和验证数字签名的 API。要使用这些 API,需要先获得一个数字证书。数字证书由可信的第三方颁发,包含持有人的公钥和个人信息。
第一章基础知识.......................................................................................................................8 1.1 对称算法........................................................
可使用此格式,通过ASN1C生成完整的PKCS7签名C语言代码,实现诸如SM2算法数字签名及验证。 注意,CertificateSerialNumber本来在PKCS7标准ASN1结构中定义为INTEGER类型,但由于ASN1C将INTEGER类型翻译成long,不支持...