加密之非對(duì)稱RSA,DH,DSA,ECC

1 RSA算法

1.1 定義

RSA,這種算法1978年就出現(xiàn)了,它是第一個(gè)既能用于數(shù)據(jù)加密也能用于數(shù)字簽名的算法。它易于理解和操作,也很流行。算法的名字以發(fā)明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
這種加密算法的特點(diǎn)主要是密鑰的變化,DES只有一個(gè)密鑰。相當(dāng)于只有一把鑰匙,如果這把鑰匙丟了,數(shù)據(jù)也就不安全了。但是RSA同時(shí)有兩把鑰匙,公鑰私鑰。同時(shí)支持?jǐn)?shù)字簽名。數(shù)字簽名的意義在于,對(duì)傳輸過來的數(shù)據(jù)進(jìn)行校驗(yàn)。確保數(shù)據(jù)在傳輸工程中不被修改。

RSA算法一直是最廣為使用的非對(duì)稱加密算法。毫不夸張地說,只要有計(jì)算機(jī)網(wǎng)絡(luò)的地方,就有RSA算法。
這種算法非??煽?,密鑰越長,它就越難破解。根據(jù)已經(jīng)披露的文獻(xiàn),目前被破解的最長RSA密鑰是768個(gè)二進(jìn)制位。也就是說,長度超過768位的密鑰,還無法破解(至少?zèng)]人公開宣布)。因此可以認(rèn)為,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。

1.2 原理

流程分析:
甲方構(gòu)建密鑰對(duì)兒,將公鑰公布給乙方,將私鑰保留。
甲方使用私鑰加密數(shù)據(jù),然后用私鑰對(duì)加密后的數(shù)據(jù)簽名,發(fā)送給乙方簽名以及加密后的數(shù)據(jù);乙方使用公鑰、簽名來驗(yàn)證待解密數(shù)據(jù)是否有效,如果有效使用公鑰對(duì)數(shù)據(jù)解密。
乙方使用公鑰加密數(shù)據(jù),向甲方發(fā)送經(jīng)過加密后的數(shù)據(jù);甲方獲得加密數(shù)據(jù),通過私鑰解密

1.2.1 互質(zhì)關(guān)系

如果兩個(gè)正整數(shù),除了1以外,沒有其他公因子,我們就稱這兩個(gè)數(shù)是互質(zhì)關(guān)系(coprime)。比如,15和32沒有公因子,所以它們是互質(zhì)關(guān)系。這說明,不是質(zhì)數(shù)也可以構(gòu)成互質(zhì)關(guān)系。
關(guān)于互質(zhì)關(guān)系,不難得到以下結(jié)論:

  1. 任意兩個(gè)質(zhì)數(shù)構(gòu)成互質(zhì)關(guān)系,比如13和61。
  2. 一個(gè)數(shù)是質(zhì)數(shù),另一個(gè)數(shù)只要不是前者的倍數(shù),兩者就構(gòu)成互質(zhì)關(guān)系,比如3和10。
  3. 如果兩個(gè)數(shù)之中,較大的那個(gè)數(shù)是質(zhì)數(shù),則兩者構(gòu)成互質(zhì)關(guān)系,比如97和57。
  4. 1和任意一個(gè)自然數(shù)是都是互質(zhì)關(guān)系,比如1和99。
  5. p是大于1的整數(shù),則p和p-1構(gòu)成互質(zhì)關(guān)系,比如57和56。
  6. p是大于1的奇數(shù),則p和p-2構(gòu)成互質(zhì)關(guān)系,比如17和15

1.2.2 歐拉函數(shù)

