自学内容网 自学内容网

java 加密算法

Java 提供了多种加密算法,支持对数据进行加密、解密以及消息摘要等操作。常见的加密算法可以分为 对称加密非对称加密哈希算法 三类。以下是对这些算法的简要介绍及其在 Java 中的实现方式。

1. 对称加密算法

对称加密算法使用相同的密钥进行加密和解密。常见的对称加密算法包括 AES(高级加密标准)、DES(数据加密标准)和 3DES(三重 DES)。

AES(高级加密标准)

AES 是现代加密算法中最为广泛使用的对称加密算法。它支持多种密钥长度(如 128、192、256 位)并具有高效性。

  • 加密/解密过程:
    • 使用相同的密钥进行加密和解密。
    • AES 支持不同模式(如 ECB、CBC、CFB、OFB)和填充方式(如 PKCS5Padding、NoPadding 等)。

Java 示例代码(AES):

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;

public class AESExample {
    public static void main(String[] args) throws Exception {
        // 生成 AES 密钥
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128); // 支持 128、192、256 位密钥
        SecretKey secretKey = keyGenerator.generateKey();
        
        // 创建 AES Cipher 对象
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        
        // 随机生成初始向量
        byte[] iv = new byte[16];
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        
        // 加密
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
        String plaintext = "Hello, World!";
        byte[] encrypted = cipher.doFinal(plaintext.getBytes());
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        
        // 解密
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        System.out.println("Decrypted: " + new String(decrypted));
    }
}
DES(数据加密标准)

DES 是一种较为古老的对称加密算法,已经不再推荐用于加密高安全性的数据,因其密钥长度较短(56 位)且容易被暴力破解。虽然它仍然可以用于一些较低安全要求的应用,但 AES 被广泛认为是更安全的替代方案。

Java 示例代码(DES):

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class DESExample {
    public static void main(String[] args) throws Exception {
        // 生成 DES 密钥
        KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
        keyGenerator.init(56); // DES 密钥长度为 56 位
        SecretKey secretKey = keyGenerator.generateKey();
        
        // 创建 DES Cipher 对象
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        
        // 加密
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        String plaintext = "Hello, World!";
        byte[] encrypted = cipher.doFinal(plaintext.getBytes());
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        
        // 解密
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] decrypted = cipher.doFinal(encrypted);
        System.out.println("Decrypted: " + new String(decrypted));
    }
}

2. 非对称加密算法

非对称加密使用一对密钥,分别为 公钥(用于加密)和 私钥(用于解密)。常见的非对称加密算法有 RSADSA(数字签名算法)。

RSA(Rivest-Shamir-Adleman)

RSA 是一种常见的非对称加密算法,用于数据加密和数字签名。RSA 的安全性依赖于大数分解的难度。

  • 加密/解密过程:
    • 使用公钥加密数据,私钥解密。
    • 加密的块大小必须小于密钥长度。

Java 示例代码(RSA):

 
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Base64;

public class RSAExample {
    public static void main(String[] args) throws Exception {
        // 生成 RSA 密钥对
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048); // 2048 位密钥
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // 加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        String plaintext = "Hello, World!";
        byte[] encrypted = cipher.doFinal(plaintext.getBytes());
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));

        // 解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decrypted = cipher.doFinal(encrypted);
        System.out.println("Decrypted: " + new String(decrypted));
    }
}

3. 哈希算法

哈希算法(又叫散列算法)用于生成固定长度的输出(哈希值),常用于数据完整性验证、数字签名等场景。常见的哈希算法有 MD5SHA-1SHA-256

SHA-256

SHA-256 是一种安全哈希算法,生成 256 位的哈希值,常用于密码存储和文件完整性验证。

Java 示例代码(SHA-256):

import java.security.MessageDigest;
import java.util.Base64;

public class SHA256Example {
    public static void main(String[] args) throws Exception {
        String input = "Hello, World!";
        
        // 创建 SHA-256 MessageDigest 对象
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        
        // 计算哈希值
        byte[] hash = messageDigest.digest(input.getBytes());
        
        // 输出哈希值
        System.out.println("SHA-256 Hash: " + Base64.getEncoder().encodeToString(hash));
    }
}

4. 常见加密算法总结

加密算法类型描述
AES对称加密高级加密标准,支持多种密钥长度
DES对称加密数据加密标准,已不推荐使用
RSA非对称加密公钥加密、私钥解密,广泛用于加密和数字签名
MD5哈希算法常用于数据完整性检查,已不再推荐用于安全应用
SHA-1哈希算法安全哈希算法,已被认为不够安全
SHA-256哈希算法安全哈希算法,生成 256 位哈希值

总结

  • 对称加密:算法简单,性能高,但密钥管理较为困难。常见的如 AESDES
  • 非对称加密:使用公钥和私钥,安全性较高,但性能相对较差。常见的如 RSA
  • 哈希算法:用于验证数据完整性,生成固定长度的哈希值,常见的如 SHA-256

Java 提供了强大的加密支持,可以通过 javax.cryptojava.security 包中的类来实现这些加密算法。

实战

package com.smcv.mall.tasks.util;

import com.alibaba.fastjson.JSON;
import com.smcv.mall.tasks.model.dto.lbs.req.SendConfigReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;

@Component
@Slf4j
public class LovPlatformReqSignUtils {

    private LovPlatformReqSignUtils() {
    }

    /**
     * pro xxxpro
     * uat b82bf8347c2f4800b51bd6a5d04013c9
     * <p>
     * 线下分配密钥
     */

    @Value("${smcv.xxx.lov.secret:b84800b51bd6a5d04013c9}")
    private String secret;

