1. 程式人生 > >Java加密與解密筆記(三) 非對稱加密

Java加密與解密筆記(三) 非對稱加密

arr 內容 phy 資料 密碼 load esp uid user

非對稱的特點是加密和解密時使用的是不同的鑰匙。密鑰分為公鑰和私鑰,用公鑰加密的數據只能用私鑰進行解密,反之亦然。

另外,密鑰還可以用於數字簽名。數字簽名跟上文說的消息摘要是一個道理,通過一定方法對數據內容進行處理得到一個簽名,查看這個簽名是否與對方傳遞的簽名一致。

在非對稱加密中用密鑰來指公鑰和私鑰。

RSA

RAS是最早的非對稱簽名,是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。1987年7月首次在美國公布,當時他們三人都在麻省理工學院工作實習。RSA就是他們三人姓氏開頭字母拼在一起組成的。

對於非對稱加密,在Java中可以用KeyPairGenerator工具類來負責生成密鑰對:

public class RSAUtil {
    
    public final static String ALGORITHM = "RSA";

    public static KeyPair getKey() throws Exception{
        KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
        return generator.generateKeyPair();
    }
    
    
public static void main(String[] args) throws Exception{ KeyPair keyPair = getKey(); RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate(); RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic(); String privateKeyStr = Base64.encode(privateKey.getEncoded()); String publicKeyStr
= Base64.encode(publicKey.getEncoded()); System.out.println("私鑰:" + privateKeyStr); System.out.println("公鑰:" + publicKeyStr); } }

使用公鑰加密:

    public static String encryptByPublicKey(String data,String key)throws Exception{
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        
        Key k = keyFactory.generatePublic(keySpec);
        
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
        
        return Base64Util.encode(bytes);
    } 

加密的時候用X509EncodedKeySpec來獲取公鑰,不要害怕X509,其實沒有別的X508或者X609,就這麽一個X509。X.509是一種非常通用的證書格式。所有的證書都符合ITU-T X.509國際標準。

和之前的DES類似,使用私鑰解密的代碼如下:

    public static String decryptByPrivateKey(String data,String key)throws Exception{
        
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        
        Key k = keyFactory.generatePrivate(keySpec);
        
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(Base64Util.decode(data));
        
        return new String(bytes,"UTF-8");
    } 

還用X509去讀私鑰就不行了,會報下面這個錯誤:

Exception in thread "main" java.security.spec.InvalidKeySpecException: Only RSAPrivate(Crt)KeySpec and PKCS8EncodedKeySpec supported for RSA private keys

意思是只能用RSAPrivate(Crt)KeySpec 或者 PKCS8EncodedKeySpec去讀私鑰,改成下面這樣就好了:

    public static String decryptByPrivateKey(String data,String key)throws Exception{
        
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        
        Key k = keyFactory.generatePrivate(keySpec);
        
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(Base64Util.decode(data));
        
        return new String(bytes,"UTF-8");
    }

使用私鑰加密和公鑰解密的方法就不用多說了,只需要在加密和解密時換成另外一個鑰匙就行了。

簽名和簽名驗證