請(qǐng)思考以下問題:
任意給定正整數(shù)n,請(qǐng)問在小于等于n的正整數(shù)之中,有多少個(gè)與n構(gòu)成互質(zhì)關(guān)系?(比如,在1到8之中,有多少個(gè)數(shù)與8構(gòu)成互質(zhì)關(guān)系?)
計(jì)算這個(gè)值的方法就叫做歐拉函數(shù),以φ(n)表示。在18之中,與8形成互質(zhì)關(guān)系的是1、3、5、7,所以 φ(n) = 4
φ(n) 的計(jì)算方法并不復(fù)雜,但是為了得到最后那個(gè)公式,需要一步步討論。
第一種情況
如果n=1,則 φ(1) = 1。因?yàn)?與任何數(shù)(包括自身)都構(gòu)成互質(zhì)關(guān)系。
第二種情況
如果n是質(zhì)數(shù),則 φ(n)=n-1 。因?yàn)橘|(zhì)數(shù)與小于它的每一個(gè)數(shù),都構(gòu)成互質(zhì)關(guān)系。比如5與1、2、3、4都構(gòu)成互質(zhì)關(guān)系。
第三種情況
如果n是質(zhì)數(shù)的某一個(gè)次方,即 n = p^k (p為質(zhì)數(shù),k為大于等于1的整數(shù)),則

在這里插入圖片描述

比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4
這是因?yàn)橹挥挟?dāng)一個(gè)數(shù)不包含質(zhì)數(shù)p,才可能與n互質(zhì)。而包含質(zhì)數(shù)p的數(shù)一共有p^(k-1)個(gè),即1×p、2×p、3×p、...、p^(k-1)×p,把它們?nèi)コO碌木褪桥cn互質(zhì)的數(shù)。
上面的式子還可以寫成下面的形式:
在這里插入圖片描述

可以看出,上面的第二種情況是 k=1 時(shí)的特例。
第四種情況
如果n可以分解成兩個(gè)互質(zhì)的整數(shù)之積,n = p1 × p2
則 φ(n) = φ(p1p2) = φ(p1)φ(p2)
積的歐拉函數(shù)等于各個(gè)因子的歐拉函數(shù)之積。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24
這一條的證明要用到"中國剩余定理",這里就不展開了,只簡單說一下思路:如果ap1互質(zhì)(a<p1),b與p2互質(zhì)(b<p2),c與p1p2互質(zhì)(c<p1p2),則c與數(shù)對(duì) (a,b) 是一一對(duì)應(yīng)關(guān)系。由于a的值有φ(p1)種可能,b的值有φ(p2)種可能,則數(shù)對(duì) (a,b) 有φ(p1)φ(p2)種可能,而c的值有φ(p1p2)種可能,所以φ(p1p2)就等于φ(p1)φ(p2)。
第五種情況
因?yàn)槿我庖粋€(gè)大于1的正整數(shù),都可以寫成一系列質(zhì)數(shù)的積。
在這里插入圖片描述

根據(jù)第4條的結(jié)論,得到
在這里插入圖片描述

再根據(jù)第3條的結(jié)論,得到
在這里插入圖片描述

也就等于
在這里插入圖片描述

這就是歐拉函數(shù)的通用計(jì)算公式。比如,1323的歐拉函數(shù),計(jì)算過程如下:
在這里插入圖片描述

1.2.3 歐拉定理

歐拉函數(shù)的用處,在于歐拉定理。歐拉定理指的是:
如果兩個(gè)正整數(shù)an互質(zhì),則n的歐拉函數(shù) φ(n) 可以讓下面的等式成立:

在這里插入圖片描述

也就是說,aφ(n)次方被n除的余數(shù)為1。或者說,aφ(n)次方減去1,可以被n整除。比如,3和7互質(zhì),而7的歐拉函數(shù)φ(7)等于6,所以3的6次方(729)減去1,可以被7整除(728/7=104)。
歐拉定理的證明比較復(fù)雜,這里就省略了。我們只要記住它的結(jié)論就行了。
歐拉定理可以大大簡化某些運(yùn)算。比如,7和10互質(zhì),根據(jù)歐拉定理,
在這里插入圖片描述

