zoukankan      html  css  js  c++  java
  • PKCS12 证书的生成及验证

    本文首先感谢 Apple 开发者论坛的eskimo1,他是我见过的最热心肠的人,对任何人他都不吝于给予无私帮助。

    服务器生成PKCS12证书库,并通过servlet导出为DER客户端证书(含一个密钥和一个证书)。iPhone从服务器下载证书后,如何进行验证?

    一、 生成证书

    假设密钥库为dlt.p12,库密码ipcc@95598,有效期1天,则命令为:

    keytool -genkey -v -alias root -keyalg RSA -storetype PKCS12 -keystore dlt.p12 -dname "CN=www.handtimes.com,OU=ipcc,O=云电同方,L=昆明,ST=云南,C=中国" -storepass ipcc@95598 -keypass ipcc@95598

    生成客户端用的证书:

    keytool -genkey -v -alias p12client -keyalg RSA -storetype PKCS12 -keystore dlt.p12 -dname "CN=www.handtimes.com,OU=ipcc,O=云电同方,L=昆明,ST=云南,C=中国" -storepass ipcc@95598 -keypass 123456 -validity 1

     

    要查看已生成的证书,用下面的命令:

    keytool -list -v -alias p12client -keystore dlt.p12 -storepass ipcc@95598 -storetype PKCS12

    或者:

    keytool -list -v -keystore IPCCCA dlt.p12 ipcc@95598 -storetype PKCS12

     

    如果需要将p12证书导出为.cer格式,可以使用命令:

    keytool -export -alias p12client -keystore dlt.p12 -storetype PKCS12 -storepass ipcc@95598 -rfc -file p12.cer

    查看.cer文件:

    keytool -printcert -v -file /Users/kmyhy/Desktop/client.cer

    如果需要将keystore中的私钥导出为.p12格式:

    Keytool.exe -importkeystore -srckeystore IPCCCA -srcstoretype jks -srcstorepass ipcc@95598 -srcalias p12client  -destkeystore dltclient.p12 -deststoretype pkcs12 -deststorepass ipcc@95598 -destkeypass 123456 validity 3

    注意,keytool用的是jdk1.6提供的版本。此时命令提示忽略用户输入的destkeypass密码:

    重新输入密码123456,回车,将在用户主目录下生成dltclient.p12文件。

    查看dltclient.p12内容:

    keytool.exe -list -keystore dltclient.p12 -storepass ipcc@95598 -storetype pkcs12

    可以看到如下输出:

    可以看到其中包含了证书和私钥,并且其认证指纹是和IPCCCA中的一模一样的。

    一、 提供证书下载

    先把生成的p12证书库dlt.p12放到服务器目录下。

    新建一个Servlet:GetP12Cert。

    public class GetP12Cert extends HttpServlet {

    private static final long serialVersionUID = 1L;

        private static final int max_days =1;  

        /**

         * @see HttpServlet#HttpServlet()

         */

        public GetP12Cert() {

            super ();

            // TODO Auto-generated constructor stub

        }

     

    /**

      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

      */

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    String filename= "C://Documents and Settings//Administrator//dlt.cer" ;

    String pass= "ipcc@95598" ;

    Pkcs12Manager man= null ;

    String alias= "p12client" ;

    String keypass= "123456" ;

    try {

    man= new Pkcs12Manager( new File(filename),pass);

    man.updateExpiration(alias, keypass, max_days );

    exportCert(man,alias,response);

    // man.saveCert(alias, "123456");

    //man.saveCert(alias, "123456");

    } catch (Exception e){

    e.printStackTrace();

    }

    }

     

    // 导出证书

    private void exportCert(Pkcs12Manager man,String alias,HttpServletResponse response){

    OutputStream out= null ;

    try {

        Certificate cert = man. keyStore .getCertificate(alias);

        // 得到证书内容(以编码过的格式)

        byte [] buf = cert.getEncoded();

    // 写证书文件

        response.setContentType( "application/x-download" );  

        response.addHeader( "Content-Disposition" , "attachment;filename="  

                + man. file .getName());  

        out= response.getOutputStream();

        out.write(buf);    

    }

    catch (Exception e){e.printStackTrace();}

    finally {

    try {

    out.close();

    } catch (Exception e){

    }

    }

    }

     

    /**

      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

      */

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    doGet(request,response);

    }

     

    }

    其中,Pkcs12Manager是一个javabean,提供了对PKCS12证书的读取、修改操作,代码如下:

    public class Pkcs12Manager {

        public File file ;

        public KeyStore keyStore ;

        private char [] storePass ;

        public Pkcs12Manager(File file, String pass) throws IOException, Exception {

            this . file = file;

            this . storePass =pass.toCharArray();

            getKeyStore(); // 加载 KeyStore 文件

        }

        // 加载 KeyStore 文件

        public synchronized KeyStore getKeyStore() throws IOException, Exception {

            if ( keyStore == null ) {

                FileInputStream fin = new FileInputStream( file );

                KeyStore store = KeyStore.getInstance ( "PKCS12" );

                try {

                    store.load(fin, storePass );               

                }

                finally {

                    try {

                         fin.close();

                    } catch (IOException e) { }

                }

                keyStore = store;

            }

            return keyStore ;

        }

        // 读取 alias 指定的证书内容

    public X509CertInfo getX509CertInfo(String alias) throws Exception {

    X509CertImpl cimp=getX509CertImpl(alias);

    // 获取 X509CertInfo 对象

    return (X509CertInfo) cimp.get(X509CertImpl. NAME

    + "." + X509CertImpl. INFO );

    }

        // 根据 alias 获取 X509CertImpl 对象

    private X509CertImpl getX509CertImpl(String alias) throws Exception{

    Certificate c = keyStore .getCertificate(alias); // 读取证书

    // 从待签发的证书中提取证书信息  

    byte [] enc = c.getEncoded(); // 获取 证书内容(经过编码的字节)

    X509CertImpl cimp= new X509CertImpl(enc); // 创建 X509CertImpl

    return cimp;

    }

    // 修改证书过期时间 : 过期时间顺延 n

    public void updateExpiration(String alias,String keypass, int n) throws Exception{

    System. out .println(getExpiration(alias));

    X509CertInfo cinfo=getX509CertInfo(alias); // 获取 X509CertInfo 对象

    X509CertImpl cimp=getX509CertImpl(alias); // 获取 X509CertImpl 对象

    String sigAlgrithm=cimp.getSigAlgName(); // 获取签名算法

    CertificateValidity cv=(CertificateValidity)cinfo.get(X509CertInfo. VALIDITY );

    // 有效期为当前日期后延 n

    Date d2 = new Date( new Date().getTime() + n * 24 * 60 * 60 * 1000L);

    System. out .println( "new date:" +d2.toString());

    // 创建有效期对象

    cv.set(CertificateValidity. NOT_AFTER , d2);

    cinfo.set(X509CertInfo. VALIDITY , cv); // 设置有效期

    saveCert(alias,keypass,cinfo,sigAlgrithm);

    System. out .println(getExpiration(alias));

    }

    //  读取证书过期时间

    public String getExpiration(String alias) throws Exception{

    X509CertInfo cinfo=getX509CertInfo(alias);

    CertificateValidity cv=(CertificateValidity)cinfo.get(X509CertInfo. VALIDITY );

    // 创建有效期对象

    Date d=(Date)cv.get(CertificateValidity. NOT_AFTER );

    return d.toString();

    }

    //  存储证书

    private void saveCert(String alias,String keypass,

    X509CertInfo cinfo,String algrithm) throws Exception{

    // 从密钥库中读取 CA 的私钥

    PrivateKey pKey = (PrivateKey) keyStore .getKey(alias, keypass.toCharArray());

    X509CertImpl cert = new X509CertImpl(cinfo); // 新建证书

    cert.sign(pKey, algrithm); // 使用 CA 私钥对其签名

    // 获取别名对应条目的证书链

    Certificate[] chain = new Certificate[] { cert };

    // 向密钥库中添加条目 , 使用已存在别名将覆盖已存在条目

    keyStore .setKeyEntry(alias, pKey, keypass.toCharArray(), chain);

    // keystore 存储至文件

    FileOutputStream fOut = new FileOutputStream( file );

    keyStore .store(fOut, storePass );

    fOut.close();

    }

    // 获取签名算法

    public String getSigAlgName(String alias) throws Exception{

    Certificate c = keyStore .getCertificate(alias); // 读取证书

    // 获取 证书内容(经过编码的字节)  

    byte [] enc = c.getEncoded();

    // 创建 X509CertImpl 对象

    X509CertImpl cimp2 = new X509CertImpl(enc);

    String sigAlgrithm=cimp2.getSigAlgName();

    return sigAlgrithm;

    }

    }

    将上述Servlet 和javabean 部署到服务器中。这样,通过访问http://localhost/GetP12Cert 就可以获得一个有效的证书dlt.cer。

    二、 证书的验证

    假设我们的客户端为iPhone客户端。则在iPhone中进行证书验证的代码为:

    NSString * path=[[ NSBundle mainBundle ] pathForResource : @"dlt.cer" ofType : nil ];

    // EfficientDate 参数为任意一个有效的日期(在证书有效期之内)

    MyTrustService * myTrust=[[ MyTrustService alloc ]

    initWithFilename :path EfficientDate : @"2011-05-10 0:0:0" ];

    ValuateResult ret=[myTrust trustValuate :[[ NSDate alloc ] init ]];

    switch (ret) {

    case ValuateResultOK :

    NSLog ( @" 证书有效 " );

    break ;

    case ValuateResultEXPIRED :

    NSLog ( @" 证书已过期 " );

    break ;

    default :

    NSLog ( @" 证书校验失败 " );

    break ;

    }

    [myTrust release ];

     

    其中MyTrustService代码为(注意导入security.framework框架):

    #import <Foundation/Foundation.h>

    #import <Security/Security.h>

    static const char * kTrustNames [ 8 ] = {

    "Invalid" ,

    "Proceed" ,

    "Confirm" ,

    "Deny" ,

    "Unspecified" ,

    "RecoverableTrustFailure" ,

    "FatalTrustFailure" ,

    "OtherError"

    };

    typedef enum

    {

    ValuateResultINVALID = 0 , // 评估结果无效,表明评估出错或未经过评估

    ValuateResultFAILED , // 证书签名无效

    ValuateResultEXPIRED , // 证书过期

    ValuateResultOK // 证书有效

    } ValuateResult;

     

    @interface MyTrustService : NSObject {

    NSString * file ;

    NSDate * efficientDate ;

    }

    -( id )initWithFilename:( NSString *)filename EfficientDate:( NSString *)date;

    -( ValuateResult )trustValuate:( NSDate *)date;

    -( ValuateResult )valuate:( SecCertificateRef )cert Trust:( SecTrustRef )trust Date:( NSDate *)date;

    @end

     

    #import "MyTrustService.h"

     

     

    @implementation MyTrustService

    -( id )initWithFilename:( NSString *)filename EfficientDate:( NSString *)date{

    if ( self =[ super init ]) {

    file =filename;

    // 设置有效日期,注意,第 2 个参数是一个有效的证书日期,只要这个日期对证书而言是有效的就行

    efficientDate =[[ NSDate alloc ] initWithString :date];

    }

    return self ;

    }

    -( ValuateResult )trustValuate:( NSDate *)date{

    ValuateResult ret;

    OSStatus             err;

        NSData *            certData;

        SecCertificateRef    cert;

        SecPolicyRef         policy;

        SecTrustRef          trust;

     

        assert ( file != nil );

        assert (date != nil );

    // 从文件获得 DER 数据

        certData = [ NSData dataWithContentsOfFile : file ];

        assert (certData != nil );

    // NSData 获得 Certificate 对象

        cert = SecCertificateCreateWithData ( NULL , ( CFDataRef ) certData);

        assert (cert != NULL );

    // 获得 x509 policy

        policy = SecPolicyCreateBasicX509 ();

        assert (policy != NULL );

    // 获得 Trust 对象

        err = SecTrustCreateWithCertificates (cert, policy, &trust);

        assert (err == noErr );

    // 由于是自签名证书,需要将锚证书设置为要验证的证书自己。注意,这样将使所有除了参数指定的锚证书之外的所有锚证书无效

        err = SecTrustSetAnchorCertificates (trust, ( CFArrayRef ) [ NSArray arrayWithObject :( id ) cert]);

        assert (err == noErr );

    // 调用 valuate 方法进行评估

    ret=[ self valuate :cert Trust :trust Date :date];

    // 评估结束,把 SecTrustSetAnchorCertificates 指定的锚证书失效,于是所有锚证书又可被信任了

       err= SecTrustSetAnchorCertificatesOnly (trust, NO );

        CFRelease (trust);

        CFRelease (policy);

        CFRelease (cert);

    return ret;

    }

    -( ValuateResult )valuate:( SecCertificateRef )cert Trust:( SecTrustRef )trust Date:( NSDate *)date{

    ValuateResult ret;

    OSStatus             err;

        SecTrustResultType   result;

        err = SecTrustSetVerifyDate (trust, ( CFDateRef ) date);

        assert (err == noErr );

    CFAbsoluteTime trustTime;

    trustTime = SecTrustGetVerifyTime (trust);

        err = SecTrustEvaluate (trust, &result);

        assert (err == noErr );

        if (result < ( sizeof ( kTrustNames ) / sizeof (* kTrustNames ))) { // if(result < 8)

            if (result== 5 ) { // if result=RecoverableTrustFailure

    // 设了个有效的日期,进行再次评估

    err= SecTrustSetVerifyDate (trust, ( CFDateRef ) efficientDate );

    assert (err== noErr );

    err= SecTrustEvaluate (trust, &result);

    assert (err == noErr );

    if (result== 4 ) { // if result=Unspecified,

    // 返回证书已过期,这里我们假设把证书尚未生效的情况也算作过期

    ret= ValuateResultEXPIRED ;

    }

    } else if (result== 4 ) { // 如果第 1 次就通过评估,证书有效

    ret= ValuateResultOK ;

    } else {

    ret= ValuateResultFAILED ; // 证书 无效

    }

     

        } else {

            NSLog ( @"result = unknown (%zu)" , ( size_t ) result);

        }

    return ret;

    }

    -( void )dealloc{

    [ efficientDate release ];

    [ super dealloc ];

    }

    @end

     

  • 相关阅读:
    ERROR in static/js/0.705716c542478b712e7e.js from UglifyJs Unexpected token: punc-分析打包过程可能遇到的报错
    CALL_AND_RETRY_LAST Allocation failed
    处理精度丢之-浮点数在计算机中是如何存储的
    webpack升级路上走过的路---之babel篇
    来自nvm的玩耍
    npm版本号的了解
    Vue-组件之间的通信
    gitkraken不能打开私有仓库
    02 Angular 闪现表达式原型问题
    01 Angular
  • 原文地址:https://www.cnblogs.com/encounter/p/2188490.html
Copyright © 2011-2022 走看看