/**
     * 使用私鑰進行簽名
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String sign(String data,String key)throws Exception{
        PrivateKey k = (PrivateKey)getPrivateKey(key);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initSign(k);  
        signature.update(data.getBytes("UTF-8"));  
        return Base64.encode(signature.sign());
    }
    
    /**
     * 使用公鑰進行簽名驗證
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static boolean signVerify(String data,String key,String sign)throws Exception{
        PublicKey k = (PublicKey)getPublicKey(key);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initVerify(k);  
        signature.update(data.getBytes("UTF-8"));
        return signature.verify(Base64.decode(sign));  
    }

JDK中有專門用於簽名的工具類Signature,可用的簽名算法如下:

技術分享

完整代碼重構如下:

public class RSAUtil {
    
    public final static String ALGORITHM = "RSA";
    public final static String SIGNATURE_ALGORITHM = "MD5withRSA";

    /**
     * 獲取公鑰密鑰對
     * @return
     * @throws Exception
     */
    public static KeyPair getKey() throws Exception{
        KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
        return generator.generateKeyPair();
    }
    
    private static Key getPublicKey(String key)throws Exception{
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        Key k = keyFactory.generatePublic(keySpec);
        return k;
    }
    
    private static Key getPrivateKey(String key)throws Exception{
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        Key k = keyFactory.generatePrivate(keySpec);
        return k;
    }
    
    /**
     * 使用公鑰進行加密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data,String key)throws Exception{
        
        Key k = getPublicKey(key);
        
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
        
        return Base64Util.encode(bytes);
    }
    
    /**
     * 使用私鑰進行加密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPrivateKey(String data,String key)throws Exception{
        
        Key k = getPrivateKey(key);
        
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(data.getBytes("UTF-8"));
        
        return Base64Util.encode(bytes);
    }
    
    /**
     * 使用密鑰進行解密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String decryptByPrivateKey(String data,String key)throws Exception{
        Key k = getPrivateKey(key);
        
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(Base64Util.decode(data));
        
        return new String(bytes,"UTF-8");
    }
    
    /**
     * 使用公鑰進行解密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String decryptByPublicKey(String data,String key)throws Exception{
        Key k = getPublicKey(key);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, k);
        
        byte[] bytes = cipher.doFinal(Base64Util.decode(data));
        
        return new String(bytes,"UTF-8");
    }
    
    /**
     * 使用私鑰進行簽名
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String sign(String data,String key)throws Exception{
        PrivateKey k = (PrivateKey)getPrivateKey(key);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initSign(k);  
        signature.update(data.getBytes("UTF-8"));  
        return Base64.encode(signature.sign());
    }
    
    /**
     * 使用公鑰進行簽名驗證
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static boolean signVerify(String data,String key,String sign)throws Exception{
        PublicKey k = (PublicKey)getPublicKey(key);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);  
        signature.initVerify(k);  
        signature.update(data.getBytes("UTF-8"));
        return signature.verify(Base64.decode(sign));  
    }
    
    public static void main(String[] args) throws Exception{
        KeyPair keyPair = getKey();
        RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
        RSAPublicKey  publicKey = (RSAPublicKey)keyPair.getPublic();
        
        String privateKeyStr = Base64.encode(privateKey.getEncoded());
        String publicKeyStr = Base64.encode(publicKey.getEncoded());
        
        System.out.println("私鑰:" + privateKeyStr);
        System.out.println("公鑰:" + publicKeyStr);
        
        String data = "Hello,RSA,Hello,RSAHello,RSAHello,RSAHello,RSAHello,RSAHello,RSA";
        System.out.println("---------------公鑰加密,私鑰解密-----------------");
        String encryptedData = encryptByPublicKey(data,publicKeyStr);
        System.out.println("加密後:" + encryptedData);
        
        String decryptedData = decryptByPrivateKey(encryptedData, privateKeyStr);
        System.out.println("解密後:" + decryptedData);
        System.out.println("---------------私鑰加密,公鑰解密-----------------");
        
        encryptedData = encryptByPrivateKey(data,privateKeyStr);
        System.out.println("加密後:" + encryptedData);
        decryptedData = decryptByPublicKey(encryptedData, publicKeyStr);
        System.out.println("解密後:" + decryptedData);
        
        String sign = sign(data,privateKeyStr);
        System.out.println("簽名:" + sign);
        System.out.println("簽名驗證:" + signVerify(data,publicKeyStr,sign));
        
        
    }
    
}

DH(Diffie-Hellman)

非對稱加密的算法比較耗時,所以不能用它來傳輸大數據。通常情況下會是這樣:

  1. 因為對稱加密算法中沒法安全傳遞密鑰,所以用非對稱加密算法來傳遞對稱加密的密鑰;
  2. 等對稱加密的秘鑰傳遞成功之後,正式的數據就用對稱加密算法來傳遞了。

DH算法就是為了實現這個目的而產生的。DH能實現甲乙雙方的密鑰溝通。

假設客戶端要發送數據到服務端,在Java中DH加密的完整步驟:

服務端先生成自己的密鑰對:

/**
 * 數據處理服務端*/
public class Server {

    private String publicKey;
    private String privateKey;
    private SecretKey key;
    