已知 φ(10) 等于4,所以馬上得到74倍數(shù)次方的個(gè)位數(shù)肯定是1。
在這里插入圖片描述

因此,7的任意次方的個(gè)位數(shù)(例如7的222次方),心算就可以算出來。
歐拉定理有一個(gè)特殊情況。
假設(shè)正整數(shù)a與質(zhì)數(shù)p互質(zhì),因?yàn)橘|(zhì)數(shù)pφ(p)等于p-1,則歐拉定理可以寫成
在這里插入圖片描述

這就是著名的費(fèi)馬小定理。它是歐拉定理的特例。
歐拉定理是RSA算法的核心。理解了這個(gè)定理,就可以理解RSA。

1.2.4 模反元素

還剩下最后一個(gè)概念:
如果兩個(gè)正整數(shù)a和n互質(zhì),那么一定可以找到整數(shù)b,使得 ab-1 被n整除,或者說ab被n除的余數(shù)是1。

在這里插入圖片描述

這時(shí),b就叫做a的模反元素
比如,3和11互質(zhì),那么3的模反元素就是4,因?yàn)?(3 × 4)-1 可以被11整除。顯然,模反元素不止一個(gè), 4加減11的整數(shù)倍都是3的模反元素 {...,-18,-7,4,15,26,...},即如果b是a的模反元素,則 b+kn 都是a的模反元素。
歐拉定理可以用來證明模反元素必然存在。

可以看到,a的 φ(n)-1 次方,就是a的模反元素。

1.3 實(shí)際操作

下面代碼用到的常量:

    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";

1.3.1 生成公私鑰并獲取

   /**
     * 初始化密鑰
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);

        KeyPair keyPair = keyPairGen.generateKeyPair();
        //   公鑰
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //   私鑰
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

 /**
     * 取得私鑰
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return en.encode(key.getEncoded());
    }

    /**
     * 取得公鑰
     */
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return en.encode(key.getEncoded());
    }

