当前位置:   article > 正文

信息安全第三周+_cryptography pbkdf2hmac生成key

cryptography pbkdf2hmac生成key

啊,作者对上篇文章做出改进(其实是找gpt做的改进),于本篇记录一下。

Python密钥生成

盐(Salt)

首先引入了一个随机生成的盐(salt。盐是用来确保即使两次使用相同的密码,通过密钥派生函数(如PBKDF2)生成的密钥也会是不同的。主要是服务下面的PBKDF2密钥派生函数,让函数生成的密钥变化!

salt = os.urandom(16) 

PBKDF2

使用PBKDF2HMAC,它是一个基于密码的密钥派生函数,通常用于从用户密码生成密钥。它使用HMAC作为伪随机函数,并可以进行多次迭代,增加暴力破解的难度。

  1. kdf = PBKDF2HMAC(
  2. algorithm=hashes.SHA256(),
  3. length=32,
  4. salt=salt,
  5. iterations=100000,
  6. backend=backend
  7. )
  8. key = kdf.derive(b"Some password")

在这里,使用了PBKDF2与SHA-256哈希函数,设置了100,000次迭代。这样产生的密钥不仅与所提供的密码相关,还与盐值相关,增加了密钥的复杂性和随机性。

生成随机 nonce

  • 使用os.urandom(16)代替了原来的随机函数来生成nonce。os.urandom被广泛认为是一个安全的随机数生成器,适用于密码学应用。

nonce = os.urandom(16)

数据的完整性和真实性

  • HMAC: 为了确保加密数据(密文)的完整性和真实性,添加一个HMAC(Hash-based Message Authentication Code)。HMAC是一种特定类型的消息认证码(MAC),涉及到哈希函数和秘密加密密钥。简单地说,当您发送加密的消息时,您也可以发送HMAC,接收方可以使用同样的密钥来验证消息的完整性。

  1. h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
  2. h.update(ciphertext)
  3. tag = h.finalize()

之后,在解密前,首先验证了密文的HMAC。只有在HMAC验证通过后,我们才会尝试解密密文,这可以防止潜在的篡改攻击。

  1. h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
  2. h.update(ciphertext)
  3. h.verify(tag)

这些改进旨在确保密钥的强度、数据的完整性和真实性,并采用了一些被广泛接受的加密最佳实践。然而,请注意,在实际的生产环境中,可能还需要考虑其他的安全因素和实践。

完整代码如下:

  1. import os
  2. import random
  3. from cryptography.hazmat.backends import default_backend
  4. from cryptography.hazmat.primitives import ciphers, hmac, hashes
  5. from cryptography.hazmat.primitives.ciphers import Cipher
  6. from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
  7. from cryptography.hazmat.primitives.asymmetric import padding
  8. # 密钥生成 (这只是一个简化的例子,真实情境中你需要考虑如何安全存储密钥)
  9. salt = os.urandom(16) # 加盐可以帮助防止两次生成的密钥相同
  10. backend = default_backend()
  11. kdf = PBKDF2HMAC(
  12. algorithm=hashes.SHA256(),
  13. length=32,
  14. salt=salt,
  15. iterations=100000,
  16. backend=backend
  17. )
  18. key = kdf.derive(b"Some password")
  19. # 生成随机 nonce
  20. nonce = os.urandom(16)
  21. # 创建AES Cipher实例并设置为CTR模式
  22. cipher = Cipher(ciphers.algorithms.AES(key), ciphers.modes.CTR(nonce), backend=default_backend())
  23. # 创建一个加密器对象
  24. encryptor = cipher.encryptor()
  25. # 加密"HELLO"字符串
  26. ciphertext = encryptor.update(b'HELLO') + encryptor.finalize()
  27. print("Ciphertext:", ciphertext)
  28. # 生成HMAC以确保数据完整性和真实性
  29. h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
  30. h.update(ciphertext)
  31. tag = h.finalize()
  32. decryptor = cipher.decryptor()
  33. try:
  34. # 在解密之前验证HMAC
  35. h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
  36. h.update(ciphertext)
  37. h.verify(tag)
  38. decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
  39. print("Decrypted Data:", decrypted_data)
  40. except Exception as e:
  41. print(f"Decryption failed: {e}")

作者个人问题解决

  1. kdf = PBKDF2HMAC(
  2. algorithm=hashes.SHA256(),
  3. length=32,
  4. salt=salt,
  5. iterations=100000,
  6. backend=backend
  7. )

作者首先不明白既然用的是PBKDF2,那为什么PBKDF2后要加上HMAC呢?为什么使用了HMAC作为伪随机函数,却没有在参数中体现?

原来,这段代码使用了Python的一个库:cryptography.hazmat.primitives.kdf.pbkdf2 中的 PBKDF2HMAC这个类用于实现 PBKDF2 密钥派生函数,其中 HMAC 作为其伪随机函数。


Springboot密钥生成

妈的,作者Springboot刚学,这个密钥改进花了一个多小时才完成(代码还是gpt写的),并且是在删除安全性检查依赖的基础上勉强完成的,以后一定会解决这个问题。身份认证是很重要的内容,作者不会因为它困难而不去实现,目标是请老师救救我!

下面是对Service服务层的代码处理:

密钥衍生 (Key Derivation)

在Python代码中,我们使用了PBKDF2HMAC来从密码中衍生出一个密钥。这是一个广泛应用于密码加密的技术,它通过多次对密码应用哈希函数来生成密钥。

在Java代码中:

  1. KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, 256);
  2. SecretKey tmp = javax.crypto.SecretKeyFactory.getInstance(PBKDF2_ALGORITHM).generateSecret(spec);
  3. return new SecretKeySpec(tmp.getEncoded(), ALGORITHM);

