0
点赞
收藏
分享

微信扫一扫

非对称加密算法 (RSA、DSA、ECC、DH)

云卷云舒xj 2022-01-28 阅读 114
网络协议

 

最近的文章中多次出现了非对称加密,今天就来分析一下非对称加密的算法。

 

目录:

  1. 简介
  2. RSA算法
  3. DSA算法
  4. ECC算法
  5. DH算法

 

 

1. 简介

 

  • 1.1 概念

非对称加密算法的保密性好,它消除了最终用户交换密钥的需要。但是加解密速度要远远慢于对称加密,在某些极端情况下,甚至能比对称加密慢上1000倍。

 

  • 1.2 特点

 

  • 1.3 工作原理

 

(1) A 要向 B 发送信息,A 和 B 都要产生一对用于加密和解密的公钥和私钥。
(2) A 的私钥保密,A 的公钥告诉 B;B 的私钥保密,B 的公钥告诉 A。
(3) A 要给 B 发送信息时,A 用 B 的公钥加密信息,因为 A 知道 B 的公钥。
(4) A 将这个消息发给 B (已经用 B 的公钥加密消息)。
(5) B 收到这个消息后,B 用自己的私钥解密 A 的消息。其他所有收到这个报文的人都无法解密,因为只有 B 才有 B 的私钥。

 

  • 1.4 主要算法

RSA、Elgamal、背包算法、Rabin、D-H、ECC (椭圆曲线加密算法)。使用最广泛的是 RSA 算法,Elgamal 是另一种常用的非对称加密算法。

 

  • 1.5 应用场景

(1) 信息加密

收信者是唯一能够解开加密信息的人,因此收信者手里的必须是私钥。发信者手里的是公钥,其它人知道公钥没有关系,因为其它人发来的信息对收信者没有意义。

(2) 登录认证

客户端需要将认证标识传送给服务器,此认证标识 (可能是一个随机数) 其它客户端可以知道,因此需要用私钥加密,客户端保存的是私钥。服务器端保存的是公钥,其它服务器知道公钥没有关系,因为客户端不需要登录其它服务器。

(3) 数字签名

数字签名是为了表明信息没有受到伪造,确实是信息拥有者发出来的,附在信息原文的后面。就像手写的签名一样,具有不可抵赖性和简洁性。

简洁性:对信息原文做哈希运算,得到消息摘要,信息越短加密的耗时越少。

不可抵赖性:信息拥有者要保证签名的唯一性,必须是唯一能够加密消息摘要的人,因此必须用私钥加密 (就像字迹他人无法学会一样),得到签名。如果用公钥,那每个人都可以伪造签名了。

(4) 数字证书

问题起源:对1和3,发信者怎么知道从网上获取的公钥就是真的?没有遭受中间人攻击?

这样就需要第三方机构来保证公钥的合法性,这个第三方机构就是 CA (Certificate Authority),证书中心。

CA 用自己的私钥对信息原文所有者发布的公钥和相关信息进行加密,得出的内容就是数字证书。

信息原文的所有者以后发布信息时,除了带上自己的签名,还带上数字证书,就可以保证信息不被篡改了。信息的接收者先用 CA给的公钥解出信息所有者的公钥,这样可以保证信息所有者的公钥是真正的公钥,然后就能通过该公钥证明数字签名是否真实了。

这个的实际应用可以看看我之前的文章:网络篇 - https协议中的数据是否需要二次加密,里面讲的比较详细。

 

 

 

2. RSA算法

 

  • 2.1 简介

RSA 是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。

 

  • 2.2 工作流程

A 要把信息发给 B 为例,确定角色:A 为加密者,B 为解密者。首先由 B 随机确定一个 KEY,称之为私钥,将这个 KEY 始终保存在机器 B 中而不发出来;然后,由这个 KEY 计算出另一个 KEY,称之为公钥。这个公钥的特性是几乎不可能通过它自身计算出生成它的私钥。接下来通过网络把这个公钥传给 A,A 收到公钥后,利用公钥对信息加密,并把密文通过网络发送到 B,最后 B 利用已知的私钥,就能对密文进行解码了。以上就是 RSA 算法的工作流程。

 

  • 2.3 运算速度

