天天看點

Android安全-對稱加密和非對稱加密

秘鑰:分為加密秘鑰和解密秘鑰

明文:沒有進行加密,能夠直接代表原文含義的資訊

密文:經過加密處理之後,隐藏原文含義的資訊

加密:将明文轉換為密文的過程

解密:将密文轉換為明文的過程

Android安全-對稱加密和非對稱加密

對稱加密

DES

  1977年1月,美國政府頒布:采納IBM公司設計的方案作為非機密資料的正式資料加密标準(DESData Encryption Standard) 。

  目前在國内,随着三金工程尤其是金卡工程的啟動,DES算法在POS、ATM、磁卡及智能卡(IC卡)、加油站、高速公路收費站等領域被廣泛應用,以此來實作關鍵資料的保密,如信用卡持卡人的PIN的加密傳輸,IC卡與POS間的雙向認證、金融交易資料包的MAC校驗等,均用到DES算法。

       DES算法的入口參數有三個:Key、Data、Mode。其中Key為8個位元組共64位,是DES算法的工作密鑰;Data也為8個位元組64位,是要被加密或被解密的資料;Mode為DES的工作方式,有兩種:加密或解密。

  DES算法是這樣工作的:

  如Mode為加密,則用Key去把資料Data進行加密, 生成Data的密碼形式(64位)作為DES的輸出結果;

  如Mode為解密,則用Key去把密碼形式的資料Data解密,還原為Data的明碼形式(64位)作為DES的輸出結果。

  在通信網絡的兩端,雙方約定一緻的Key,在通信的源點用Key對核心資料進行DES加密,然後以密碼形式在公共通信網(如電話網)中傳輸到通信網絡的終點,資料到達目的地後,用同樣的Key對密碼資料進行解密,便再現了明碼形式的核心資料。這樣,便保證了核心資料(如PIN、MAC等)在公共通信網中傳輸的安全性和可靠性。

  通過定期在通信網絡的源端和目的端同時改用新的Key,便能更進一步提高資料的保密性,這正是現在金融交易網絡的流行做法。

3DES

  3DES是DES加密算法的一種模式,它使用3條64位的密鑰對資料進行三次加密。資料加密标準(DES)是美國的一種由來已久的加密标準,它使用對稱密鑰加密法。

  3DES(即Triple DES)是DES向AES過渡的加密算法(1999年,NIST将3-DES指定為過渡的加密标準),是DES的一個更安全的變形。它以DES為基本子產品,通過組合分組方法設計出分組加密算法。

  設Ek()和Dk()代表DES算法的加密和解密過程,K代表DES算法使用的密鑰,P代表明文,C代表密表,這樣,

  3DES加密過程為:C=Ek3(Dk2(Ek1(P)))

  3DES解密過程為:P=Dk1((EK2(Dk3(C)))

  K1、K2、K3決定了算法的安全性,若三個密鑰互不相同,本質上就相當于用一個長為168位的密鑰進行加密。多年來,它在對付強力攻擊時是比較安全的。若資料對安全性要求不那麼高,K1可以等于K3。在這種情況下,密鑰的有效長度為112位。

 AES

  AES(Advanced Encryption Standard):進階加密标準,是下一代的加密算法标準,速度快,安全級别高。

  用AES加密2000年10月,NIST(美國國家标準和技術協會)宣布通過從15種候選算法中選出的一項新的密匙加密标準。Rijndael被選中成為将來的 AES。Rijndael是在1999年下半年,由研究員Joan Daemen 和 Vincent Rijmen 建立的。AES正日益成為加密各種形式的電子資料的實際标準。

  美國标準與技術研究院(NIST)于2002年5月26日制定了新的進階加密标準(AES)規範。

  AES算法基于排列和置換運算。排列是對資料重新進行安排,置換是将一個資料單元替換為另一個。

  AES使用幾種不同的方法來執行排列和置換運算。AES是一個疊代的、對稱密鑰分組的密碼,它可以使用128、192和256位密鑰,并且用128位(16位元組)分組加密和解密資料。

  與公共密鑰加密使用密鑰對不同,對稱密鑰密碼使用相同的密鑰加密和解密資料。通過分組密碼傳回的加密資料的位數與輸入資料相同。疊代加密使用一個循環結構,在該循環中重複置換和替換輸入資料。

調用AES/DES加密算法包最精要的就是下面兩句話:

Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
           

CBC是工作模式,DES一共有電子密碼本模式(ECB)、加密分組連結模式(CBC)、加密回報模式(CFB)和輸出回報模式(OFB)四種模式,

PKCS5Padding是填充模式,還有其它的填充模式

然後,cipher.init()一共有三個參數:Cipher.ENCRYPT_MODE, key, zeroIv,zeroIv就是初始化向量。

工作模式、填充模式、初始化向量這三種因素一個都不能少。

1、加密

public static String encrypt(String text) throws Exception {
		// 私鑰 AES固定格式為128/192/256bits.即:16/24/32bytes。DES固定格式為128bits,即8bytes。
		String key = "aaaaaaaaaaaaaaaa";
		// 初始化向量參數,AES 為16bytes. DES 為8bytes
		String iv ="bbbbbbbbbbbbbbbb"; 
		
		// 兩個參數,第一個為私鑰位元組數組, 第二個為加密方式AES或者DES
		Key keySpec = new SecretKeySpec(key.getBytes(), "AES"); 

		IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
		// 執行個體化加密類,參數為加密方式,要寫全
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		// 初始化,此方法可以采用三種方式,按伺服器要求來添加。(1)無第三個參數(2)第三個參數為SecureRandom
		//(3)采用此代碼中的IVParameterSpec
		cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
              
                // random = new SecureRandom();中random對象,随機數。(AES不可采用這種方法)
		// cipher.init(Cipher.ENCRYPT_MODE, keySpec);
		// SecureRandom random = new SecureRandom();
		// cipher.init(Cipher.ENCRYPT_MODE, keySpec, random);
		byte[] bytes = cipher.doFinal(text.getBytes());// 加密操作,傳回加密後的位元組數組,然後需要編碼。主要編解碼方式有Base64, HEX, UUE,
		// 7bit等等。此處看伺服器需要什麼編碼方式
		String result = Base64.encodeToString(bytes, Base64.DEFAULT);
		return result;

	}
           

2、解密

public static String decrypt(String text) throws Exception {

		String keySpec = "aaaaaaaaaaaaaaaa";
		String iv = "bbbbbbbbbbbbbbbb";
		byte[] textBytes = Base64.decode(text.getBytes(), Base64.DEFAULT);

		IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());

		Key key = new SecretKeySpec(keySpec.getBytes(), "AES");

		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

		cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); // 與加密時不同MODE:Cipher.DECRYPT_MODE

		String result = cipher.doFinal(textBytes);

		return result;

}
           