这段代码做的事情和Python代码相同:使用PBKDF2WithHmacSHA256算法从给定的密码和salt中衍生出一个256位的密钥。

生成HMAC

HMAC (Hash-based Message Authentication Code) 是一个用于数据完整性和真实性验证的工具。简单地说,它可以用来确保加密后的数据在传输或存储过程中没有被篡改。

Python代码中:

  1. h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
  2. h.update(ciphertext)
  3. tag = h.finalize()

这段代码计算了密文的HMAC值。

在Java代码中,这一步是这样完成的:

  1. Mac mac = Mac.getInstance(HMAC_ALGORITHM);
  2. mac.init(secretKey);
  3. mac.update(encryptedBytes);
  4. byte[] hmac = mac.doFinal();

我们使用了Java的Mac类来计算HMAC。上述代码段将加密后的字节数据传递给HMAC函数,并使用之前衍生出的密钥进行初始化,然后计算HMAC。便于后续进行完整性验证。

合并数据

为了便于存储和传输,通常会将多个数据片段(例如 salt, iv, hmac 和加密文本)合并成一个字节数组。

Python代码中,这部分没有显示如何做,但在Java代码中,我们使用了System.arraycopy函数来完成这一步:

byte[] combined = new byte[salt.length + iv.length + hmac.length + encryptedBytes.length];

此数组长度的计算是基于所有组件(salt, iv, hmac, 加密文本)的长度。

HMAC 验证

当解密时,一个重要的步骤是验证HMAC。这确保了数据在传输或存储过程中没有被篡改。

在Java代码中:

  1. Mac mac = Mac.getInstance(HMAC_ALGORITHM);
  2. mac.init(secretKey);
  3. mac.update(encryptedBytes);
  4. byte[] computedHmac = mac.doFinal();
  5. if (!java.util.Arrays.equals(hmac, computedHmac)) {
  6. throw new IllegalArgumentException("Invalid HMAC, data might be tampered");
  7. }

上述代码首先计算存储的加密数据的HMAC值,然后将其与传入的HMAC值进行比较。如果它们不匹配,这意味着数据可能已被篡改,因此会抛出一个异常。

完整代码演示

EnhancedEncryptionService

首先,Java中的javax.crypto库并不直接提供PBKDF2的支持。但是,Java的安全包java.security提供了PBKDF2的支持,我们可以使用这个。

