1. 程式人生 > >Java語言的非對稱加密的實現

Java語言的非對稱加密的實現

眾所周知,網際網路上的安全是非常重要的一個課題,如何讓我們的軟體,通訊協議更加安全,是每個程式設計師都需要思考的問題。
本文主要討論三種非對稱加密的情況。
1.私鑰加密-公鑰解密
2.公鑰加密-私鑰解密
3.私鑰簽名-公鑰驗證

產生金鑰對

無論上述哪種情況,我們都需要產生一個金鑰對。

使用Keytool生成

需要藉助JDK中自帶的keytool來生成。在命令列模式下輸入keytool.

這裡寫圖片描述

如果配置正常,應該顯示以上內容,如果提示commend not found.那需要把JDK的路徑加入到Path中。有不會的同學請自行百度。

我們使用-genkeypair命令。

在命令列中輸入:

keytool -genkey -help

這裡寫圖片描述

可以看到這裡詳細引數的用法。

我們建立一個金鑰庫(含金鑰對),命令如下:

keytool -genkey -alias blogkey -keyalg RSA -validity 36500 -keystore h:\key\blogkey.keystore

這裡寫圖片描述

-alias 金鑰庫的別名,用於識別這個金鑰庫
-keyalg 這裡我們使用常見的RSA演算法
-validity 有效期,這裡我們填了100年
-keystore 生成的檔名

根據系統引導填完內容,我們就建立了一個金鑰庫。

接下來我們需要匯出證書,裡面包含公鑰。
我們使用-export命令。
這裡寫圖片描述

keytool -export -rfc -alias blogkey -file pubkey.cer -keystore h:\key\blogkey.keystore

我們把匯出的證書存為pubkey.cer

到目前為止,我們獲取一個blogkey.keystore的金鑰庫及pubkey.cer的證書,其中blogkey.keystore自用,pubkey.cer轉給別人使用。

從keystore中提取privatekey

    private static PrivateKey getPrivateKey(String keyStorePath, String alias, String password)
            throws Exception {
        FileInputStream is
= new FileInputStream(keyStorePath); KeyStore ks = KeyStore.getInstance("JKS"); ks.load(is, password.toCharArray()); is.close(); PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray()); return key; }

在pubkey.cer中獲取publickey

    private static PublicKey getPublicKey(String certificatePath)
            throws Exception {

       CertificateFactory certificateFactory = CertificateFactory
                .getInstance("X.509");
        FileInputStream in = new FileInputStream(certificatePath);

        Certificate certificate = certificateFactory.generateCertificate(in);
        in.close();

        PublicKey key = certificate.getPublicKey();
        return key;
    }

使用KeyPairGenerator直接生成

        KeyPairGenerator keyPairGenerator =  KeyPairGenerator.getInstance("RSA");  
        //可支援DiffieHellman (1024), DSA (1024),RSA (1024, 2048)
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

私鑰加密-公鑰解密

前面的章節我們獲得了privateKey及publicKey, 接下來的事情就簡單了。

私鑰加密

    private static byte[] encryptByPrivateKey(data[] plainData, PrivateKey privateKey )
            throws Exception {
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(plainData);
    }

公鑰解密

public static byte[] decryptByPublicKey(byte[] data, PublicKey publicKey )
        throws Exception {
    Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
    cipher.init(Cipher.DECRYPT_MODE, publicKey);
    return cipher.doFinal(data);
}

公鑰加密-私鑰解密

公鑰加密

  public static byte[] encryptByPublicKey(byte[] plainData, PublicKey publicKey)
            throws Exception {
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(plainData);

    }

私鑰解密

  public static byte[] decryptByPrivateKey(byte[] data,  PrivateKey privateKey ) throws Exception {
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);

    }

私鑰簽名-公鑰驗證

私鑰簽名

利用私鑰簽名除了私鑰之外,我們還需要一個證書,因此如果沒有在第一步生成pubkey.cer,這部分是無法實驗的。



    public static byte[] sign(byte[] sign, String keyStorePath, String alias,
                              String password) throws Exception {
        // 獲得證書
        X509Certificate x509Certificate = (X509Certificate) getCertificate(
                keyStorePath, alias, password);
        // 獲取私鑰
        KeyStore ks = getKeyStore(keyStorePath, password);
        // 取得私鑰
        PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
                .toCharArray());

        // 構建簽名
        Signature signature = Signature.getInstance(x509Certificate
                .getSigAlgName());
        signature.initSign(privateKey);
        signature.update(sign);
        return (signature.sign());
    }

  private static Certificate getCertificate(String keyStorePath,
                                              String alias, String password) throws Exception {
        KeyStore ks = getKeyStore(keyStorePath, password);
        Certificate certificate = ks.getCertificate(alias);

        return certificate;
    }

公鑰驗證

   public static boolean verify(byte[] data, byte[] sign,
                                 String certificatePath) throws Exception {
        // 獲得證書
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
        // 獲得公鑰
        PublicKey publicKey = x509Certificate.getPublicKey();
        // 構建簽名
        Signature signature = Signature.getInstance(x509Certificate
                .getSigAlgName());
        signature.initVerify(publicKey);
        signature.update(data);

        return signature.verify((sign));

    }
    private static Certificate getCertificate(String certificatePath)
            throws Exception {
        CertificateFactory certificateFactory = CertificateFactory
                .getInstance(X509);
        FileInputStream in = new FileInputStream(certificatePath);

        Certificate certificate = certificateFactory.generateCertificate(in);
        in.close();

        return certificate;
    }

小結

本文講述了利用公鑰和私鑰對通訊過程的資料進行非對稱加密。而在實際應用過程中,由於非對稱加密比對稱加密更加費CPU,因此,通常都是非對稱加密與對稱加密相結合。即對稱加密使用的金鑰通過非對稱加密來傳遞。對於交換金鑰,另一個演算法叫DH和他的優化演算法Oakley,專門用來處理這類任務,有興趣的同學可以下去了解下。

加密的目的是為了提高破解門檻,要記住一句話,世界沒有絕對安全,只有更加安全。

擴充套件,關於Key的序列化問題

通常,我們的Key是以byte[]的形式進行交換,因此,我們需要把PublicKey/PrivateKey的物件轉換為byte[].
在Key的方法有一個getEncoded()能夠將其轉為byte[].

        byte[] dataprivate = privateKey.getEncoded();
        byte[] datapublic = publicKey.getEncoded();

在恢復時,則需要分別處理。
對於私鑰,我們有

        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(dataprivate);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey1 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

對於公鑰,我們有

    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(datapublic);
    keyFactory = KeyFactory.getInstance("RSA");
    PublicKey publicKey1 = keyFactory.generatePublic(x509EncodedKeySpec);

參考文章:

參考書目:Java加密與解密的藝術