1.3.2 公私鑰加簽驗(yàn)簽

    /**
     * 用私鑰對(duì)信息生成數(shù)字簽名
     * @param data       加密數(shù)據(jù)
     * @param privateKey 私鑰
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        //   解密由base64編碼的私鑰
        byte[] keyBytes = de.decodeBuffer(privateKey);
        //   構(gòu)造PKCS8EncodedKeySpec對(duì)象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //   KEY_ALGORITHM   指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //   取私鑰匙對(duì)象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        //   用私鑰對(duì)信息生成數(shù)字簽名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);
        return en.encode(signature.sign());
    }


    /**
     * 公鑰校驗(yàn)數(shù)字簽名
     * @param data      加密數(shù)據(jù)
     * @param publicKey 公鑰
     * @param sign      數(shù)字簽名
     * @return 校驗(yàn)成功返回true   失敗返回false
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        //   解密由base64編碼的公鑰
        byte[] keyBytes = de.decodeBuffer(publicKey);
        //   構(gòu)造X509EncodedKeySpec對(duì)象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        //   KEY_ALGORITHM   指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //   取公鑰匙對(duì)象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        //   驗(yàn)證簽名是否正常
        return signature.verify(de.decodeBuffer(sign));
    }

1.3.3 公私鑰加密解密

    /**
     * 加密
     * 用私鑰加密
     * @param data
     * @param key
     */
    public static String decryptByPrivateKey(byte[] data, String key) throws Exception {
        //   對(duì)密鑰解密
        byte[] keyBytes = de.decodeBuffer(key);
        //   取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //   對(duì)數(shù)據(jù)解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(cipher.doFinal(data));
    }

    /**
     * 加密
     * 用公鑰加密
     * @param data
     * @param key
     */
    public static String decryptByPublicKey(byte[] data, String key) throws Exception {
        //   對(duì)密鑰解密
        byte[] keyBytes = de.decodeBuffer(key);
        //   取得公鑰
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        //   對(duì)數(shù)據(jù)解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return new String(cipher.doFinal(data));
    }

    /**
     * 加密<br>
     * 用公鑰加密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(byte[] data, String key) throws Exception {
        //   對(duì)公鑰解密
        byte[] keyBytes = de.decodeBuffer(key);
        //   取得公鑰
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        //   對(duì)數(shù)據(jù)加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return en.encode(cipher.doFinal(data));
    }

    /**
     * 加密<br>
     * 用私鑰加密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPrivateKey(byte[] data, String key) throws Exception {
        //   對(duì)密鑰解密
        byte[] keyBytes = de.decodeBuffer(key);
        //   取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //   對(duì)數(shù)據(jù)加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);

        return en.encode(cipher.doFinal(data));
    }

1.3.4 結(jié)果驗(yàn)證

    public static void main(String[] args) throws Exception{
        //生成 公鑰私鑰
        Map<String, Object> keyMap = initKey();
        String publicKey = getPublicKey(keyMap);
        String privateKey = getPrivateKey(keyMap);
        System.err.println("公鑰:   \n\r" + publicKey);
        System.err.println("私鑰:   \n\r" + privateKey);

        System.err.println("公鑰加密——私鑰解密");
        String inputStr = "abc";
        byte[] data = inputStr.getBytes();
        String encodedData = encryptByPublicKey(data, publicKey);
        String decodedData = decryptByPrivateKey(de.decodeBuffer(encodedData),privateKey);
        System.err.println("加密前:   " + inputStr +"\n\r" +"加密中:   "+encodedData+"\n\r" + "解密后:   " + decodedData);
        System.err.println("公鑰加密——私鑰解密");


        System.err.println("私鑰加密——公鑰解密");
        String inputStr_new = "sign";
        byte[] data_new = inputStr_new.getBytes();

        String encodedData_new = encryptByPrivateKey(data_new, privateKey);
        String decodedData_new =decryptByPublicKey(de.decodeBuffer(encodedData_new), publicKey);

        System.err.println("加密前:   " + inputStr_new + "\n\r" +"加密中:   "+encodedData_new+"\n\r" + "解密后:   " + decodedData_new);
        System.err.println("私鑰加密——公鑰解密");

        System.err.println("私鑰簽名——公鑰驗(yàn)證簽名");
        //   產(chǎn)生簽名
        String sign = sign(encodedData_new.getBytes(), privateKey);
        System.err.println("簽名:\r\n" + sign);
        //   驗(yàn)證簽名
        boolean status = verify(encodedData_new.getBytes(), publicKey, sign);
        System.err.println("狀態(tài):\r\n" + status);
        System.err.println("私鑰簽名——公鑰驗(yàn)證簽名");
    }

2 DH算法

2.1 定義

接下來我們分析DH加密算法,一種適基于密鑰一致協(xié)議的加密算法。
Diffie-Hellman算法(D-H算法),密鑰一致協(xié)議,非對(duì)稱加密算法。
是由公開密鑰密碼體制的奠基人Diffie和Hellman所提出的一種思想。簡單的說就是允許兩名用戶在公開媒體上交換信息以生成一致的、可以共享的密鑰。換句話說,就是由甲方產(chǎn)出一對(duì)密鑰(公鑰、私鑰),乙方依照甲方公鑰產(chǎn)生乙方密鑰對(duì)(公鑰、私鑰)。以此為基線,作為數(shù)據(jù)傳輸保密基礎(chǔ),同時(shí)雙方使用同一種對(duì)稱加密算法構(gòu)建本地密鑰(SecretKey)對(duì)數(shù)據(jù)加密。這樣,在互通了本地密鑰(SecretKey)算法后,甲乙雙方公開自己的公鑰,使用對(duì)方的公鑰和剛才產(chǎn)生的私鑰加密數(shù)據(jù),同時(shí)可以使用對(duì)方的公鑰和自己的私鑰對(duì)數(shù)據(jù)解密。不單單是甲乙雙方兩方,可以擴(kuò)展為多方共享數(shù)據(jù)通訊,這樣就完成了網(wǎng)絡(luò)交互數(shù)據(jù)的安全通訊!該算法源于中國的同余定理——中國馀數(shù)定理

流程分析:

  1. 甲方構(gòu)建密鑰對(duì)兒,將公鑰公布給乙方,將私鑰保留;雙方約定數(shù)據(jù)加密算法;乙方通過甲方公鑰構(gòu)建密鑰對(duì)兒,將公鑰公布給甲方,將私鑰保留。
  2. 甲方使用私鑰、乙方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰加密數(shù)據(jù),發(fā)送給乙方加密后的數(shù)據(jù);乙方使用私鑰、甲方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰對(duì)數(shù)據(jù)解密。
  3. 乙方使用私鑰、甲方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰加密數(shù)據(jù),發(fā)送給甲方加密后的數(shù)據(jù);甲方使用私鑰、乙方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰對(duì)數(shù)據(jù)解密。

2.2 實(shí)際操作

下面代碼用到的常量

 /**
     * 默認(rèn)密鑰字節(jié)數(shù)
     * Default Keysize 1024
     */
    private static final int KEY_SIZE = 1024;
    public static final String ALGORITHM = "DH";
    /**
     * DH加密下需要一種對(duì)稱加密算法對(duì)數(shù)據(jù)加密,這里我們使用DES,也可以使用其他對(duì)稱加密算法。
     */
    public static final String SECRET_ALGORITHM = "DES";
    private static final String PUBLIC_KEY = "DHPublicKey";
    private static final String PRIVATE_KEY = "DHPrivateKey";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();

