1. 程式人生 > >RSA非對稱加解密演算法的使用

RSA非對稱加解密演算法的使用

加密金鑰和解密金鑰相同時則稱為對稱加密。由於加密金鑰和解密金鑰相同,它們也被稱為Shared Key。如AES等。

加密金鑰(公鑰)和解密金鑰(私鑰)不相同時則稱為非對稱加密,別稱公鑰密碼。如RSA等。

非對稱加密例子:

假設張三擁有的公鑰Pu和私鑰Pr,其公鑰是公開的,誰想跟張三通訊的話必須用張三的公鑰Pu進行加密後傳輸給張三,張三用自己的私鑰Pr解密後就能檢視通訊內容了。

RSA:建立在分解大數的困難度、公鑰/私鑰長度至少是1024bit。1英文字元=1位元組=8bit位。

對稱加密的優缺點:

1.高效

2.金鑰交換問題

3.不如RSA的加密安全程度高,但是當選擇256bit的AES時,仍然能勝任絕大多數的安全領域

非對稱加密的優缺點:

1.安全性足夠高

2.沒有金鑰交換的問題

3.效率低,對於大資料加密很慢

實際的保密會話應用場景:

1.基於高效的對稱加密演算法對會話進行加密

2.會話金鑰實時產生並週期性變化

3.基於其他足夠安全的方式進行會話金鑰的傳輸和交換

針對上面的第3步,可以利用非對稱加密方法來交換會話金鑰:

1.產生實時隨機的會話金鑰

2.A使用對端B的公鑰對產生的會話金鑰進行加密並傳遞給對端B

3.對端B使用自己的私鑰解密獲取會話金鑰

4.雙方開始基於共享的會話金鑰進行對稱加密的保密會話通訊

點對點通訊使用上面的加解密方法進行通訊,點對多的通訊可以使用下面的方法進行廣播:

5.B向任何人發訊息時,傳送的是使用自己私鑰加密後的內容

6.任何人接收到訊息後使用訊息傳送者B的公鑰進行解密得到B廣播的訊息內容

非對稱加密的兩面性:

A使用B的公鑰加密後,B可以使用自己的私鑰進行解密;

B使用自己的私鑰加密後,A可以使用B的公鑰進行解密。

RSA加密演算法例項:

public class RSA {

    private static RSA mInstance;
    private File mPublicKeyFile;
    private File mPrivateKeyFile;

    public static RSA getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new RSA(context);
        }
        return mInstance;
    }

    public RSA(Context context) {
        String path = context.getFilesDir().getAbsolutePath();
        mPublicKeyFile = createFile(path + "/publickey.dat");
        mPrivateKeyFile = createFile(path + "/privatekey.dat");
        generateKeyPair();
    }

    /**
     * 生成金鑰對
     */
    private void generateKeyPair() {
        try {
            // RSA演算法要求有一個可信任的隨機數源
            SecureRandom sr = new SecureRandom();
            // 為RSA演算法建立一個KeyPairGenerator物件
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            // 初始化金鑰長度
            kpg.initialize(1024, sr);
            // 生成金鑰對
            KeyPair genKeyPair = kpg.genKeyPair();
            // 獲取公鑰
            PublicKey publicKey = genKeyPair.getPublic();
            // 儲存公鑰
            FileOutputStream fos = new FileOutputStream(mPublicKeyFile);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(publicKey);
            oos.close();
            // 獲取私鑰
            PrivateKey privateKey = genKeyPair.getPrivate();
            // 儲存私鑰
            FileOutputStream fos1 = new FileOutputStream(mPrivateKeyFile);
            ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
            oos1.writeObject(privateKey);
            oos1.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 公鑰加解密
     * isEncrypt: true是利用公鑰加密,false是利用公鑰解密
     */
    public byte[] encrypt(byte[] source, boolean isEncrypt) {
        byte[] result = null;
        try {
            // 獲取公鑰
            FileInputStream fis = new FileInputStream(mPublicKeyFile);
            ObjectInputStream ois = new ObjectInputStream(fis);
            RSAPublicKey publicKey = (RSAPublicKey) ois.readObject();
            ois.close();
            Cipher cipher = Cipher.getInstance("RSA");
            if (isEncrypt) {
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, publicKey);
            }
            
            if (source != null) {
                // 加密
                result = cipher.doFinal(source);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 私鑰加解密
     * isDecrypt: true是利用私鑰解密,false是利用私鑰加密
     */
    public byte[] decrypt(byte[] source, boolean isDecrypt) {
        byte[] result = null;
        try {
            // 讀取私鑰
            FileInputStream fis = new FileInputStream(mPrivateKeyFile);
            ObjectInputStream ois = new ObjectInputStream(fis);
            RSAPrivateKey privateKey = (RSAPrivateKey) ois.readObject();
            ois.close();
            Cipher cipher = Cipher.getInstance("RSA");
            if (isDecrypt) {
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            }
            if (source != null) {
                // 解密
                result = cipher.doFinal(source);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private File createFile(String path) {
        File file = new File(path);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return file;
    }

}

呼叫:
try {
    String source = "哈哈嘻嘻的假短髮接歐文多拉點開機費的";
    byte[] encrypt = RSA.getInstance(this).encrypt(source.getBytes("UTF-8"), true);
    String result = new String(RSA.getInstance(this).decrypt(encrypt, true), "UTF-8");
    System.out.println("zyf 使用公鑰加密,私鑰解密的結果: " + result);
    
    byte[] decrypt = RSA.getInstance(this).decrypt(source.getBytes("UTF-8"), false);
    result = new String(RSA.getInstance(this).encrypt(decrypt, false), "UTF-8");
    System.out.println("zyf 使用私鑰加密,公鑰解密的結果: " + result);
} catch (Exception e) {
    e.printStackTrace();
}

RSA的低效率特性導致即便是簽名也不適合直接對原始資訊進行簽名,可以:

1.先計算出原始訊息的MD5值

2.對MD5值進行基於非對稱加密演算法的簽名

3.簽名一般附著於原始訊息尾部或頭部一起傳送

收到訊息後,先用非對稱加密演算法對簽名進行解密,解密後的MD5值與原始訊息的MD5值進行比對,一致則認為訊息沒有被篡改。