All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.shoulder.crypto.negotiation.util.TransportCryptoByteUtil Maven / Gradle / Ivy

Go to download

Shoulder 提供的 协商加密模块,用于非信任网络环境下的安全通信。基于 DH + ECC 实现先进的加密算法协商算法,比传统的 DH + DES 协商算法性能显著更高,更安全。

There is a newer version: 1.0.0-M2.2
Show newest version
package org.shoulder.crypto.negotiation.util;

import cn.hutool.core.collection.ConcurrentHashSet;
import jakarta.annotation.Nullable;
import org.apache.commons.collections4.CollectionUtils;
import org.shoulder.core.constant.ByteSpecification;
import org.shoulder.core.util.ByteUtils;
import org.shoulder.crypto.asymmetric.AsymmetricCipher;
import org.shoulder.crypto.asymmetric.exception.AsymmetricCryptoException;
import org.shoulder.crypto.asymmetric.exception.KeyPairException;
import org.shoulder.crypto.negotiation.constant.NegotiationConstants;
import org.shoulder.crypto.negotiation.dto.NegotiationResult;
import org.shoulder.crypto.negotiation.exception.NegotiationException;
import org.shoulder.crypto.negotiation.support.dto.NegotiationRequest;
import org.shoulder.crypto.negotiation.support.dto.NegotiationResponse;
import org.shoulder.crypto.symmetric.SymmetricAlgorithmEnum;
import org.shoulder.crypto.symmetric.SymmetricCipher;
import org.shoulder.crypto.symmetric.exception.SymmetricCryptoException;
import org.shoulder.crypto.symmetric.impl.DefaultSymmetricCipher;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * 传输加解密相关实现。仅为 byte 提供服务
 *
 * @author lym
 */
public class TransportCryptoByteUtil {

    /**
     * 128/192/256,长度为 4 特用于优化随机数性能
     */
    private static final int[] SUPPORT_KEY_BYTE_LENGTH = {16, 16, 24, 32};
    /**
     * 数据密钥加密算法,这里写死
     */
    private static final SymmetricCipher KEY_CIPHER = DefaultSymmetricCipher.getFlyweight(SymmetricAlgorithmEnum.AES_CBC_PKCS5Padding.getAlgorithmName());

    /**
     * 支持的对称加密算法
     */
    public static final Set SUPPORT_ENCRYPTION_SCHEMES = new ConcurrentHashSet<>();

    /**
     * 非对称加密器,生成/保存自己的公私钥
     */
    private final AsymmetricCipher asymmetricCipher;
    /**
     * 服务端密钥协商缓存过期默认时间
     */
    private final Duration negotiationDuration = Duration.ofHours(1);

    static {
        // 默认值支持 AES CBC pkcs5padding
        SUPPORT_ENCRYPTION_SCHEMES.add(SymmetricAlgorithmEnum.AES_CBC_PKCS5Padding.getAlgorithmName());
    }

    public static synchronized void setSupportEncryptionSchemes(Set allSupportedEncryptionSchemes) {
        SUPPORT_ENCRYPTION_SCHEMES.clear();
        SUPPORT_ENCRYPTION_SCHEMES.addAll(allSupportedEncryptionSchemes);
    }

    /**
     * 构造器
     *
     * @param asymmetricCipher 非对称加密器,生成/保存自己的公私钥
     */
    public TransportCryptoByteUtil(AsymmetricCipher asymmetricCipher) {
        this.asymmetricCipher = asymmetricCipher;
    }

    /**
     * 生成一次请求使用的数据密钥
     * 使用时机:协商完毕,每次请求时
     *
     * @param length 密钥长度:16/24/32 (128/192/256)
     * @return 数据密钥 xDk
     */
    public static byte[] generateDataKey(int length) {
        return ByteUtils.randomBytes(length);
    }

    /**
     * 生成数据密钥的密文(DK)
     */
    public static byte[] encryptDk(NegotiationResult negotiationResult, byte[] dataKeyPlain) throws SymmetricCryptoException {
        return KEY_CIPHER.encrypt(negotiationResult.getShareKey(), negotiationResult.getLocalIv(), dataKeyPlain);
    }

    /**
     * 解密 xDk(用于协商完毕,每次请求中)
     *
     * @return dataKey
     */
    public static byte[] decryptDk(NegotiationResult negotiationResult, byte[] xDk) throws SymmetricCryptoException {
        return KEY_CIPHER.decrypt(negotiationResult.getShareKey(), negotiationResult.getLocalIv(), xDk);
    }