2.2.1 生成甲乙公私鑰

2.2.1.1 生成甲公私鑰

    /**
     * 初始化甲方密鑰
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 甲方公鑰
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        // 甲方私鑰
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

2.2.1.2 根據(jù)甲公鑰生成乙公私鑰

    /**
     * 使用甲方公鑰====初始化乙方密鑰
     */
    public static Map<String, Object> initKey(String key) throws Exception {
        // 解析甲方公鑰  
        byte[] keyBytes = de.decodeBuffer(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        // 由甲方公鑰====構(gòu)建乙方密鑰
        DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
        keyPairGenerator.initialize(dhParamSpec);

        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 乙方公鑰
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
        // 乙方私鑰
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

2.2.1.3 獲取公私鑰

    /**
     * 取得私鑰
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return en.encode(key.getEncoded());
    }

    /**
     * 取得公鑰
     */
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return en.encode((key.getEncoded()));
    }

2.2.2 根據(jù)公私鑰生成本地密鑰

    /**
     * 構(gòu)建密鑰
     *
     * @param publicKey  公鑰
     * @param privateKey 私鑰
     */
    private static SecretKey getSecretKey(String publicKey, String privateKey) throws Exception {
        // 初始化公鑰
        byte[] pubKeyBytes = de.decodeBuffer(publicKey);

        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

        // 初始化私鑰
        byte[] priKeyBytes = de.decodeBuffer(privateKey);

        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
        Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
        //用自己的私鑰初始化keyAgreement
        keyAgree.init(priKey);
        keyAgree.doPhase(pubKey, true);

        // 生成本地密鑰
        SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);
        return secretKey;
    }

2.2.3 加密解密

    /**
     * 加密<br>
     * @param data       待加密數(shù)據(jù)
     * @param publicKey  甲方公鑰
     * @param privateKey 乙方私鑰
     */
    public static String encrypt(byte[] data, String publicKey,String privateKey) throws Exception {
        // 生成本地密鑰
        SecretKey secretKey = getSecretKey(publicKey, privateKey);
        // 數(shù)據(jù)加密
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return en.encode(cipher.doFinal(data));
    }

    /**
     * 解密<br>
     *
     * @param data       待解密數(shù)據(jù)
     * @param publicKey  乙方公鑰
     * @param privateKey 乙方私鑰
     */
    public static String decrypt(byte[] data, String publicKey,String privateKey) throws Exception {
        // 生成本地密鑰
        SecretKey secretKey = getSecretKey(publicKey, privateKey);
        // 數(shù)據(jù)解密  
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return new String(cipher.doFinal(data));
    }