    public String signature(String path, SendConfigReqDTO body, String timestamp) {
        // 过滤掉这些特殊字符 { } " ' , :
        String param = path + JSON.toJSONString(body).replaceAll("[{}\"',:]", "");
        // 对字符进⾏排序
        char[] chars = param.toCharArray();
        Arrays.sort(chars);
        // 整体结构:secret + 请求参数 + timestamp
        String str = secret + new String(chars) + timestamp;
        log.info("param: {}, str: {}", param, str);
        // 使⽤sha1计算得到⼀个签名值
        return sha1(str);
    }

    /**
     * 加签
     *
     * @param path      url路径参数,从(?,结尾]
     * @param body      消息体,此处⽤map代替,⼀般为实体类
     * @param timestamp 与header中时间戳保持⼀致
     * @return 签名
     */
    public String signature(String path, Map<String, Object> body, String timestamp) {
        // 过滤掉这些特殊字符 { } " ' , :
        String param = path + JSON.toJSONString(body).replaceAll("[{}\"',:]", "");
        // 对字符进⾏排序
        char[] chars = param.toCharArray();
        Arrays.sort(chars);
        // 整体结构:secret + 请求参数 + timestamp
        String str = secret + new String(chars) + timestamp;
        // 使⽤sha1计算得到⼀个签名值
        return sha1(str);
    }

    /**
     * SHA1加密,可⽤⼯具类代替
     *
     * @param input
     * @return
     */
    private String sha1(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] result = md.digest(input.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : result) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

一般加密串,为什么拼接时间戳

在加密和数据传输过程中,拼接 时间戳 是一种常见的做法,它有助于提高安全性和防止各种攻击。以下是将时间戳拼接到加密串中的主要原因:

1. 防止重放攻击 (Replay Attack)

重放攻击 是指攻击者截获并重新发送已经有效的消息,通常用于绕过认证或授权过程。为了防止这种攻击,通常会在消息中加入 时间戳

  • 时间戳可以确保每个消息只能在特定时间内有效。如果消息的时间戳超出了允许的时间窗口(例如 5 分钟),接收方会认为这条消息是重放的,进而拒绝该消息。
  • 通过在每次请求时加入不同的时间戳,确保同一请求不会被攻击者重复发送。

示例: 假设某个 API 接口要求请求中携带时间戳。如果攻击者截获了有效请求并稍后再次发送(重放请求),由于时间戳不再有效,接收方会拒绝这个请求。

2. 增加唯一性

拼接时间戳可以确保每次生成的加密字符串都是唯一的,即使消息内容完全相同。即使其他部分(如用户ID、请求内容)不变,时间戳的不同也能确保生成的加密串不会重复。这对于防止 碰撞攻击(即两个不同的输入生成相同的加密输出)是有帮助的。

3. 防止缓存中间人攻击(Man-in-the-Middle Attack)

在一些情况下,攻击者可能在数据传输过程中进行中间人攻击(MITM),修改消息内容或发送伪造的消息。时间戳可以有效避免这种攻击,因为加密串通常是基于特定时间戳生成的,攻击者无法在不重新计算加密值的情况下修改时间戳。

4. 确保消息的时效性

在某些系统中,特别是金融交易或敏感操作,消息的时效性非常重要。时间戳可以确保处理的消息是在一个合理的时间范围内生成的。如果消息的时间戳过旧,接收方可以拒绝消息处理,避免处理过时的数据。

5. 避免缓存攻击

时间戳的引入能够避免缓存攻击,即攻击者通过保存某一时刻的请求及其加密结果,在稍后重新提交来获取响应。由于时间戳会随请求的生成而变化,每次请求都会有不同的加密串,防止了攻击者重复使用缓存中的加密数据。

6. 增强安全性与一致性

时间戳是一个简单但有效的随机因素,结合加密技术使用时能显著增强加密信息的安全性。它增加了每个请求的不可预测性,即使攻击者知道加密算法和密钥,也无法推测出有效的请求内容。

示例:时间戳与加密的结合

假设我们要加密一个用户请求,在请求中包含时间戳。通常,时间戳和请求内容会一起拼接并进行加密,这样可以确保每个请求的加密串是唯一的,并且与时间相关。

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Base64;

public class EncryptWithTimestampExample {
    public static void main(String[] args) throws Exception {
        // 生成时间戳
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        String timestamp = sdf.format(new Date());

        // 请求内容
        String content = "userID=123&action=login";

        // 拼接时间戳
        String dataToEncrypt = content + "&timestamp=" + timestamp;

        // 加密操作
        String encryptedData = encrypt(dataToEncrypt);
        System.out.println("Encrypted Data: " + encryptedData);

        // 解密操作(模拟验证)
        String decryptedData = decrypt(encryptedData);
        System.out.println("Decrypted Data: " + decryptedData);
    }

    // 加密方法
    private static String encrypt(String data) throws Exception {
        // 使用 AES 算法生成密钥
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);  // 使用 128 位密钥
        SecretKey secretKey = keyGenerator.generateKey();

        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }

    // 解密方法
    private static String decrypt(String encryptedData) throws Exception {
        // 使用 AES 算法生成密钥(这里应该与加密时使用的密钥相同)
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);
        SecretKey secretKey = keyGenerator.generateKey();

        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decrypted, StandardCharsets.UTF_8);
    }
}

总结

拼接时间戳到加密串的主要目的是为了:

  1. 防止重放攻击,确保每个请求是唯一且及时的;
  2. 增加数据的唯一性和不可预测性,避免碰撞和缓存攻击;
  3. 提高消息的时效性,确保只有在合理的时间内传输的消息有效。

通过这些方法,结合加密技术使用时间戳可以显著增强系统的安全性,确保通信的完整性和保密性。


原文地址:https://blog.csdn.net/C18298182575/article/details/144810648

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!