由于进行的都是大数计算,使得 RSA 最快的情况也比 DES 慢上好几倍,无论是软件还是硬件实现。速度一直是 RSA 的缺陷。一般来说只用于少量数据加密。RSA 的速度是对应同样安全级别的对称密码算法的1/1000左右。

比起 DES 和其它对称算法来说,RSA 要慢得多。实际上一般使用一种对称算法来加密信息,然后用 RSA 来加密比较短的公钥,然后将用 RSA 加密的公钥和用对称算法加密的消息发送给接收方。

这样一来对随机数的要求就更高了,尤其对产生对称密码的要求非常高,否则的话可以越过 RSA 来直接攻击对称密码。

 

  • 2.4 公钥传递安全

和其它加密过程一样,对 RSA 来说分配公钥的过程是非常重要的。分配公钥的过程必须能够抵挡中间人攻击。假设 A 交给 B 一个公钥,并使 B 相信这是A 的公钥,并且 C 可以截下 A 和 B 之间的信息传递,那么 C 可以将自己的公钥传给 B,B 以为这是 A 的公钥。C 可以将所有 B 传递给 A 的消息截下来,将这个消息用自己的密钥解密,读这个消息,然后将这个消息再用 A 的公钥加密后传给 A。理论上 A 和 B 都不会发现 C 在偷听它们的消息,今天人们一般用数字认证来防止这样的攻击。

 

  • 2.5 攻击

(1) 针对 RSA 最流行的攻击一般是基于大数因数分解。1999年,RSA-155 (512 bits) 被成功分解,花了五个月时间(约8000 MIPS 年)和224 CPU hours 在一台有3.2G 中央内存的 Cray C916计算机上完成。

RSA-158 表示如下:

1

395058745832651445264197678006144819960207764603049364541393760515793556265294506836097278424682195350935443058704902519956553357102097992264849779494429556033388495837466721394368393204672181522815830368604993048084925840555281177×  11658823406671259903148376558383270818131012258146392600439520994131344334162924536139

2009年12月12日,编号为 RSA-768 (768 bits, 232 digits) 数也被成功分解。这一事件威胁了现通行的1024-bit 密钥的安全性,普遍认为用户应尽快升级到2048-bit 或以上。

RSA-768表示如下:

1

12301866845301177551304949583849627207728535695953347921973224521517264005072636575187452021997864693899564749427740638459251925573263034537315482685079170261221429134616704292143116022212404792747377940806653514195974598569021434133347807169895689878604416984821269081770479498371376856891  2431388982883793878002287614711652531743087737814467999489×  3674604366679959042824463379962795263227915816434308764267  6032283815739666511279233373417143396810270092798736308917

(2) 秀尔算法