2.2.4 結(jié)果驗(yàn)證

    public static void main(String[] args)throws Exception {
        // 生成甲方密鑰對(duì)兒
        Map<String, Object> aKeyMap = initKey();
        String aPublicKey = getPublicKey(aKeyMap);
        String aPrivateKey = getPrivateKey(aKeyMap);
        System.err.println("甲方公鑰:\r\n" + aPublicKey);
        System.err.println("甲方私鑰:\r\n" + aPrivateKey);

        // 由甲方公鑰產(chǎn)生本地密鑰對(duì)兒
        Map<String, Object> bKeyMap = initKey(aPublicKey);
        String bPublicKey = getPublicKey(bKeyMap);
        String bPrivateKey = getPrivateKey(bKeyMap);

        System.err.println("乙方公鑰:\r\n" + bPublicKey);
        System.err.println("乙方私鑰:\r\n" + bPrivateKey);

        String aInput = "abc ";
        System.err.println("原文: " + aInput);

        // 由甲方公鑰,乙方私鑰構(gòu)建密文
        String aCode = encrypt(aInput.getBytes(), aPublicKey,bPrivateKey);
        System.err.println("由甲方公鑰,乙方私鑰構(gòu)建密文: \r\n" + aCode);
        // 由乙方公鑰,甲方私鑰解密
        String aDecode = decrypt(de.decodeBuffer(aCode), bPublicKey, aPrivateKey);
        System.err.println("由乙方公鑰,甲方私鑰解密: \r\n" + aDecode);


        System.err.println(" ===============反過來加密解密================== ");
        String bInput = "def ";
        System.err.println("原文: " + bInput);

        // 由乙方公鑰,甲方私鑰構(gòu)建密文
        String bCode = encrypt(bInput.getBytes(), bPublicKey,aPrivateKey);
        System.err.println("由乙方公鑰,甲方私鑰構(gòu)建密文: \r\n" + bCode);
        // 由甲方公鑰,乙方私鑰解密
        String bDecode = decrypt(de.decodeBuffer(bCode), aPublicKey, bPrivateKey);
        System.err.println("由甲方公鑰,乙方私鑰解密: \r\n" + bDecode);

    }

3 DSA算法

3.1 定義

接下來我們介紹DSA數(shù)字簽名,非對(duì)稱加密的另一種實(shí)現(xiàn)。
DSADSA-Digital Signature AlgorithmSchnorrElGamal簽名算法的變種,被美國NIST作為DSS(DigitalSignature Standard)。簡單的說,這是一種更高級(jí)的驗(yàn)證方式,用作數(shù)字簽名。不單單只有公鑰、私鑰,還有數(shù)字簽名。私鑰加密生成數(shù)字簽名,公鑰驗(yàn)證數(shù)據(jù)及簽名。如果數(shù)據(jù)和簽名不匹配則認(rèn)為驗(yàn)證失敗!數(shù)字簽名的作用就是校驗(yàn)數(shù)據(jù)在傳輸過程中不被修改。數(shù)字簽名,是單向加密的升級(jí)

3.2 實(shí)際操作

用到的常量

public static final String ALGORITHM = "DSA";

    /**
     * 默認(rèn)密鑰字節(jié)數(shù)
     */
    private static final int KEY_SIZE = 1024;

    /**
     * 默認(rèn)種子
     */
    private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";

    private static final String PUBLIC_KEY = "DSAPublicKey";
    private static final String PRIVATE_KEY = "DSAPrivateKey";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();

