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

com.github.joekerouac.common.tools.crypto.impl.AesCipher Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.github.joekerouac.common.tools.crypto.impl;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Objects;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.github.joekerouac.common.tools.constant.Const;
import com.github.joekerouac.common.tools.constant.ExceptionProviderConst;
import com.github.joekerouac.common.tools.crypto.CipherSpi;
import com.github.joekerouac.common.tools.crypto.constant.CipherDesc;
import com.github.joekerouac.common.tools.crypto.exception.CryptoException;
import com.github.joekerouac.common.tools.string.StringUtils;
import com.github.joekerouac.common.tools.util.Assert;

/**
 * AES加密器
 * 
 * @since 1.0.0
 * @author JoeKerouac
 * @date 2022-10-14 14:37:00
 */
public class AesCipher implements CipherSpi {

    private static final String SUN_JCE_PROVIDER = "com.sun.crypto.provider.SunJCE";

    /**
     * 实际的加密器
     */
    public final Cipher cipher;

    /**
     * 加密器提供者
     */
    private final Provider provider;

    /**
     * 加密套件说明
     */
    private final CipherDesc cipherDesc;

    /**
     * 加解密模式
     */
    private int mode = -1;

    public AesCipher(CipherDesc cipherAlgorithm) {
        this(cipherAlgorithm, Const.BC_PROVIDER);
    }

    public AesCipher(CipherDesc cipherAlgorithm, Provider provider) {
        this.cipherDesc = Objects.requireNonNull(cipherAlgorithm);
        this.provider = provider == null ? Const.BC_PROVIDER : provider;

        try {
            this.cipher = Cipher.getInstance(cipherDesc.getCipherName(), this.provider);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new com.github.joekerouac.common.tools.crypto.exception.NoSuchAlgorithmException(
                cipherAlgorithm.getCipherName(), e);
        }
    }

    @Override
    public int getMode() {
        return mode;
    }

    @Override
    public void init(byte[] key, byte[] iv, int mode, SecureRandom secureRandom) {
        // 这里是固定的
        Assert.notNull(key, "key不能为null", ExceptionProviderConst.IllegalArgumentExceptionProvider);
        Assert.notNull(iv, "IV不能为null", ExceptionProviderConst.IllegalArgumentExceptionProvider);
        Assert.assertTrue(mode == ENCRYPT_MODE || mode == DECRYPT_MODE, "不支持的加解密模式:" + mode,
            ExceptionProviderConst.IllegalArgumentExceptionProvider);
        this.mode = mode;
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.clone(), "AES");
        try {
            if (cipherDesc.getIvLen() == 0) {
                cipher.init(mode, secretKeySpec, (IvParameterSpec)null, secureRandom);
            } else if ((provider.getClass().getName().equals(SUN_JCE_PROVIDER)) && cipherDesc.isGcm()) {
                // 注意,SunJCE提供的加密器如果是GCM模式,需要使用GCMParameterSpec
                byte[] realIv = iv.clone();
                GCMParameterSpec parameterSpec = new GCMParameterSpec(16 * 8, realIv);
                cipher.init(mode, secretKeySpec, parameterSpec, secureRandom);
            } else {
                IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
                cipher.init(mode, secretKeySpec, ivParameterSpec, secureRandom);
            }
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new CryptoException(StringUtils.format("key: [{}], iv: [{}], mode: [{}]", key, iv, mode), e);
        }
    }

    @Override
    public int getBlockSize() {
        return cipher.getBlockSize();
    }

    @Override
    public void updateAAD(byte[] data) {
        cipher.updateAAD(data);
    }

    @Override
    public byte[] doFinal() {
        try {
            return cipher.doFinal();
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new CryptoException(StringUtils.format("block size: [{}], algorithm: [{}]", cipher.getBlockSize(),
                cipherDesc.getCipherName()), e);
        }
    }

    @Override
    public int doFinal(byte[] data, int offset, int len, byte[] result, int resultOffset) {
        try {
            return cipher.doFinal(data, offset, len, result, resultOffset);
        } catch (IllegalBlockSizeException | BadPaddingException | ShortBufferException e) {
            throw new CryptoException(StringUtils.format("data len: [{}], block size: [{}], algorithm: [{}]", len,
                cipher.getBlockSize(), cipherDesc.getCipherName()), e);
        }
    }

    @Override
    public int update(byte[] data, int offset, int len, byte[] result, int resultOffset) {
        try {
            return cipher.update(data, offset, len, result, resultOffset);
        } catch (ShortBufferException e) {
            throw new CryptoException(StringUtils.format("data len: [{}], block size: [{}], algorithm: [{}]", len,
                cipher.getBlockSize(), cipherDesc.getCipherName()), e);
        }
    }

    @Override
    public int getOutputSize(int len) {
        // 对于javax.crypto.Cipher.getOutputSize来说,如果是加密模式,那么返回加密数据长度+tagLen,如果是解密模式,返回解密数据长度-tagLen
        return cipher.getOutputSize(len);
    }

    @Override
    public int getTagLen() {
        return cipherDesc.getTagLen();
    }

    @Override
    public String name() {
        return cipherDesc.getCipherName();
    }

    @Override
    public CipherSpi copy() throws CloneNotSupportedException {
        return new AesCipher(cipherDesc);
    }

    @Override
    public Provider provider() {
        return provider;
    }

    @Override
    public String[] alias() {
        return new String[] {cipherDesc.getCipherName()};
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy