微信消息加解密技术介绍 Java

引言

微信是目前国内最流行的社交媒体平台之一,它为用户提供了便捷的消息传递和社交互动功能。为了确保用户的隐私安全,微信采用了消息加解密技术,对传输的消息进行加密和解密处理。本文将介绍微信消息加解密的原理和使用 Java 实现的示例代码。

微信消息加解密原理

微信消息加解密是通过对消息进行 AES(高级加密标准)对称加解密操作来实现的。在加解密过程中,需要使用到微信公众平台提供的相关 API,其中包括消息加解密的密钥和相关算法。下面是微信消息加解密的基本流程:

  1. 接收消息:当用户发送消息给公众号时,公众号会将消息内容以 XML 格式发送给开发者服务器。

  2. 解密消息:开发者服务器首先对收到的消息进行解密操作,解密前需要验证消息的真实性。解密操作需要使用到以下参数:

    • 消息加解密的密钥(EncodingAESKey)
    • AppID(开发者在微信公众平台申请的唯一标识)
  3. 校验消息:解密后的消息为明文 XML 格式,开发者服务器需要根据消息的内容和相关规则进行校验,以确保消息的真实性。

  4. 加密回复:开发者服务器在回复用户时,需要将回复的消息进行加密操作,加密操作需要使用到以下参数:

    • 消息加解密的密钥(EncodingAESKey)
    • AppID(开发者在微信公众平台申请的唯一标识)
  5. 发送回复:加密后的消息以 XML 格式发送给微信公众平台,然后由公众平台将消息转发给用户。

Java 示例代码

下面是使用 Java 实现微信消息加解密的示例代码:

import java.security.AlgorithmParameters;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class WechatMessageEncryptor {
    private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";

    private byte[] aesKey;
    private String appId;

    public WechatMessageEncryptor(byte[] aesKey, String appId) {
        this.aesKey = aesKey;
        this.appId = appId;
    }

    public String encrypt(String plaintext, String timestamp, String nonce) throws Exception {
        byte[] randomBytes = new byte[16];
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);

        AlgorithmParameters params = cipher.getParameters();
        IvParameterSpec ivSpec = params.getParameterSpec(IvParameterSpec.class);
        randomBytes = ivSpec.getIV();

        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());

        StringBuilder sb = new StringBuilder();
        sb.append(Base64.encodeBase64String(encryptedBytes));
        sb.append(Base64.encodeBase64String(appId.getBytes()));
        sb.append(Base64.encodeBase64String(timestamp.getBytes()));
        sb.append(Base64.encodeBase64String(nonce.getBytes()));

        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digest = md.digest(sb.toString().getBytes());
        String signature = Base64.encodeBase64String(digest);

        return Base64.encodeBase64String(randomBytes) + signature;
    }

    public String decrypt(String ciphertext, String signature, String timestamp, String nonce) throws Exception {
        byte[] encryptedBytes = Base64.decodeBase64(ciphertext);
        byte[] decryptedBytes = decryptAES(encryptedBytes);

        String decryptedText = new String(decryptedBytes);
        String decryptedSignature = calculateSignature(decryptedText, timestamp, nonce);

        if (!decryptedSignature.equals(signature)) {
            throw new Exception("Invalid signature");
        }

        return decryptedText;
    }

    private byte[] decryptAES(byte[] encryptedBytes) throws Exception {
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);

        AlgorithmParameters params = cipher.getParameters();
        IvParameterSpec ivSpec = params.getParameterSpec(IvParameterSpec.class);
        byte[] ivBytes = ivSpec.getIV();

        return cipher.doFinal(encryptedBytes);
    }