JAVA安全-数字证书(五)

本文详细介绍数字证书的原理及应用,包括证书签发、密钥管理与加密交互流程。通过KeyTool操作,生成自签名证书及CA签发证书,实现加密网络应用平台构建。

概要

数字证书具备常规加密解密必要的信息,包含签名算法,可用于网络数据加密解密交互,标识网络用户(计算机)身份。数字证书为发布公钥提供了一种简便的途径,其数字证书则成为加密算法以及公钥的载体。依靠数字证书,我们可以构建一个简单的加密网络应用平台。数字证书类似于个人身份证,由数字证书颁发认证机构(Certificate Authority, CA)签发。只有经过CA签发的证书在网络中才具备可认证性。CA颁发给自己的证书叫根证书。VeriSign, GeoTrust和Thawte是国际权威数字证书颁发认证机构的三巨头。其中应用最广泛的是VeriSign签发的电子商务用数字证书。最为常用的非对称加密算法是RSA,与之配套的签名算法是SHA1withRSA,最常用的消息摘要算法是SHA1。

证书签发

 

2)加密交互,图略。

当客户端获取到服务器下发的数字证书后,就可以进行加密交互了。具体做法是:

客户端使用公钥,加密后发送给服务端,服务端用私钥进行解密验证。

服务端使用私钥进行加密和数字签名。

3. KeyTool 管理证书

KeyTool与本地密钥库相关联,将私钥存于密钥库,公钥则以数字证书输出。KeyTool位于JDK目录下的bin目录中,需要通过命令行进行相应的操作。

1)构建自签名证书

申请数字证书之前,需要在密钥库中以别名的方式生成本地数字证书,建立相应的加密算法,密钥,有效期等信息。

keytool -genkeypair -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 3600 -alias myCertificate -keystore myKeystore.keystore

各参数含义如下:

-genkeypair  表示生成密钥对

-keyalg    指定密钥算法,这里是RSA

-keysize    指定密钥长度,默认1024,这里指定2048

-sigal     指定签名算法,这里是SHA1withRSA

-validity    指定有效期,单位为天

-alias     指定别名

-keystore    指定密钥库存储位置

这里我输入参数Changeme123作为密钥库的密码,也可通过参数-storepass指定密码。可以用-dname "CN=xxx...."这样的形式,避免更多交互。

注意:一个keystore应该是可以存储多套<私钥-数字证书>的信息,通过别名来区分。通过实践,调用上述命令两次(别名不同),生成同一个keystore,用不同别名进行加密解密和签名验签,没有任何问题。

更多命令可参考:http://blog.chinaunix.net/uid-17102734-id-2830223.html 

经过上述操作后,密钥库中已经创建了数字证书。虽然这时的数字证书并没有经过CA认证,但并不影响我们使用。我们仍可将证书导出,发送给合作伙伴进行加密交互。

keytool -exportcert -alias myCertificate -keystore myKeystore.keystore -file myCer.cer -rfc

各参数含义如下:

-exportcert  表示证书导出操作

-alias     指定别名

-keystore   指定密钥库文件

-file      指定导出证书的文件路径

-rfc      指定以Base64编码格式输出

打印证书

keytool -printcert -file myCer.cer

2)构建CA签发证书

如果要获取CA机构谁的数字证书,需要将数字证书签发申请(CSR)导出,经由CA机构认证并颁发,将认证后的证书导入本地密钥库和信息库。

keytool -certreq -alias myCertificate -keystore myKeystore.keystore -file myCsr.csr -v

各参数含义如下:

-certreq    表示数字证书申请操作

-alias      指定别名

-keystore    指定密钥库文件路径

-file      指定导出申请的路径

-v       详细信息

获得签发的数字证书后,需要将其导入信任库。

keytool -importcert -trustcacerts -alias myCertificate -file myCer.cer -keystore myKeystore.keystore

参数不作详细讲解,如果是原来的证书文件,那么会报错:

查看证书

keytool -list -alias myCertificate -keystore myKeystore.keystore

 

经过上述的所有操作后,可以得到下面几个文件

4. 证书使用

终于到了激动人心的时刻,可以用代码通过keystore进行加解密操作了!

Java 6提供了完善的数字证书管理实现,我们几乎无需关注,仅通过操作密钥库和数字证书就可完成相应的加密解密和签名验签过程。

密钥库管理私钥,数字证书管理公钥,公钥和私钥分属消息传递双方,进行加密消息传递。

考虑一个场景。

A机器某模块需要将数据导出到一个文件中,将文件发送到B机器,由B将数据导入。

在这个场景中,A就相当于服务端,需要将证书给B,同时用私钥加密数据,生成签名,导出到文件中。

B相当于客户端,用收到的数字证书进行解密和验签。

public class MyCertifacate {
    private static final String STORE_PASS = "Changeme123";
    private static final String ALIAS = "myCertificate";
    private static final String KEYSTORE_PATH = "D:\\JavaDemo\\Certifacate\\myKeystore.keystore";
    private static final String CERT_PATH = "D:\\JavaDemo\\Certifacate\\myCer.cer";
    private static final String PLAIN_TEXT = "MANUTD is the most greatest club in the world.";
    /** JDK6只支持X.509标准的证书 */
    private static final String CERT_TYPE = "X.509";

    public static void main(String[] args) throws IOException {
        /**
         * 假设现在有这样一个场景 。A机器上的数据,需要加密导出,然后将导出文件放到B机器上导入。 在这个场景中,A相当于服务器,B相当于客户端
         */

        /** A */
        KeyStore keyStore = getKeyStore(STORE_PASS, KEYSTORE_PATH);
        PrivateKey privateKey = getPrivateKey(keyStore, ALIAS, STORE_PASS);
        X509Certificate certificate = getCertificateByKeystore(keyStore, ALIAS);

        /** 加密和签名 */
        byte[] encodedText = encode(PLAIN_TEXT.getBytes(), privateKey);
        byte[] signature = sign(certificate, privateKey, PLAIN_TEXT.getBytes());

        /** 现在B收到了A的密文和签名,以及A的可信任证书 */
        X509Certificate receivedCertificate = getCertificateByCertPath(
                CERT_PATH, CERT_TYPE);
        PublicKey publicKey = getPublicKey(receivedCertificate);
        byte[] decodedText = decode(encodedText, publicKey);
        System.out.println("Decoded Text : " + new String(decodedText));
        System.out.println("Signature is : "
                + verify(receivedCertificate, decodedText, signature));
    }

    /**
     * 加载密钥库,与Properties文件的加载类似,都是使用load方法
     * 
     * @throws IOException
     */
    public static KeyStore getKeyStore(String storepass, String keystorePath)
            throws IOException {
        InputStream inputStream = null;
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            inputStream = new FileInputStream(keystorePath);
            keyStore.load(inputStream, storepass.toCharArray());
            return keyStore;
        } catch (KeyStoreException | NoSuchAlgorithmException
                | CertificateException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
        }
        return null;
    }

    /**
     * 获取私钥
     * 
     * @param keyStore
     * @param alias
     * @param password
     * @return
     */
    public static PrivateKey getPrivateKey(KeyStore keyStore, String alias,
            String password) {
        try {
            return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
        } catch (UnrecoverableKeyException | KeyStoreException
                | NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取公钥
     * 
     * @param certificate
     * @return
     */
    public static PublicKey getPublicKey(Certificate certificate) {
        return certificate.getPublicKey();
    }

    /**
     * 通过密钥库获取数字证书,不需要密码,因为获取到Keystore实例
     * 
     * @param keyStore
     * @param alias
     * @return
     */
    public static X509Certificate getCertificateByKeystore(KeyStore keyStore,
            String alias) {
        try {
            return (X509Certificate) keyStore.getCertificate(alias);
        } catch (KeyStoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过证书路径生成证书,与加载密钥库差不多,都要用到流。
     * 
     * @param path
     * @param certType
     * @return
     * @throws IOException
     */
    public static X509Certificate getCertificateByCertPath(String path,
            String certType) throws IOException {
        InputStream inputStream = null;
        try {
            // 实例化证书工厂
            CertificateFactory factory = CertificateFactory
                    .getInstance(certType);
            // 取得证书文件流
            inputStream = new FileInputStream(path);
            // 生成证书
            Certificate certificate = factory.generateCertificate(inputStream);

            return (X509Certificate) certificate;
        } catch (CertificateException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
        }
        return null;

    }

    /**
     * 从证书中获取加密算法,进行签名
     * 
     * @param certificate
     * @param privateKey
     * @param plainText
     * @return
     */
    public static byte[] sign(X509Certificate certificate,
            PrivateKey privateKey, byte[] plainText) {
        /** 如果要从密钥库获取签名算法的名称,只能将其强制转换成X509标准,JDK 6只支持X.509类型的证书 */
        try {
            Signature signature = Signature.getInstance(certificate
                    .getSigAlgName());
            signature.initSign(privateKey);
            signature.update(plainText);
            return signature.sign();
        } catch (NoSuchAlgorithmException | InvalidKeyException
                | SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 验签,公钥包含在证书里面
     * 
     * @param certificate
     * @param decodedText
     * @param receivedignature
     * @return
     */
    public static boolean verify(X509Certificate certificate,
            byte[] decodedText, final byte[] receivedignature) {
        try {
            Signature signature = Signature.getInstance(certificate
                    .getSigAlgName());
            /** 注意这里用到的是证书,实际上用到的也是证书里面的公钥 */
            signature.initVerify(certificate);
            signature.update(decodedText);
            return signature.verify(receivedignature);
        } catch (NoSuchAlgorithmException | InvalidKeyException
                | SignatureException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 加密。注意密钥是可以获取到它适用的算法的。
     * 
     * @param plainText
     * @param privateKey
     * @return
     */
    public static byte[] encode(byte[] plainText, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return cipher.doFinal(plainText);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;

    }

    /**
     * 解密,注意密钥是可以获取它适用的算法的。
     * 
     * @param encodedText
     * @param publicKey
     * @return
     */
    public static byte[] decode(byte[] encodedText, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return cipher.doFinal(encodedText);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }
}

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值