密碼學進階(一):淺談常見的七種加密算法及實現(經典加密密碼算法實現)

      網友投稿 1992 2025-03-31

      前言

      數字簽名、信息加密 是前后端開發都經常需要使用到的技術,應用場景包括了用戶登入、交易、信息通訊、oauth 等等,不同的應用場景也會需要使用到不同的簽名加密算法,或者需要搭配不一樣的簽名加密算法來達到業務目標。這里簡單的給大家介紹幾種常見的簽名加密算法和一些典型場景下的應用。

      正文

      1.數字簽名

      數字簽名,簡單來說就是通過提供可鑒別的數字信息驗證自身身份的一種方式。一套數字簽名通常定義兩種互補的運算,一個用于簽名,另一個用于驗證。分別由發送者持有能夠代表自己身份的私鑰 (私鑰不可泄露),由接受者持有與私鑰對應的公鑰,能夠在接受到來自發送者信息時用于驗證其身份。

      注意:圖中加密過程有別于公鑰加密,更多介紹戳這里。簽名最根本的用途是要能夠唯一 證明發送方的身份,防止中間人攻擊、CSRF跨域身份偽造。基于這一點在諸如設備認證、用戶認證、第三方認證等認證體系中都會使用到簽名算法 (彼此的實現方式可能會有差異)。

      2.加密和解密

      2.1. 加密

      數據加密的基本過程,就是對原來為明文的文件或數據按某種算法進行處理,使其成為不可讀的一段代碼,通常稱為 “密文”。通過這樣的途徑,來達到保護數據不被非法人竊取、閱讀的目的。

      2.2. 解密

      加密的逆過程為解密,即將該編碼信息轉化為其原來數據的過程。

      3.對稱加密和非對稱加密

      加密算法分對稱加密和非對稱加密,其中對稱加密算法的加密與解密的密鑰相同,非對稱加密算法的加密密鑰與解密的密鑰不同,此外,還有一類不需要密鑰的散列算法。

      常見的對稱加密算法主要有DES、3DES、AES等,常見的非對稱算法主要有RSA、DSA 等,散列算法主要有SHA-1、MD5 等。

      3.1. 對稱加密

      對稱加密算法是應用較早的加密算法,又稱為共享密鑰加密算法。在對稱加密算法中,使用的密鑰只有一個,發送和接收雙方都使用這個密鑰對數據進行加密和解密。這就要求加密和解密方事先都必須知道加密的密鑰。

      數據加密過程:在對稱加密算法中,數據發送方將明文 (原始數據) 和加密密鑰一起經過特殊加密處理,生成復雜的加密密文進行發送。

      數據解密過程:數據接收方收到密文后,若想讀取原數據,則需要使用加密使用的密鑰及相同算法的逆算法對加密的密文進行解密,才能使其恢復成可讀明文。

      3.2. 非對稱加密

      非對稱加密算法,又稱為公開密鑰加密算法。它需要兩個密鑰,一個稱為公開密鑰 (public key),即公鑰,另一個稱為 私有密鑰 (private key),即私鑰。

      因為 加密 和 解密 使用的是兩個不同的密鑰,所以這種算法稱為 非對稱加密算法。

      如果使用 公鑰 對數據 進行加密,只有用對應的 私鑰 才能 進行解密。

      如果使用 私鑰 對數據 進行加密,只有用對應的 公鑰 才能 進行解密。

      例子:甲方生成 一對密鑰 并將其中的一把作為 公鑰 向其它人公開,得到該公鑰的 乙方 使用該密鑰對機密信息 進行加密 后再發送給甲方,甲方再使用自己保存的另一把 專用密鑰 (私鑰),對 加密 后的信息 進行解密。

      4.常見的簽名加密算法

      4.1. MD5算法

      MD5用的是哈希函數,它的典型應用是對一段信息產生 信息摘要,以防止被篡改。嚴格來說,MD5 不是一種 加密算法 而是 摘要算法。無論是多長的輸入,MD5 都會輸出長度為 128bits 的一個串 (通常用 16 進制 表示為 32 個字符)。

      public static final byte[] computeMD5(byte[] content) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); return md5.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }

      1

      2

      3

      4

      5

      6

      7

      8

      4.2. SHA1算法

      SHA1 是和 MD5 一樣流行的 消息摘要算法,然而 SHA1 比 MD5 的 安全性更強。對于長度小于 2 ^ 64 位的消息,SHA1 會產生一個 160 位的 消息摘要。基于 MD5、SHA1 的信息摘要特性以及 不可逆 (一般而言),可以被應用在檢查 文件完整性 以及 數字簽名 等場景。

      public static byte[] computeSHA1(byte[] content) { try { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); return sha1.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }

      1

      2

      3

      4

      5

      6

      7

      8

      4.3. HMAC算法

      HMAC 是密鑰相關的 哈希運算消息認證碼(Hash-based Message Authentication Code),HMAC 運算利用 哈希算法 (MD5、SHA1 等),以 一個密鑰 和 一個消息 為輸入,生成一個 消息摘要 作為 輸出。

      HMAC 發送方 和 接收方 都有 key 進行計算,而沒有這把 key 的第三方,則是無法計算 出正確的散列值的,這樣就可以防止數據被篡改。

      package net.pocrd.util; import net.pocrd.annotation.NotThreadSafe; import net.pocrd.define.ConstField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; @NotThreadSafe public class HMacHelper { private static final Logger logger = LoggerFactory.getLogger(HMacHelper.class); private Mac mac; /** * MAC算法可選以下多種算法 * HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512 */ private static final String KEY_MAC = "HmacMD5"; public HMacHelper(String key) { try { SecretKey secretKey = new SecretKeySpec(key.getBytes(ConstField.UTF8), KEY_MAC); mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); } catch (Exception e) { logger.error("create hmac helper failed.", e); } } public byte[] sign(byte[] content) { return mac.doFinal(content); } public boolean verify(byte[] signature, byte[] content) { try { byte[] result = mac.doFinal(content); return Arrays.equals(signature, result); } catch (Exception e) { logger.error("verify sig failed.", e); } return false; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      測試結論:HMAC 算法實例在多線程環境下是不安全的。但是需要在多線程訪問時,進行同步的輔助類,使用 ThreadLocal 為每個線程緩存一個實例可以避免進行鎖操作。

      4.4. AES/DES/3DES算法

      AES、DES、3DES 都是 對稱 的 塊加密算法,加解密 的過程是 可逆的。常用的有 AES128、AES192、AES256 (默認安裝的 JDK 尚不支持 AES256,需要安裝對應的 jce 補丁進行升級 jce1.7,jce1.8)。

      4.4.1. DES算法

      DES 加密算法是一種分組密碼,以64位為分組對數據加密,它的密鑰長度是56位,加密解密用同一算法。

      DES 加密算法是對 密鑰 進行保密,而 公開算法,包括加密和解密算法。這樣,只有掌握了和發送方相同密鑰的人才能解讀由 DES加密算法加密的密文數據。因此,破譯 DES 加密算法實際上就是搜索密鑰的編碼。對于56位長度的密鑰來說,如果用窮舉法來進行搜索的話,其運算次數為2 ^ 56次。

      4.4.2. 3DES算法

      是基于 DES 的 對稱算法,對一塊數據用三個不同的密鑰進行三次加密,強度更高。

      4.4.3. AES算法

      AES 加密算法是密碼學中的高級加密標準,該加密算法采用對稱分組密碼體制,密鑰長度的最少支持為128 位、192 位、256 位,分組長度 128 位,算法應易于各種硬件和軟件實現。這種加密算法是美國聯邦政府采用的區塊加密標準。

      AES 本身就是為了取代 DES 的,AES 具有更好的 安全性、效率 和 靈活性。

      import net.pocrd.annotation.NotThreadSafe; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; @NotThreadSafe public class AesHelper { private SecretKeySpec keySpec; private IvParameterSpec iv; public AesHelper(byte[] aesKey, byte[] iv) { if (aesKey == null || aesKey.length < 16 || (iv != null && iv.length < 16)) { throw new RuntimeException("錯誤的初始密鑰"); } if (iv == null) { iv = Md5Util.compute(aesKey); } keySpec = new SecretKeySpec(aesKey, "AES"); this.iv = new IvParameterSpec(iv); } public AesHelper(byte[] aesKey) { if (aesKey == null || aesKey.length < 16) { throw new RuntimeException("錯誤的初始密鑰"); } keySpec = new SecretKeySpec(aesKey, "AES"); this.iv = new IvParameterSpec(Md5Util.compute(aesKey)); } public byte[] encrypt(byte[] data) { byte[] result = null; Cipher cipher = null; try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); result = cipher.doFinal(data); } catch (Exception e) { throw new RuntimeException(e); } return result; } public byte[] decrypt(byte[] secret) { byte[] result = null; Cipher cipher = null; try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); result = cipher.doFinal(secret); } catch (Exception e) { throw new RuntimeException(e); } return result; } public static byte[] randomKey(int size) { byte[] result = null; try { KeyGenerator gen = KeyGenerator.getInstance("AES"); gen.init(size, new SecureRandom()); result = gen.generateKey().getEncoded(); } catch (Exception e) { throw new RuntimeException(e); } return result; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      4.5. RSA算法

      RSA 加密算法是目前最有影響力的公鑰加密算法,并且被普遍認為是目前最優秀的公鑰方案之一。RSA 是第一個能同時用于加密 和數字簽名的算法,它能夠抵抗到目前為止已知的所有密碼攻擊,已被 ISO 推薦為公鑰數據加密標準。

      RSA 加密算法基于一個十分簡單的數論事實:將兩個大素數相乘十分容易,但想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰。

      import net.pocrd.annotation.NotThreadSafe; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.KeyFactory; import java.security.Security; import java.security.Signature; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @NotThreadSafe public class RsaHelper { private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class); private RSAPublicKey publicKey; private RSAPrivateCrtKey privateKey; static { Security.addProvider(new BouncyCastleProvider()); //使用bouncycastle作為加密算法實現 } public RsaHelper(String publicKey, String privateKey) { this(Base64Util.decode(publicKey), Base64Util.decode(privateKey)); } public RsaHelper(byte[] publicKey, byte[] privateKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } if (privateKey != null && privateKey.length > 0) { this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public RsaHelper(String publicKey) { this(Base64Util.decode(publicKey)); } public RsaHelper(byte[] publicKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public byte[] encrypt(byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } if (content == null) { return null; } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int size = publicKey.getModulus().bitLength() / 8 - 11; ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11)); int left = 0; for (int i = 0; i < content.length; ) { left = content.length - i; if (left > size) { cipher.update(content, i, size); i += size; } else { cipher.update(content, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } public byte[] decrypt(byte[] secret) { if (privateKey == null) { throw new RuntimeException("private key is null."); } if (secret == null) { return null; } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); int size = privateKey.getModulus().bitLength() / 8; ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size); int left = 0; for (int i = 0; i < secret.length; ) { left = secret.length - i; if (left > size) { cipher.update(secret, i, size); i += size; } else { cipher.update(secret, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { logger.error("rsa decrypt failed.", e); } return null; } public byte[] sign(byte[] content) { if (privateKey == null) { throw new RuntimeException("private key is null."); } if (content == null) { return null; } try { Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initSign(privateKey); signature.update(content); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verify(byte[] sign, byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } if (sign == null || content == null) { return false; } try { Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } catch (Exception e) { logger.error("rsa verify failed.", e); } return false; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      81

      82

      83

      84

      85

      86

      87

      88

      89

      90

      91

      92

      93

      94

      95

      96

      97

      98

      99

      100

      101

      102

      103

      104

      105

      106

      107

      108

      109

      110

      111

      112

      113

      114

      115

      116

      117

      118

      119

      120

      121

      122

      123

      124

      125

      126

      127

      128

      129

      130

      131

      132

      133

      134

      135

      136

      137

      138

      139

      140

      141

      142

      143

      144

      145

      146

      147

      148

      149

      150

      151

      152

      153

      154

      155

      156

      157

      4.6. ECC算法

      ECC 也是一種非對稱加密算法,主要優勢是在某些情況下,它比其他的方法使用更小的密鑰,比如 RSA 加密算法,提供相當的或更高等級的安全級別。不過一個缺點是加密和解密操作的實現比其他機制時間長 (相比 RSA 算法,該算法對 CPU 消耗嚴重)。

      import net.pocrd.annotation.NotThreadSafe; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.KeyFactory; import java.security.Security; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @NotThreadSafe public class EccHelper { private static final Logger logger = LoggerFactory.getLogger(EccHelper.class); private static final int SIZE = 4096; private BCECPublicKey publicKey; private BCECPrivateKey privateKey; static { Security.addProvider(new BouncyCastleProvider()); } public EccHelper(String publicKey, String privateKey) { this(Base64Util.decode(publicKey), Base64Util.decode(privateKey)); } public EccHelper(byte[] publicKey, byte[] privateKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } if (privateKey != null && privateKey.length > 0) { this.privateKey = (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); } } catch (ClassCastException e) { throw new RuntimeException("", e); } catch (Exception e) { throw new RuntimeException(e); } } public EccHelper(String publicKey) { this(Base64Util.decode(publicKey)); } public EccHelper(byte[] publicKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public byte[] encrypt(byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } try { Cipher cipher = Cipher.getInstance("ECIES", "BC"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int size = SIZE; ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 45)); int left = 0; for (int i = 0; i < content.length; ) { left = content.length - i; if (left > size) { cipher.update(content, i, size); i += size; } else { cipher.update(content, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } public byte[] decrypt(byte[] secret) { if (privateKey == null) { throw new RuntimeException("private key is null."); } try { Cipher cipher = Cipher.getInstance("ECIES", "BC"); cipher.init(Cipher.DECRYPT_MODE, privateKey); int size = SIZE + 45; ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size + 44) / (size + 45) * size); int left = 0; for (int i = 0; i < secret.length; ) { left = secret.length - i; if (left > size) { cipher.update(secret, i, size); i += size; } else { cipher.update(secret, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { logger.error("ecc decrypt failed.", e); } return null; } public byte[] sign(byte[] content) { if (privateKey == null) { throw new RuntimeException("private key is null."); } try { Signature signature = Signature.getInstance("SHA1withECDSA", "BC"); signature.initSign(privateKey); signature.update(content); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verify(byte[] sign, byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } try { Signature signature = Signature.getInstance("SHA1withECDSA", "BC"); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } catch (Exception e) { logger.error("ecc verify failed.", e); } return false; } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      81

      82

      83

      84

      85

      86

      87

      88

      89

      90

      91

      92

      93

      94

      95

      96

      97

      98

      99

      100

      101

      102

      103

      104

      105

      106

      107

      108

      109

      110

      111

      112

      113

      114

      115

      116

      117

      118

      119

      120

      121

      122

      123

      124

      125

      126

      127

      128

      129

      130

      131

      132

      133

      134

      135

      136

      137

      138

      139

      140

      141

      142

      143

      144

      5.各種加密算法對比

      5.1. 散列算法比較

      5.2. 對稱加密算法比較

      5.3. 非對稱加密算法比較

      5.4. 對稱算法與非對稱加密算法

      5.4.1. 對稱算法

      密鑰管理:比較難,不適合互聯網,一般用于內部系統

      安全性:中

      加密速度:快好 幾個數量級 (軟件加解密速度至少快 100 倍,每秒可以加解密數 M 比特 數據),適合大數據量的加解密處理

      5.4.2. 非對稱算法

      密鑰管理:密鑰容易管理

      安全性:高

      加密速度:比較慢,適合小數據量加解密或數據簽名

      小結

      本文介紹了 數字簽名,加密和解密,對稱加密和非對稱加密,然后詳細介紹了 MD5,SHA-1,HMAC,DES/AES,RSA 和 ECC 這幾種加密算法和代碼示例。

      密碼學進階(一):淺談常見的七種加密算法及實現(經典加密密碼算法實現)

      數據加密服務

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Excel怎樣把單雙號提出來?(Excel把單雙號提出來)
      下一篇:為什么在一個單元格里輸入資料再輸入(),總是會跳到前一格單元格呢??
      相關文章
      亚洲一级大黄大色毛片| 亚洲精品和日本精品| 亚洲AV之男人的天堂| 亚洲熟女www一区二区三区| 亚洲精品美女在线观看| 亚洲成色999久久网站| 亚洲V无码一区二区三区四区观看| 久久久久国产成人精品亚洲午夜| 亚洲国产a级视频| 亚洲A∨精品一区二区三区| mm1313亚洲精品无码又大又粗| 色偷偷亚洲第一综合| 国产亚洲Av综合人人澡精品| 校园亚洲春色另类小说合集| 免费在线观看亚洲| 亚洲国产精品国产自在在线| 亚洲成AⅤ人影院在线观看| 亚洲国产成人精品无码久久久久久综合 | 亚洲精品美女久久7777777| 亚洲色大成网站www| 亚洲av永久无码天堂网| 亚洲av成人一区二区三区在线播放| 亚洲а∨精品天堂在线| 国产av无码专区亚洲av毛片搜| 一区二区三区亚洲视频| 亚洲欧洲久久av| 亚洲日韩激情无码一区| 久久91亚洲精品中文字幕| 久久亚洲AV无码精品色午夜麻豆| 亚洲精品成人图区| 亚洲粉嫩美白在线| 亚洲AV永久无码天堂影院| 亚洲第一区精品观看| 亚洲综合色婷婷七月丁香| 日本亚洲欧洲免费天堂午夜看片女人员| 国产国拍亚洲精品福利| 亚洲gv白嫩小受在线观看 | 亚洲欧洲日产国码久在线| 九九精品国产亚洲AV日韩| 亚洲欧洲一区二区三区| 成人午夜亚洲精品无码网站|