非對稱加密

非對稱加密。 密鑰不是一個,而是一對,A和B。使用A加密的密文隻能使用B來解密。使用B加密的密文隻能使用A來解密。選擇其中的一個作為公鑰,另一個作為私鑰,可以用于許多非對稱加密應用場合。比如電子證書、電子指紋、電子簽名、握手協定等傳統對稱加密手段無法實作的功能。

其核心是基于“大數的分解質因數非常困難”這個數學難題。使得在得知A的人無法推算出B,B也無法推算出A。

    RSA公鑰加密算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。

    RSA是目前最有影響力的公鑰加密算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰資料加密算法。

  流程分析: 

  • 甲方建構密鑰對兒,将公鑰公布給乙方,将私鑰保留。
  • 甲方使用私鑰加密資料,然後用私鑰對加密後的資料簽名,發送給乙方簽名以及加密後的資料;乙方使用公鑰、簽名來驗證待解密資料是否有效,如果有效使用公鑰對資料解密。
  • 乙方使用公鑰加密資料,向甲方發送經過加密後的資料;甲方獲得加密資料,通過私鑰解密。

  RSA加密解密的實作,需要有一對公私密鑰,公私密鑰的生成如下:

/**
	 * 初始化密鑰
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> initKey() throws Exception {
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
		keyPairGenerator.initialize(512);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		// 公鑰
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		// 私鑰
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put("PublicKey", publicKey);
		keyMap.put("PrivateKey", privateKey);

		return keyMap;
	}
           

  從代碼中可以看出密鑰的初始化長度為512位(密鑰最短為512bit),密鑰的長度越長,安全性就越好,但是加密解密所用的時間就會越多。而一次能加密的密文長度也與密鑰的長度成正比。一次能加密的密文長度為:密鑰的長度/8-11。是以1024bit長度的密鑰一次可以加密的密文為1024/8-11=117bit。是以非對稱加密一般都用于加密對稱加密算法的密鑰,而不是直接加密内容。對于小檔案可以使用RSA加密,但加密過程仍可能會使用分段加密。

  從map中擷取公鑰、私鑰

/**
	 * 取得公鑰,并轉化為String類型
	 * 
	 * @param keyMap
	 * @return
	 * @throws Exception
	 */
	public static String getPublicKey(Map<String, Object> keyMap)
			throws Exception {
		Key key = (Key) keyMap.get("PublicKey");
		return Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
	}

	/**
	 * 取得私鑰,并轉化為String類型
	 * 
	 * @param keyMap
	 * @return
	 * @throws Exception
	 */
	public static String getPrivateKey(Map<String, Object> keyMap)
			throws Exception {
		Key key = (Key) keyMap.get("PrivateKey");
		return Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
	}
           

 對于RSA産生的公鑰、私鑰,我們可以有兩種方式可以對資訊進行加密解密。私鑰加密-公鑰解密 和 公鑰加密-私鑰解密。

  • 私鑰加密
/**
	 * 用私鑰加密
	 * 
	 * @param data
	 *            加密資料
	 * @param key
	 *            密鑰
	 * @return
	 * @throws Exception
	 */
	public static String encryptByPrivateKey(String data, String key)
			throws Exception {
		// 解密密鑰
		byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT);
		// 取私鑰
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
				keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

		// 對資料加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		byte[] resultBytes=cipher.doFinal(data.getBytes("UTF-8"));
		return Base64.encodeToString(resultBytes, Base64.DEFAULT);
	}
           
  • 私鑰解密
/**
	 * 用私鑰解密
	 * 
	 * @param data
	 *            加密資料
	 * 
	 * @param key
	 *            密鑰
	 * @return
	 * @throws Exception
	 */
	public static String decryptByPrivateKey(String data, String key)
			throws Exception {
		// 對私鑰解密
		byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT);

		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
				keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		// 對資料解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		byte[] dataBytes=Base64.decode(data.getBytes(), Base64.DEFAULT);
		byte[] resultBytes=cipher.doFinal(dataBytes);
		return new String(resultBytes,"UTF-8");
	}
           
  • 公鑰加密
/**
     * 用公鑰加密
     * @param data   加密資料
     * @param key    密鑰
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data,String key)throws Exception{
        //對公鑰解密
        byte[] keyBytes =  Base64.decode(key.getBytes(), Base64.DEFAULT);
        //取公鑰
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
         
        //對資料解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] resultBytes=cipher.doFinal(data.getBytes());
	return Base64.encodeToString(resultBytes, Base64.DEFAULT);
    }
           
  • 公鑰解密
/**
     * 用公鑰解密
     * @param data   加密資料
     * @param key    密鑰
     * @return
     * @throws Exception
     */
    public static String decryptByPublicKey(String data,String key)throws Exception{
        //對私鑰解密
        byte[] keyBytes =  Base64.decode(key.getBytes(),Base64.DEFAULT);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
         
        //對資料解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] dataBytes=Base64.decode(data.getBytes(), Base64.DEFAULT);
        byte[] resultBytes=cipher.doFinal(dataBytes);
        return new String(resultBytes,"UTF-8");
    }
           

  關于數字簽名,先了解下何為數字簽名。數字簽名,就是隻有資訊的發送者才能産生的别人無法僞造的一段數字串,這段數字串同時也是對資訊的發送者發送資訊真實性的一個有效證明。數字簽名是非對稱密鑰加密技術與數字摘要技術的應用。簡單地說,所謂數字簽名就是附加在資料單元上的一些資料,或是對資料單元所作的密碼變換。這種資料或變換允許資料單元的接收者用以确認資料單元的來源和資料單元的完整性并保護資料,防止被人(例如接收者)進行僞造。

    數字簽名的主要功能如下:

    保證資訊傳輸的完整性、發送者的身份認證、防止交易中的抵賴發生。

    數字簽名技術是将摘要資訊用發送者的私鑰加密,與原文一起傳送給接收者。接收者隻有用發送者的公鑰才能解密被加密的摘要資訊,然後用對收到的原文産生一個摘要資訊,與解密的摘要資訊對比。如果相同,則說明收到的資訊是完整的,在傳輸過程中沒有被修改,否則說明資訊被修改過,是以數字簽名能夠驗證資訊的完整性。

    數字簽名是個加密的過程,數字簽名驗證是個解密的過程.

     數字簽名算法依靠公鑰加密技術來實作的。在公鑰加密技術裡,每一個使用者有一對密鑰:一把公鑰和一把私鑰。公鑰可以自由釋出,但私鑰則秘密儲存;還有一個要求就是要讓通過公鑰推算出私鑰的做法不可能實作。

  • 私鑰簽名
/**
	 * 用私鑰對資訊生成數字簽名
	 * 
	 * @param data
	 *            //加密資料
	 * @param privateKey
	 *            //私鑰
	 * @return
	 * @throws Exception
	 */
	public static String sign(byte[] data, String privateKey) throws Exception {
		// 解密私鑰
		byte[] keyBytes = Base64.decode(privateKey.getBytes(), Base64.DEFAULT);
		// 構造PKCS8EncodedKeySpec對象
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
				keyBytes);
		// 指定加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		// 取私鑰匙對象
		PrivateKey privateKey2 = keyFactory
				.generatePrivate(pkcs8EncodedKeySpec);
		// 用私鑰對資訊生成數字簽名
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateKey2);
		signature.update(data);
		return Base64.encodeToString(signature.sign(), Base64.DEFAULT);
	}
           
  • 公鑰校驗
/**
     * 校驗數字簽名
     * @param data   加密資料
     * @param publicKey  公鑰
     * @param sign   數字簽名
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data,String publicKey,String sign)throws Exception{
        //解密公鑰
        byte[] keyBytes = Base64.decode(publicKey.getBytes(), Base64.DEFAULT);
        //構造X509EncodedKeySpec對象
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        //取公鑰匙對象
        PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
         
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey2);
        signature.update(data);
        //驗證簽名是否正常
        return signature.verify(Base64.decode(sign,Base64.DEFAULT));
         
    }
           

優缺點

  • aes/des加密速度快,适合大量資料,des容易破解,一般用3重des,後來又出現了更快更安全的aes
  • rsa是公鑰加密,速度慢,隻能處理少量資料,優點是公鑰即使在不安全的網絡上公開,也能保證安全
  • 常見情況是雙方用rsa協商出一個密鑰後通過aes/3des給資料加密