    /**
     * 加密数据(具体加密算法根据协商确定)
     */
    public static byte[] encrypt(NegotiationResult negotiationResult, byte[] dataKey, byte[] toCipher) throws SymmetricCryptoException {
        SymmetricCipher cipher = DefaultSymmetricCipher.getFlyweight(negotiationResult.getEncryptionScheme());
        return cipher.encrypt(dataKey, negotiationResult.getLocalIv(), toCipher);
    }

    /**
     * 解密数据
     */
    public static byte[] decrypt(NegotiationResult negotiationResult, byte[] dataKey, byte[] cipherText) throws SymmetricCryptoException {
        SymmetricCipher cipher = DefaultSymmetricCipher.getFlyweight(negotiationResult.getEncryptionScheme());
        return cipher.decrypt(dataKey, negotiationResult.getLocalIv(), cipherText);
    }

    /**
     * 简单随机算法,50% 128,25%192,25%256
     *
     * @return 128/192/256
     */
    private static int randomKeyLength() {
        return SUPPORT_KEY_BYTE_LENGTH[ThreadLocalRandom.current().nextInt(SUPPORT_KEY_BYTE_LENGTH.length)];
    }

    /**
     * 创建一个协商请求
     */
    public NegotiationRequest createRequest() throws AsymmetricCryptoException {
        NegotiationRequest request = new NegotiationRequest();
        // 生成会话唯一标识
        String clientSessionId = UUID.randomUUID().toString().replaceAll("-", "");
        request.setXSessionId(clientSessionId);
        asymmetricCipher.buildKeyPair(clientSessionId, negotiationDuration);
        request.setPublicKey(ByteSpecification.encodeToString(asymmetricCipher.getPublicKey(clientSessionId).getEncoded()));
        request.setEncryptionSchemeSupports(SUPPORT_ENCRYPTION_SCHEMES);
        request.setRefresh(false);
        request.setToken(ByteSpecification.encodeToString(generateRequestToken(request)));
        return request;
    }

    /**
     * 协商密钥交换响应(发起方、处理方都会调用)
     * 主要是获得相同的密钥与 iv
     */
    public NegotiationResult negotiation(NegotiationResponse negotiationResponse) throws KeyPairException, NegotiationException {
        byte[] selfPrivateKey = asymmetricCipher.getPrivateKey(negotiationResponse.getXSessionId()).getEncoded();
        byte[] otherPublicKey = ByteSpecification.decodeToBytes(negotiationResponse.getPublicKey());
        List keyAndIv = ECDHUtils.negotiationToKeyAndIv(selfPrivateKey, otherPublicKey, negotiationResponse.getKeyBytesLength());

        NegotiationResult result = new NegotiationResult();
        result.setShareKey(keyAndIv.get(0));
        result.setLocalIv(keyAndIv.get(1));
        result.setOtherPublicKey(otherPublicKey);
        result.setXSessionId(negotiationResponse.getXSessionId());
        result.setEncryptionScheme(negotiationResponse.getEncryptionScheme());
        result.setKeyLength(negotiationResponse.getKeyBytesLength());

        long expireTimePoint = System.currentTimeMillis() + negotiationResponse.getExpireTime();
        result.setExpireTime(expireTimePoint);
        return result;
    }

    // =========================== 防篡改 ==================================

    /**
     * 服务端根据协商请求准备协商参数:确定加密算法、密钥长度、协商有效期
     */
    public NegotiationResponse prepareNegotiation(NegotiationRequest negotiationRequest) throws AsymmetricCryptoException {
        // 这时候还没有缓存,因此需要生成
        String xSessionId = negotiationRequest.getXSessionId();
        asymmetricCipher.buildKeyPair(xSessionId, negotiationDuration);
        byte[] selfPublicKey = asymmetricCipher.getPublicKey(xSessionId).getEncoded();

        NegotiationResponse response = new NegotiationResponse();
        response.setXSessionId(xSessionId);
        response.setExpireTime(NegotiationConstants.EXPIRE_TIME);
        response.setPublicKey(ByteSpecification.encodeToString(selfPublicKey));
        // 根据协商请求,选择自己支持的算法
        List shareAlgorithms = CollectionUtils.emptyIfNull(negotiationRequest.getEncryptionSchemeSupports()).stream()
                .filter(SUPPORT_ENCRYPTION_SCHEMES::contains)
            .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(shareAlgorithms)) {
            throw new IllegalStateException("There is no common algorithm!");
        }
        // 这里未使用 findAny(),因为 findAny 在短 list 中往往是第一个(最高性能地获取),不具有随机性
        String shareAlgorithm = shareAlgorithms.get(ThreadLocalRandom.current().nextInt(shareAlgorithms.size()));
        response.setEncryptionScheme(shareAlgorithm);
        response.setKeyBytesLength(randomKeyLength());
        response.setToken(ByteSpecification.encodeToString(generateResponseToken(response)));