量子计算里的秀尔算法能使穷举的效率大大的提高。由于 RSA 算法是基于大数分解 (无法抵抗穷举攻击),因此在未来量子计算能对 RSA 算法构成较大的威胁。一个拥有 N 量子位的量子计算机,每次可进行2^N 次运算,理论上讲,密钥为1024位长的 RSA 算法,用一台512量子比特位的量子计算机在1秒内即可破解。

 

  • 2.6 例子

 
  1. private static final String ALGO = "RSA";

  2. private static final String CHARSET = "UTF-8";

  3. /*

  4. * 用于存储随机产生的公钥与私钥

  5. */

  6. private static Map<Integer, String> KEY_CACHE = new HashMap<>();

  7. /**

  8. * 随机生成密钥对

  9. *

  10. * @throws NoSuchAlgorithmException

  11. */

  12. private static void generateKeyPair () throws NoSuchAlgorithmException {

  13. // KeyPairGenerator 类用于生成公钥和私钥对,基于RSA算法生成对象

  14. KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGO);

  15. // 初始化密钥对生成器,密钥大小为 96-1024 位

  16. keyPairGen.initialize( 1024, new SecureRandom());

  17. // 生成一个密钥对,保存在 keyPair 中

  18. KeyPair keyPair = keyPairGen.generateKeyPair();

  19. // 得到私钥

  20. RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

  21. // 得到公钥

  22. RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

  23. String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));

  24. // 得到私钥字符串

  25. String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));

  26. // 将公钥和私钥保存到 Map

  27. KEY_CACHE.put( 0, publicKeyString);

  28. KEY_CACHE.put( 1, privateKeyString);

  29. }

  30. /**

  31. * RSA公钥加密

  32. *

  33. * @param data 加密字符串

  34. * @param publicKey 公钥

  35. * @return 密文

  36. * @throws Exception 加密过程中的异常信息

  37. */

  38. private static String encrypt (String data, String publicKey) throws Exception {

  39. // base64 编码的公钥

  40. byte[] decoded = Base64.getDecoder().decode(publicKey);

  41. RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(ALGO).generatePublic( new X509EncodedKeySpec(decoded));

  42. // RSA加密

  43. Cipher cipher = Cipher.getInstance(ALGO);

  44. // 公钥加密

  45. cipher.init(Cipher.ENCRYPT_MODE, pubKey);

  46. return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes(CHARSET)));

  47. }

  48. /**

  49. * RSA私钥解密

  50. *

  51. * @param data 加密字符串

  52. * @param privateKey 私钥

  53. * @return 铭文

  54. * @throws Exception 解密过程中的异常信息

  55. */

  56. private static String decrypt (String data, String privateKey) throws Exception {

  57. byte[] inputByte = Base64.getDecoder().decode(data.getBytes(CHARSET));

  58. // base64 编码的私钥

  59. byte[] decoded = Base64.getDecoder().decode(privateKey);

  60. RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(ALGO).generatePrivate( new PKCS8EncodedKeySpec(decoded));

  61. // RSA 解密

  62. Cipher cipher = Cipher.getInstance(ALGO);

  63. // 私钥解密

  64. cipher.init(Cipher.DECRYPT_MODE, priKey);

  65. return new String(cipher.doFinal(inputByte));

  66. }

  67. public static void main (String[] args) {

  68. String originData = "hellp Test Asymmetric encrypt!";

  69. try {

  70. generateKeyPair();

  71. String encryData = encrypt(originData, KEY_CACHE.get( 0));

  72. System.out.println( "encryData = " + encryData);

  73. String decryData = decrypt(encryData, KEY_CACHE.get( 1));

  74. System.out.println( "decryData = " + decryData);

  75. } catch (Exception e) {

  76. e.printStackTrace();

  77. }

  78. }

执行输出:

 

 

 

3. DSA算法

 

  • 3.1 简介

简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名,如果数据和签名不匹配则认为验证失败。数字签名的作用就是校验数据在传输过程中不被修改,数字签名,是单向加密的升级。

 

  • 3.2 处理过程

(1) 使用消息摘要算法将发送数据加密生成数字摘要。
(2) 发送方用自己的私钥对摘要再加密,形成数字签名。 
(3) 将原文和加密的摘要同时传给对方。 
(4) 接受方用发送方的公钥对摘要解密,同时对收到的数据用消息摘要算法产生同一摘要。
(5) 将解密后的摘要和收到的数据在接收方重新加密产生的摘要相互对比,如果两者一致,则说明在传送过程中信息没有破坏和篡改。否则,则说明信息已经失去安全性和保密性。