3.2.1 生成公私鑰并獲取

 /**
     * 生成密鑰
     *
     * @param seed 種子
     * @return 密鑰對(duì)象
     * @throws Exception
     */
    public static Map<String, Object> initKey(String seed) throws Exception {
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
        //  初始化隨機(jī)產(chǎn)生器
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.setSeed(seed.getBytes());
        keygen.initialize(KEY_SIZE, secureRandom);

        KeyPair keys = keygen.genKeyPair();
        DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
        DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();

        Map<String, Object> map = new HashMap<String, Object>(2);
        map.put(PUBLIC_KEY, publicKey);
        map.put(PRIVATE_KEY, privateKey);
        return map;
    }

    /**
     * 取得私鑰
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return en.encode(key.getEncoded());
    }

    /**
     * 取得公鑰
     */
    public static String getPublicKey(Map<String, Object> keyMap)  throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return en.encode(key.getEncoded());
    }

3.2.2 加簽驗(yàn)簽

3.2.2.1 私鑰加簽

    /**
     * 用私鑰對(duì)信息生成數(shù)字簽名
     * @param data       加密數(shù)據(jù)
     * @param privateKey 私鑰
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        //  解密由base64編碼的私鑰
        byte[] keyBytes = de.decodeBuffer(privateKey);

        //  構(gòu)造PKCS8EncodedKeySpec對(duì)象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

        //  KEY_ALGORITHM  指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

        //  取私鑰匙對(duì)象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        //  用私鑰對(duì)信息生成數(shù)字簽名
        Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
        signature.initSign(priKey);
        signature.update(data);
        return en.encode(signature.sign());
    }

3.2.2.1 公鑰驗(yàn)簽

    /**
     * 校驗(yàn)數(shù)字簽名
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        //  解密由base64編碼的公鑰
        byte[] keyBytes = de.decodeBuffer(publicKey);

        //  構(gòu)造X509EncodedKeySpec對(duì)象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        //  ALGORITHM  指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        //  取公鑰匙對(duì)象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
        signature.initVerify(pubKey);
        signature.update(data);
        //  驗(yàn)證簽名是否正常
        return signature.verify(de.decodeBuffer(sign));
    }

3.2.3 結(jié)果驗(yàn)證

    public static void main(String[] args) throws Exception{
        String inputStr = "abc";
        byte[] data = inputStr.getBytes();
        //  構(gòu)建密鑰
        Map<String, Object> keyMap = initKey(DEFAULT_SEED);

        //  獲得密鑰
        String publicKey = getPublicKey(keyMap);
        String privateKey = getPrivateKey(keyMap);

        System.err.println("公鑰:\r\n" + publicKey);
        System.err.println("私鑰:\r\n" + privateKey);

        //  產(chǎn)生簽名    
        String sign = sign(data, privateKey);
        System.err.println("簽名:\r\n" + sign);

        //  驗(yàn)證簽名
        boolean status = verify(data, publicKey, sign);
        System.err.println("狀態(tài):\r\n" + status);
    }

4 ECC

4.1 定義

ECC非對(duì)稱加密算法最高級(jí)
ECC-Elliptic Curves Cryptography,橢圓曲線密碼編碼學(xué),是目前已知的公鑰體制中,對(duì)每比特所提供加密強(qiáng)度最高的一種體制。在軟件注冊(cè)保護(hù)方面起到很大的作用,一般的序列號(hào)通常由該算法產(chǎn)生。
ECC算法在jdk1.5后加入支持,目前僅僅只能完成密鑰的生成與解析。

如果想要獲得ECC算法實(shí)現(xiàn),需要調(diào)用硬件完成加密/解密( ECC算法相當(dāng)耗費(fèi)資源 ,如果單純使用CPU進(jìn)行加密/解密,效率低下)

4.2 實(shí)際操作

注意Chipher不支持EC算法 ,以下代碼僅供參考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。為了確保程序能夠正常執(zhí)行,使用了NullCipher類,驗(yàn)證程序
其中的KeyFactory.getInstance("EC", "SunEC");是參考ECKeyFactory源碼

如下代碼用到的常量

    public static final String ALGORITHM = "EC";
    private static final String PUBLIC_KEY = "ECCPublicKey";
    private static final String PRIVATE_KEY = "ECCPrivateKey";
    static BASE64Decoder de = new BASE64Decoder();
    static BASE64Encoder en = new BASE64Encoder();

4.2.1 生成公私鑰并獲取

 /**
     * 初始化密鑰
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {
        BigInteger x1 = new BigInteger("2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16);
        BigInteger x2 = new BigInteger("289070fb05d38ff58321f2e800536d538ccdaa3d9", 16);

        ECPoint g = new ECPoint(x1, x2);
        //   the   order   of   generator
        BigInteger n = new BigInteger("5846006549323611672814741753598448348329118574063", 10);
        //   the   cofactor
        int h = 2;
        int m = 163;
        int[] ks = {7, 6, 3};
        ECFieldF2m ecField = new ECFieldF2m(m, ks);
        //   y^2+xy=x^3+x^2+1
        BigInteger a = new BigInteger("1", 2);
        BigInteger b = new BigInteger("1", 2);

        EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);

        ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g, n, h);
        //   公鑰
        ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);

        BigInteger s = new BigInteger("1234006549323611672814741753598448348329118574063", 10);
        //   私鑰
        ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);

        return keyMap;
    }

 /**
     * 取得私鑰
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return en.encode(key.getEncoded());
    }

    /**
     * 取得公鑰
     */
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return en.encode(key.getEncoded());
    }