以下是对先前的示例进行改进,使用PBKDF2进行密钥衍生,并添加HMAC验证来确保数据完整性:

  1. import org.springframework.stereotype.Service;
  2. import javax.crypto.Cipher;
  3. import javax.crypto.Mac;
  4. import javax.crypto.SecretKey;
  5. import javax.crypto.spec.IvParameterSpec;
  6. import javax.crypto.spec.PBEKeySpec;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import java.nio.charset.StandardCharsets;
  9. import java.security.NoSuchAlgorithmException;
  10. import java.security.SecureRandom;
  11. import java.security.spec.InvalidKeySpecException;
  12. import java.security.spec.KeySpec;
  13. import java.util.Base64;
  14. @Service
  15. public class EnhancedEncryptionService {
  16. private static final String ALGORITHM = "AES";
  17. private static final String MODE = "AES/CTR/NoPadding";
  18. private static final String HMAC_ALGORITHM = "HmacSHA256";
  19. private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
  20. private static final int SALT_LENGTH = 16;
  21. private static final int ITERATIONS = 100000;
  22. public String encrypt(String plainText, String password) throws Exception {
  23. // 生成salt和密钥
  24. SecureRandom secureRandom = new SecureRandom();
  25. byte[] salt = new byte[SALT_LENGTH];
  26. secureRandom.nextBytes(salt);
  27. SecretKey secretKey = deriveKey(password, salt);
  28. // 加密
  29. byte[] iv = new byte[SALT_LENGTH];
  30. secureRandom.nextBytes(iv);
  31. IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
  32. Cipher cipher = Cipher.getInstance(MODE);
  33. cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
  34. byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
  35. // 生成HMAC
  36. Mac mac = Mac.getInstance(HMAC_ALGORITHM);
  37. mac.init(secretKey);
  38. mac.update(encryptedBytes);
  39. byte[] hmac = mac.doFinal();
  40. // 合并 salt, iv, hmac 和 加密文本 以便存储和后续解密
  41. byte[] combined = new byte[salt.length + iv.length + hmac.length + encryptedBytes.length];
  42. System.arraycopy(salt, 0, combined, 0, salt.length);
  43. System.arraycopy(iv, 0, combined, salt.length, iv.length);
  44. System.arraycopy(hmac, 0, combined, salt.length + iv.length, hmac.length);
  45. System.arraycopy(encryptedBytes, 0, combined, salt.length + iv.length + hmac.length, encryptedBytes.length);
  46. return Base64.getEncoder().encodeToString(combined);
  47. }
  48. public String decrypt(String encryptedText, String password) throws Exception {
  49. byte[] combined = Base64.getDecoder().decode(encryptedText);
  50. // 从组合的数据中提取 salt, iv, hmac 和 加密的文本
  51. byte[] salt = new byte[SALT_LENGTH];
  52. byte[] iv = new byte[SALT_LENGTH];
  53. byte[] hmac = new byte[32];
  54. byte[] encryptedBytes = new byte[combined.length - SALT_LENGTH - SALT_LENGTH - hmac.length];
  55. System.arraycopy(combined, 0, salt, 0, salt.length);
  56. System.arraycopy(combined, salt.length, iv, 0, iv.length);
  57. System.arraycopy(combined, salt.length + iv.length, hmac, 0, hmac.length);
  58. System.arraycopy(combined, salt.length + iv.length + hmac.length, encryptedBytes, 0, encryptedBytes.length);
  59. SecretKey secretKey = deriveKey(password, salt);
  60. // 验证 HMAC
  61. Mac mac = Mac.getInstance(HMAC_ALGORITHM);
  62. mac.init(secretKey);
  63. mac.update(encryptedBytes);
  64. byte[] computedHmac = mac.doFinal();
  65. if (!java.util.Arrays.equals(hmac, computedHmac)) {
  66. throw new IllegalArgumentException("Invalid HMAC, data might be tampered");
  67. }
  68. // 解密
  69. Cipher cipher = Cipher.getInstance(MODE);
  70. cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
  71. byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
  72. return new String(decryptedBytes, StandardCharsets.UTF_8);
  73. }
  74. private SecretKey deriveKey(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
  75. KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, 256); // AES-256
  76. SecretKey tmp = javax.crypto.SecretKeyFactory.getInstance(PBKDF2_ALGORITHM).generateSecret(spec);
  77. return new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
  78. }
  79. }