    public Server(){
        try {
            String[] keyPair = DHUtil.getStringKeyPair();
            publicKey = keyPair[0];
            privateKey = keyPair[1];
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
   ...

服務端的私鑰自己保持,公布公鑰,客戶端則需要根據服務端的公鑰生成自己的密鑰對:

/**
 * 數據傳輸客戶端*/
public class Client {
    
    private String publicKey;
    private String privateKey;private Server server;
    
    public Client(Server server){
        this.server = server;
        String serverPublicKey = server.getPublicKey();//明文獲取到公鑰
        try{
            String[] keyPair = DHUtil.getStringKeyPair(serverPublicKey);
            publicKey = keyPair[0];
            privateKey = keyPair[1];
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    ...

客戶端在和服務端通信時,使用的加密算法是對稱加密。對稱加密的密鑰是根據服務端的公鑰和客戶端的私鑰生成的。

public class Client {
    
    private String publicKey;
    private String privateKey;
    private SecretKey key;
    
    private Server server;
    
    public Client(Server server){
        this.server = server;
        String serverPublicKey = server.getPublicKey();
        try{
            String[] keyPair = DHUtil.getStringKeyPair(serverPublicKey);
            publicKey = keyPair[0];
            privateKey = keyPair[1];
            key = DHUtil.getAgreementSecretKey(serverPublicKey, privateKey);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
...

好了,現在可以往服務端發送數據了,比如有一個登錄操作:

public class Client {
    ...
    public boolean login(String user,String pwd){
        String data = "user=" + user + "&pwd=" + pwd;
        try {
            data = DHUtil.encrypt(data, key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String response = server.service(data,publicKey);
        System.out.println("Login Response:" + response);
        return response.equals("OK");
    }
}

可見,公鑰是通過明文的形式發送給服務端的。服務端對數據進行處理:

/**
 * 數據處理服務端
 * @author huqiao
 */
public class Server {

    ...

    public String service(String data,String clientPublicKey){try {
            key = DHUtil.getAgreementSecretKey(clientPublicKey, privateKey);//根據客戶端的publicKey生成本地密鑰
            String decryptedData = DHUtil.decrypt(data, key);
            System.out.println("Data decryped:" + decryptedData);
            if(verfiy(decryptedData)){
                return "OK";
            }else{
                return "Error";
                
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }
    ... 

服務器拿到客戶端的公鑰之後生成本地的密鑰,然後對數據進行解密。為了簡單起見,這裏服務器往客戶度返回數據時沒有做加密。

非常重要的DHUtil.java完整代碼:

public class DHUtil {
    
    public final static String ALGORITHM = "DH";
    public final static String SYMMETRIC_SECRET_ALGORITHM = "AES";//對稱加密算法名稱

    /**
     * 產生密鑰對
     * @return
     * @throws Exception
     */
    public static KeyPair getKeyPair()throws Exception{
        KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
        generator.initialize(1024);
        return generator.generateKeyPair();
    }
    
    /**
     * 獲取字符串類型的密鑰對
     * @return
     * @throws Exception
     */
    public static String[] getStringKeyPair()throws Exception{
        KeyPair keyPair = getKeyPair();
        return keyPairToStringArray(keyPair);
    }
    
    public static String[] getStringKeyPair(String publicKey)throws Exception{
        KeyPair keyPair = getKeyPairByPublicKey(publicKey);
        return keyPairToStringArray(keyPair);
    }
    
    private static String[] keyPairToStringArray(KeyPair keyPair){
        String[] res = new String[2];
        PublicKey pubKey = keyPair.getPublic(); 
        PrivateKey priKey = keyPair.getPrivate();
        
        res[0] = Base64Util.encode(pubKey.getEncoded());
        res[1] = Base64Util.encode(priKey.getEncoded());
        
        return res;
    }
    
    
    /**
     * 由一個公鑰產生密鑰對
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static KeyPair getKeyPairByPublicKey(String publicKey)throws Exception{
        
        PublicKey pKey = getPublicKey(publicKey);
        
        KeyPairGenerator generator = KeyPairGenerator.getInstance(pKey.getAlgorithm());
        
        DHParameterSpec dhGenParam = ((DHPublicKey) pKey).getParams();
        generator.initialize(dhGenParam);
        
        return generator.generateKeyPair();
    }
    
    private static PublicKey getPublicKey(String key)throws Exception{
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey k = keyFactory.generatePublic(keySpec);
        return k;
    }
    
    private static PrivateKey getPrivateKey(String key)throws Exception{
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey k = keyFactory.generatePrivate(keySpec);
        return k;
    }
    
    /**
     * 根據一方公鑰和另外一方私鑰構建本地密鑰
     * @param publicKey
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static SecretKey getAgreementSecretKey(String publicKey,String privateKey)throws Exception{
        PublicKey pubKey = getPublicKey(publicKey);
        PrivateKey priKey = getPrivateKey(privateKey);
        
        return getAgreementSecretKey(pubKey,priKey);
        
    }
    
    public static SecretKey getAgreementSecretKey(PublicKey pubKey,PrivateKey priKey)throws Exception{
        KeyAgreement argeement = KeyAgreement.getInstance(pubKey.getAlgorithm());
        argeement.init(priKey);
        argeement.doPhase(pubKey, true);
        
        SecretKey secretKey = argeement.generateSecret(SYMMETRIC_SECRET_ALGORITHM);
        return secretKey;
    }
    
    /**
     * 加密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String encrypt(String data,SecretKey key)throws Exception{
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] encryptedData = cipher.doFinal(data.getBytes("UTF-8"));
        return Base64Util.encode(encryptedData);
    }
    
    /**
     * 解密
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static String decrypt(String data,SecretKey key)throws Exception{
        byte[] encryptedData = Base64Util.decode(data);
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] decryptedData = cipher.doFinal(encryptedData);
        return new String(decryptedData,"UTF-8");
    }
} 

不要被它的長度嚇到,它做的事情其實很簡單,就是生成密鑰對和加密解密。生成密鑰對,加密和解密在之前都見過,這裏最重要的方法是使用來自兩方的公鑰和私鑰生成一個本地密鑰:

    /**
     * 根據一方公鑰和另外一方私鑰構建本地密鑰
     * @param publicKey
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static SecretKey getAgreementSecretKey(String publicKey,String privateKey)throws Exception{
        PublicKey pubKey = getPublicKey(publicKey);
        PrivateKey priKey = getPrivateKey(privateKey);
        
        return getAgreementSecretKey(pubKey,priKey);
        
    }
    
    public static SecretKey getAgreementSecretKey(PublicKey pubKey,PrivateKey priKey)throws Exception{
        KeyAgreement argeement = KeyAgreement.getInstance(pubKey.getAlgorithm());
        argeement.init(priKey);
        argeement.doPhase(pubKey, true);
        
        SecretKey secretKey = argeement.generateSecret(SYMMETRIC_SECRET_ALGORITHM);
        return secretKey;
    } 

完整客戶端和服務端代碼如下:

/**
 * 數據傳輸客戶端
 * @author huqiao
 */
public class Client {
    
    private String publicKey;
    private String privateKey;
    private SecretKey key;
    
    private Server server;
    
    public Client(Server server){
        this.server = server;
        String serverPublicKey = server.getPublicKey();
        try{
            String[] keyPair = DHUtil.getStringKeyPair(serverPublicKey);
            publicKey = keyPair[0];
            privateKey = keyPair[1];
            key = DHUtil.getAgreementSecretKey(serverPublicKey, privateKey);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    public boolean login(String user,String pwd){
        String data = "user=" + user + "&pwd=" + pwd;
        try {
            data = DHUtil.encrypt(data, key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String response = server.service(data,publicKey);
        System.out.println("Login Response:" + response);
        return response.equals("OK");
    }
}

/**
 * 數據處理服務端
 * @author huqiao
 */
public class Server {

    private String publicKey;
    private String privateKey;
    private SecretKey key;
    
    public Server(){
        try {
            String[] keyPair = DHUtil.getStringKeyPair();
            publicKey = keyPair[0];
            privateKey = keyPair[1];
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    public String service(String data,String clientPublicKey){
        System.out.println("----------------Data received at Server:----------------\r\n"+ data);
        System.out.println("----------------Client PublicKey received at Server:----------------\r\n"+clientPublicKey);
        try {
            key = DHUtil.getAgreementSecretKey(clientPublicKey, privateKey);
            String decryptedData = DHUtil.decrypt(data, key);
            System.out.println("Data decryped:" + decryptedData);
            if(verfiy(decryptedData)){
                return "OK";
            }else{
                return "Error";
                
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }
    
    private boolean verfiy(String decryptedData) {
        //解析用戶名和密碼,進行驗證
        return true;
    }

    /**
     * 明文拿到服務端公鑰
     * @return
     */
    public String getPublicKey(){
        return publicKey;
    }
}

測試:

public class DHTest {
    
    public static void main(String[] args) {
        Server server = new Server();
        
        Client client = new Client(server);
        boolean loginSuccess = client.login("admin", "123456");
        
        System.out.println("login success:" + loginSuccess);
    }
}

測試結果:

----------------Data received at Server:----------------
pVWbVMP57wkLftZN3bXx1mf4631yTMlxJ+hnMm4Dwmg=
----------------Client PublicKey received at Server:----------------
MIIBpzCCARsGCSqGSIb3DQEDATCCAQwCgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/gLZR
JmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7
V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZndFIAccCgYEA9+GghdabPd7LvKtc
NrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotU
fI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7P
SSoCAgIAA4GFAAKBgQC+WT4qNq/Yay1WFA89n5IOy+hJa8JQh4R0uyy5Yfo2ckgQ4cjh/u5GPKev
Ua2B3vQVFEifKSn7tfP5bmYMQ5IZLPJ3JrP2m/QAjQ1T7swG/Kbtfc4eTgq+wpnb2LbDoznKGN28
Mcrbf4HkwZ8QK0M26CySSEQCFliWydd6u/vl0A==
Data decryped:user=admin&pwd=123456
Login Response:OK
login success:true

DSA

DSA的全稱為數字簽名算法(Digital Signature Algorithm),它與RSA的區別在於它只用於簽名,並且它的速度比RSA要快。在安全性上兩者差不多。

因為在RSA中已經說到過簽名以及驗證的過程,DSA和它完全類似:

public class DSAUtil {

    static final String ALGORITHM = "DSA";
    
    private static KeyPair getKeyPair()throws Exception{
        KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
        generator.initialize(1024);
        return generator.genKeyPair();
    }
    

    
    /**
     * 用私鑰簽名
     * @param data
     * @param privateKey
     * @return
     */
    public static String sign(String data,String privateKey)throws Exception{
        PrivateKey priKey = getPrivateKey(privateKey);
        Signature sign = Signature.getInstance(ALGORITHM);
        sign.initSign(priKey);
        sign.update(data.getBytes("UTF-8"));
        byte[] signBytes = sign.sign();
        return Base64Util.encode(signBytes);
    }
    
    /**
     * 用公鑰進行簽名驗證
     * @param data
     * @param publicKey
     * @param signData
     * @return
     * @throws Exception
     */
    public static boolean verify(String data,String publicKey,String signData)throws Exception{
        PublicKey pubKey = getPublicKey(publicKey);
        Signature sign = Signature.getInstance(ALGORITHM);
        sign.initVerify(pubKey);
        sign.update(data.getBytes("UTF-8"));
        return sign.verify(Base64Util.decode(signData));
    }
    
    
    private static PublicKey getPublicKey(String key)throws Exception{
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey k = keyFactory.generatePublic(keySpec);
        return k;
    }
    
    private static PrivateKey getPrivateKey(String key)throws Exception{
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Util.decode(key));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey k = keyFactory.generatePrivate(keySpec);
        return k;
    }
    
    /**
     * 獲取字符串類型的密鑰對
     * @return
     * @throws Exception
     */
    public static String[] getStringKeyPair()throws Exception{
        KeyPair keyPair = getKeyPair();
        return keyPairToStringArray(keyPair);
    }
    
    private static String[] keyPairToStringArray(KeyPair keyPair){
        String[] res = new String[2];
        PublicKey pubKey = keyPair.getPublic(); 
        PrivateKey priKey = keyPair.getPrivate();
        
        res[0] = Base64Util.encode(pubKey.getEncoded());
        res[1] = Base64Util.encode(priKey.getEncoded());
        
        return res;
    }
    
    public static void main(String[] args) throws Exception{
        String data = "Hello,DSA";
        String[] keyPair = getStringKeyPair();
        String pubKey = keyPair[0];
        String priKey = keyPair[1];
        System.out.println("原文:" + data);
        System.out.println("---------Public Key----------");
        System.out.println(pubKey);
        System.out.println("---------Private Key----------");
        System.out.println(priKey);
        System.out.println();
        
        String signData = sign(data, priKey);
        System.out.println("Sign Data:" + signData);
        System.out.println("Verify Result:" + verify(data, pubKey, signData));
        
    }
}

ECC

ECC-Elliptic Curves Cryptography,橢圓曲線密碼編碼學,是目前已知的公鑰體制中,對每比特所提供加密強度最高的一種體制。ECC算法相當耗費資源,如果單純使用CPU進行加密/解密,效率低下。目前JDK9都沒有實現ECC的加密解密,僅僅提供ECC的秘鑰生成。

可以自己去實現ECC的Provider。因為Java的安全框架(JCA)提供了密鑰生成的擴展機制(JCE)。參考這裏如何實現一個Provider:

How to Implement a Provider in the Java Cryptography Architecture


參考資料:

http://snowolf.iteye.com/blog/381767

http://snowolf.iteye.com/blog/382422

http://snowolf.iteye.com/blog/382749

Java加密與解密筆記(三) 非對稱加密