基于自签名的X.509数字证书生成及验证

 

基于自签名的 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 方法则通过对话框方式返回证书校验的结果。

 

X.509证书认证技术实验可从多个方面开展,以下提供一个基础的实验思路。 ### 实验目的 验证X.509证书在网络通信中对实体身份验证和数据传输安全保护的作用。 ### 实验环境 - 操作系统:可选择常见的Linux发行版,如Ubuntu。 - 工具:OpenSSL,它是一个强大的开源工具包,可用于生成X.509证书、管理密钥等操作。 ### 实验步骤 1. **生成根证书(Root CA证书)** 根证书由根证书颁发机构(Root CA)签发,在证书信任体系中处于顶级地位。由于Root CA通常是固定的几家信誉良好的供应商,在实验中,可以模拟生成一个根证书。 ```bash # 生成私钥 openssl genrsa -out rootCA.key 2048 # 生成根证书 openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt ``` 这里生成了一个有效期为3650天的根证书。 2. **生成服务器证书请求** 服务器需要向CA申请证书,首先要生成证书签名请求(CSR)。 ```bash # 生成服务器私钥 openssl genrsa -out server.key 2048 # 生成服务器证书签名请求 openssl req -new -key server.key -out server.csr ``` 3. **使用根证书签署服务器证书** 根证书对服务器的CSR进行签名,生成服务器证书。 ```bash openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 365 -sha256 ``` 4. **配置服务器使用证书** 以Nginx服务器为例,配置其使用生成服务器证书。 ```nginx server { listen 443 ssl; server_name example.com; ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/server.key; location / { root /var/www/html; index index.html; } } ``` 5. **客户端验证服务器证书** 客户端在与服务器建立连接时,会验证服务器证书的有效性。可以使用浏览器或命令行工具(如`curl`)进行验证。 ```bash curl https://example.com --cacert rootCA.crt ``` ### 实验分析 - **身份验证**:在实验中,客户端通过验证服务器证书中的CA签名和有效期等信息,确认服务器的身份真实性。 - **数据安全**:基于X.509证书的TLS/SSL协议,确保客户端与服务器之间的数据传输是加密的,防止数据被窃取或篡改。 X.509证书是公钥基础设施(PKI)的标准格式,是目前世界上使用最广泛的数字证书类型,包含公钥、证书认证机构(CA)信息、有效期、序列号等元数据,广泛应用于HTTPS、S/MIME、SSL/TLS等安全协议中,以确保双方的身份真实性和数据传输安全[^1][^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值