4.2.2 加密解密

    /**
     * 解密<br>
     * 用私鑰解密
     */
    public static String decrypt(byte[] data, String key) throws Exception {
        //   對(duì)密鑰解密
        byte[] keyBytes = de.decodeBuffer(key);
        //   取得私鑰
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "SunEC");
        ECPrivateKey priKey = (ECPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);

        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(), priKey.getParams());

        //   對(duì)數(shù)據(jù)解密
        //  Chipher不支持EC算法   未能實(shí)現(xiàn)
        Cipher cipher = new NullCipher();
        //   Cipher.getInstance(ALGORITHM,   keyFactory.getProvider());
        cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
        return new String(cipher.doFinal(data));
    }

    /**
     * 加密<br>
     * 用公鑰加密
     */
    public static String encrypt(byte[] data, String privateKey) throws Exception {
        //   對(duì)公鑰解密
        byte[] keyBytes = de.decodeBuffer(privateKey);

        //   取得公鑰
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "SunEC");

        ECPublicKey pubKey = (ECPublicKey) keyFactory.generatePublic(x509KeySpec);

        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(), pubKey.getParams());

        //   對(duì)數(shù)據(jù)加密
        //   TODO   Chipher不支持EC算法   未能實(shí)現(xiàn)
        Cipher cipher = new NullCipher();
        //   Cipher.getInstance(ALGORITHM,   keyFactory.getProvider());
        cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());

        return en.encode(cipher.doFinal(data));
    }

4.2.3 結(jié)果驗(yàn)證

    public static void main(String[] args) throws Exception{
        String inputStr = "abc";
        byte[] data = inputStr.getBytes();

        Map<String, Object> keyMap = initKey();

        String publicKey = getPublicKey(keyMap);
        String privateKey = getPrivateKey(keyMap);
        System.err.println("公鑰:    \r\n" + publicKey);
        System.err.println("私鑰:   \r\n" + privateKey);

        String encodedData = encrypt(data, publicKey);

        String decodedData = decrypt(de.decodeBuffer(encodedData), privateKey);

        System.err.println("加密前:    " + inputStr + "\n\n" + "解密后:    " + decodedData);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容