具体可以看这篇:网络篇 - https协议中的数据是否需要二次加密

 

  • 3.3 例子

 
  1. /**

  2. * DSA安全编码组件

  3. */

  4. public abstract class DSACoder extends Coder {

  5. public static final String ALGORITHM = "DSA";

  6. /**

  7. * 默认密钥字节数

  8. *

  9. * <pre>

  10. * DSA

  11. * Default Keysize 1024

  12. * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).

  13. * </pre>

  14. */

  15. private static final int KEY_SIZE = 1024;

  16. /**

  17. * 默认种子

  18. */

  19. private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";

  20. private static final String PUBLIC_KEY = "DSAPublicKey";

  21. private static final String PRIVATE_KEY = "DSAPrivateKey";

  22. /**

  23. * 用私钥对信息生成数字签名

  24. *

  25. * @param data

  26. * 加密数据

  27. * @param privateKey

  28. * 私钥

  29. *

  30. * @return

  31. * @throws Exception

  32. */

  33. public static String sign (byte[] data, String privateKey) throws Exception {

  34. // 解密由base64编码的私钥

  35. byte[] keyBytes = decryptBASE64(privateKey);

  36. // 构造PKCS8EncodedKeySpec对象

  37. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

  38. // KEY_ALGORITHM 指定的加密算法

  39. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

  40. // 取私钥匙对象

  41. PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

  42. // 用私钥对信息生成数字签名

  43. Signature signature = Signature.getInstance(keyFactory.getAlgorithm());

  44. signature.initSign(priKey);

  45. signature.update(data);

  46. return encryptBASE64(signature.sign());

  47. }

  48. /**

  49. * 校验数字签名

  50. *

  51. * @param data

  52. * 加密数据

  53. * @param publicKey

  54. * 公钥

  55. * @param sign

  56. * 数字签名

  57. *

  58. * @return 校验成功返回true 失败返回false

  59. * @throws Exception

  60. *

  61. */

  62. public static boolean verify (byte[] data, String publicKey, String sign)

  63. throws Exception {

  64. // 解密由base64编码的公钥

  65. byte[] keyBytes = decryptBASE64(publicKey);

  66. // 构造X509EncodedKeySpec对象

  67. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

  68. // ALGORITHM 指定的加密算法

  69. KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

  70. // 取公钥匙对象

  71. PublicKey pubKey = keyFactory.generatePublic(keySpec);

  72. Signature signature = Signature.getInstance(keyFactory.getAlgorithm());

  73. signature.initVerify(pubKey);

  74. signature.update(data);

  75. // 验证签名是否正常

  76. return signature.verify(decryptBASE64(sign));

  77. }

  78. /**

  79. * 生成密钥

  80. *

  81. * @param seed

  82. * 种子

  83. * @return 密钥对象

  84. * @throws Exception

  85. */

  86. public static Map<String, Object> initKey (String seed) throws Exception {

  87. KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);

  88. // 初始化随机产生器

  89. SecureRandom secureRandom = new SecureRandom();

  90. secureRandom.setSeed(seed.getBytes());

  91. keygen.initialize(KEY_SIZE, secureRandom);

  92. KeyPair keys = keygen.genKeyPair();

  93. DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();

  94. DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();

  95. Map<String, Object> map = new HashMap<String, Object>( 2);

  96. map.put(PUBLIC_KEY, publicKey);

  97. map.put(PRIVATE_KEY, privateKey);

  98. return map;

  99. }

  100. /**

  101. * 默认生成密钥

  102. *

  103. * @return 密钥对象

  104. * @throws Exception

  105. */

  106. public static Map<String, Object> initKey () throws Exception {

  107. return initKey(DEFAULT_SEED);

  108. }

  109. /**

  110. * 取得私钥

  111. *

  112. * @param keyMap

  113. * @return

  114. * @throws Exception

  115. */

  116. public static String getPrivateKey (Map<String, Object> keyMap)

  117. throws Exception {

  118. Key key = (Key) keyMap.get(PRIVATE_KEY);

  119. return encryptBASE64(key.getEncoded());

  120. }

  121. /**

  122. * 取得公钥

  123. *

  124. * @param keyMap

  125. * @return

  126. * @throws Exception

  127. */

  128. public static String getPublicKey (Map<String, Object> keyMap)

  129. throws Exception {

  130. Key key = (Key) keyMap.get(PUBLIC_KEY);

  131. return encryptBASE64(key.getEncoded());

  132. }

  133. }

 

 

 

4. ECC算法

 

  • 4.1 简介

ECC 的主要优势是在某些情况下它比其他的方法使用更小的密钥 (比如 RSA),提供相当的或更高等级的安全。ECC 的另一个优势是可以定义群之间的双线性映射,基于 Weil 对或是 Tate 对;双线性映射已经在密码学中发现了大量的应用,例如基于身份的加密。不过一个缺点是加密和解密操作的实现比其他机制花费的时间长。

ECC 被广泛认为是在给定密钥长度的情况下,最强大的非对称算法,因此在对带宽要求十分紧的连接中会十分有用。

 

  • 4.2 优势

(1) 安全性高,有研究表示160位的椭圆密钥与1024位的 RSA 密钥安全性相同。

(2) 处理速度快,在私钥的加密解密速度上,ECC 算法比 RSA、DSA 速度更快,存储空间占用小,带宽要求低。 

 

  • 4.3 例子

https://github.com/esxgx/easy-ecc

Java 中 Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey 均不支持 ECC 算法。

 

 

 

5. DH算法

 

  • 5.1 简介

 

  • 5.2 例子

具体例子可以移步到这篇文章:非对称密码之DH密钥交换算法

 

</article>
举报

相关推荐

0 条评论