CryptoController

  1. package com.example.dataencryption.controller;
  2. import com.example.dataencryption.service.EnhancedEncryptionService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.*;
  5. @RestController
  6. @RequestMapping("/encryption")
  7. public class CryptoController {
  8. private final EnhancedEncryptionService encryptionService;
  9. @Autowired
  10. public CryptoController(EnhancedEncryptionService encryptionService) {
  11. this.encryptionService = encryptionService;
  12. }
  13. @PostMapping("/encrypt")
  14. public EncryptResponse encrypt(@RequestBody EncryptRequest request) throws Exception {
  15. String encryptedText = encryptionService.encrypt(request.getPlainText(), request.getPassword());
  16. return new EncryptResponse(encryptedText);
  17. }
  18. @PostMapping("/decrypt")
  19. public DecryptResponse decrypt(@RequestBody DecryptRequest request) throws Exception {
  20. String decryptedText = encryptionService.decrypt(request.getEncryptedText(), request.getPassword());
  21. return new DecryptResponse(decryptedText);
  22. }
  23. static class EncryptRequest {
  24. private String plainText;
  25. private String password;
  26. // getters and setters
  27. public String getPlainText() {
  28. return plainText;
  29. }
  30. public void setPlainText(String plainText) {
  31. this.plainText = plainText;
  32. }
  33. public String getPassword() {
  34. return password;
  35. }
  36. public void setPassword(String password) {
  37. this.password = password;
  38. }
  39. }
  40. static class DecryptRequest {
  41. private String encryptedText;
  42. private String password;
  43. // getters and setters
  44. public String getEncryptedText() {
  45. return encryptedText;
  46. }
  47. public void setEncryptedText(String encryptedText) {
  48. this.encryptedText = encryptedText;
  49. }
  50. public String getPassword() {
  51. return password;
  52. }
  53. public void setPassword(String password) {
  54. this.password = password;
  55. }
  56. }
  57. static class EncryptResponse {
  58. private String encryptedText;
  59. public EncryptResponse(String encryptedText) {
  60. this.encryptedText = encryptedText;
  61. }
  62. // getter
  63. public String getEncryptedText() {
  64. return encryptedText;
  65. }
  66. }
  67. static class DecryptResponse {
  68. private String decryptedText;
  69. public DecryptResponse(String decryptedText) {
  70. this.decryptedText = decryptedText;
  71. }
  72. // getter
  73. public String getDecryptedText() {
  74. return decryptedText;
  75. }
  76. }
  77. }
  1. @RestController & @RequestMapping:标记这是一个RESTful Controller,处理的基本URL路径是/encryption

  2. 加密和解密的请求方法:我为加密和解密操作创建了两个HTTP POST方法,因为我们需要向服务器发送数据。

  3. Request & Response Classes:为了清晰地定义输入和输出格式,我创建了四个内部类:EncryptRequest, DecryptRequest, EncryptResponse, 和 DecryptResponse。这些类代表请求的内容和响应的格式。

测试操作

以下是如何使用Postman来测试上述Spring Boot加密和解密Controller的步骤:

  1. 安装和启动 Postman:

  2. 配置请求:

    • 在请求类型选择框中选择 POST
    • 在URL输入框中输入您的Spring Boot应用的URL,如 http://localhost:8080/encryption/encrypt(这假设你的应用运行在默认的8080端口上)。
  3. 设置请求体:

    • 选择 Body 选项卡。
    • 选择 rawJSON
    • 在输入框中输入请求体。例如:
      1. {
      2. "plainText": "HELLO",
      3. "password": "yourpassword"
      4. }
  4. 发送请求:

    • 点击 Send 按钮发送请求。
    • 在下方的响应部分,你应该看到返回的加密文本。
  5. 测试解密:

    • 修改URL为 http://localhost:8080/encryption/decrypt
    • 在请求体中,将plainText换成encryptedText并输入从上一步获得的加密文本。
      1. {
      2. "encryptedText": "the_encrypted_text_here",
      3. "password": "yourpassword"
      4. }
    • 点击 Send,你应该看到原文 "HELLO" 作为响应。
  6. 查看响应:

    • 下方会展示服务器的响应。你可以查看返回的数据、HTTP状态码、响应时间等信息。

作者使用的是Apipost7,挺好看的倒是,结果如下:

解密效果如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/687845
推荐阅读
相关标签
  

闽ICP备14008679号