        return response;
    }

    /**
     * 发起协商请求时需要签名的数据
     *
     * @param request 协商请求
     * @return 需要签名的数据
     */
    private byte[] getNeedToSign(NegotiationRequest request) {
        byte[] xSessionIdBytes = request.getXSessionId().getBytes(ByteSpecification.STD_CHAR_SET);
        byte[] publicKeyBytes = ByteSpecification.decodeToBytes(request.getPublicKey());
        return ByteUtils.compound(Arrays.asList(xSessionIdBytes, publicKeyBytes));
    }

    /**
     * 生成 token(发起协商请求时)
     */
    public byte[] generateRequestToken(NegotiationRequest request) throws AsymmetricCryptoException {
        return asymmetricCipher.sign(request.getXSessionId(), getNeedToSign(request));
    }

    // -------------------------------

    /**
     * 验签,防篡改(处理协商请求时)
     */
    public boolean verifyRequestToken(NegotiationRequest request) throws AsymmetricCryptoException {
        byte[] signature = ByteSpecification.decodeToBytes(request.getToken());
        return asymmetricCipher.verify(
            ByteSpecification.decodeToBytes(request.getPublicKey()),
            getNeedToSign(request),
            signature
        );
    }

    /**
     * 响应需要签名的数据
     *
     * @param response 响应
     * @return 需要签名的数据
     */
    private byte[] getNeedToSign(NegotiationResponse response) {
        byte[] xSessionIdBytes = response.getXSessionId().getBytes(ByteSpecification.STD_CHAR_SET);
        byte[] publicKeyBytes = ByteSpecification.decodeToBytes(response.getPublicKey());
        byte[] aesBytes = response.getEncryptionScheme().getBytes(ByteSpecification.STD_CHAR_SET);
        byte[] aesKeyLength = ByteUtils.intToBytes(response.getKeyBytesLength());
        byte[] expireTimeBytes = ByteUtils.intToBytes(response.getExpireTime());
        return ByteUtils.compound(Arrays.asList(xSessionIdBytes, publicKeyBytes, aesBytes, aesKeyLength, expireTimeBytes));
    }

    /**
     * 生成 token(协商响应时)
     */
    public byte[] generateResponseToken(NegotiationResponse response) throws AsymmetricCryptoException {
        return asymmetricCipher.sign(response.getXSessionId(), getNeedToSign(response));
    }

    // -------------------------------

    /**
     * 验签,防篡改(确认协商响应请求时)
     */
    public boolean verifyResponseToken(NegotiationResponse response) throws AsymmetricCryptoException {
        byte[] signature = ByteSpecification.decodeToBytes(response.getToken());
        return asymmetricCipher.verify(
            ByteSpecification.decodeToBytes(response.getPublicKey()),
            getNeedToSign(response),
            signature
        );
    }

    /**
     * 生成 token(协商完毕,每次发送安全会话请求时)
     *
     * @param xDk 每次请求的临时密钥密文
     */
    public byte[] generateToken(String xSessionId, @Nullable byte[] xDk) throws AsymmetricCryptoException {
        byte[] xSessionIdBytes = xSessionId.getBytes(ByteSpecification.STD_CHAR_SET);
        byte[] toSin = ByteUtils.compound(Arrays.asList(xSessionIdBytes, xDk));
        return asymmetricCipher.sign(xSessionId, toSin);
    }

    /**
     * 验签,防篡改(协商完毕,每次处理请求时)
     *
     * @param xSessionId 安全会话标识(密钥交换缓存 key )
     * @param xDk        临时数据密钥密文
     * @param signature  签名
     */
    public boolean verifyToken(String xSessionId, byte[] xDk, byte[] signature, byte[] otherPublicKey) throws AsymmetricCryptoException {
        byte[] xSessionIdBytes = xSessionId.getBytes(ByteSpecification.STD_CHAR_SET);
        byte[] toSin = ByteUtils.compound(Arrays.asList(xSessionIdBytes, xDk));
        return asymmetricCipher.verify(otherPublicKey, toSin, signature);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy