加密算法 AES/CBC/PKCS5Padding 与 秘钥生成算法 PBKDF2WithHmacSHA256 示例

加密算法 AES/CBC/PKCS5Padding 与 秘钥生成算法 PBKDF2WithHmacSHA256 示例

说明

  1. 密钥由读取文件中的随机数和代码中的随机数拼接后使用派生算法PBKDF2WithHmacSHA256生成,迭代次数不小于10000次,salt不小于16字节,PBKDF的输出长度dkLen不小于256比特。
  2. 加密算法使用AES/CBC/PKCS5Padding,秘钥长度不小于256比特。

依赖

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.13.0</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.16.0</version>
        </dependency>

代码示例

  1. Crypt.class 使用encrypt与decrypt加解密
package com.run.first.encrypt;

import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class Crypt {
    private static final String ROOT_FILE = "31aa7864d551bba8";
    private static final String SECRET_FILE = "primary.ks";
    private static final int KEY_LEN = 32;
    private static final String KEY = "1e33af2116562bfc2aa486795bbd65466af61222037fa3154be64953331644a1";
    private static final String INIT_IV = "45b3af6865af411aac21e3abd9172826";
    private static final int ITERATIONS = 10000;
    private static final String AES = "AES";
    private static final String AES_CBC_PKCS_5_PADDING = "AES/CBC/PKCS5Padding";
    private static final String HMAC_SHA_256 = "HmacSHA256";
    private static final int AES_IV_LEN = 16;

    private final String rootKeyPath;
    private final String keyStorePath;
    private byte[] rootKey;
    private final Map<Integer, byte[]> keyStore = new HashMap<>();

    public Crypt(String keyStoreDir) throws CryptException {
        prepareKeySoreDir(keyStoreDir);
        this.rootKeyPath = keyStoreDir + File.separator + ROOT_FILE;
        this.keyStorePath = keyStoreDir + File.separator + SECRET_FILE;
        loadOrGenRootKey();
        loadOrGenKeyStore();
    }

    public String encrypt(String plain) throws CryptException {
        if (plain == null) {
            return null;
        }
        try {
            return String.valueOf(Hex.encodeHex(encryptRow(plain.getBytes(StandardCharsets.UTF_8), keyStore.get(0))));
        } catch (Exception e) {
            throw new CryptException(e.getMessage(), e);
        }
    }

    public String decrypt(String cipher) throws CryptException {
        if (cipher == null) {
            return null;
        }
        try {
            return new String(decryptRow(Hex.decodeHex(cipher.toCharArray()), keyStore.get(0)), Charsets.UTF_8);
        } catch (Exception e) {
            throw new CryptException(e.getMessage(), e);
        }
    }

    private void loadOrGenKeyStore() throws CryptException {
        File store = new File(keyStorePath);
        try {
            if (store.exists()) {
                loadKeyStore();
            } else {
                genKeyStore();
            }
        } catch (Exception e) {
            throw new CryptException(e.getMessage(), e);
        }
    }

    private void genKeyStore() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IllegalBlockSizeException, NoSuchPaddingException, IOException, BadPaddingException, InvalidKeyException {
        byte[] defaultKey = RandomBytesGen.randomBytesGenerate(KEY_LEN);
        keyStore.put(0, defaultKey);
        saveKeyStore();
    }

    private void saveKeyStore() throws IOException, InvalidAlgorithmParameterException, IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, InvalidKeyException {
        Properties properties = new Properties();
        for(Map.Entry<Integer, byte[]> entry: keyStore.entrySet()) {
            byte[] cipherKey = encryptRow(entry.getValue(), rootKey);
            properties.setProperty(entry.getKey().toString(), new String(Hex.encodeHex(cipherKey)));
        }
        writeProperties(new File(keyStorePath), properties);
    }

    private void loadKeyStore() throws IOException, DecoderException, CryptException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        Properties properties = Crypt.readProperties(new File(keyStorePath));
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            Integer domainId = Integer.parseInt(entry.getKey().toString());
            byte[] key = decryptRow(Hex.decodeHex(entry.getValue().toString()), rootKey);
            keyStore.put(domainId, key);
        }
    }


    private void loadOrGenRootKey() throws CryptException {
        try {
            this.rootKey = getRootKey(loadOrGenRootKeyRandomPart());
        } catch (Exception e) {
            throw new CryptException(e.getMessage(), e);
        }
    }


    private byte[] getRootKey(byte[] cfgKey) throws DecoderException, CryptException, InvalidKeySpecException, NoSuchAlgorithmException {
        byte[] c1 = Hex.decodeHex(KEY);
        byte[] c2 = Hex.decodeHex(CryptConst.KEY);
        byte[] c3 = cfgKey.clone();
        if (c1.length != KEY_LEN || c2.length != KEY_LEN || c3.length != KEY_LEN) {
            throw new CryptException("Generate root key fail, key len error");
        }
        char[] combined = new char[KEY_LEN];
        for (int i = 0; i < KEY_LEN; i++) {
            combined[i] = (char) (c1[i] ^ c2[i] ^ c3[i]);
        }
        return encryptPBKDF2WithSHA256(combined, Hex.decodeHex(INIT_IV));
    }


    private static byte[] encryptRow(byte[] content, byte[] key) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
        byte[] iv = RandomBytesGen.randomBytesGenerate(AES_IV_LEN);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS_5_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] result = cipher.doFinal(content);
        byte[] resultWithIv = new byte[result.length + AES_IV_LEN];
        System.arraycopy(iv, 0, resultWithIv, 0,AES_IV_LEN);
        System.arraycopy(result, 0, resultWithIv, AES_IV_LEN, result.length);
        return resultWithIv;
    }

    private static byte[] decryptRow(byte[] contentWithIv, byte[] key) throws CryptException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        if (contentWithIv.length <= AES_IV_LEN) {
            throw new CryptException("Cipher text too short");
        }
        byte[] iv = new byte[AES_IV_LEN];
        byte[] content = new byte[contentWithIv.length - AES_IV_LEN];
        System.arraycopy(contentWithIv, 0, iv, 0, AES_IV_LEN);
        System.arraycopy(contentWithIv, AES_IV_LEN, content, 0, content.length);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
        Cipher cipher = Cipher.getInstance(AES_CBC_PKCS_5_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(content);
    }

    private static byte[] digestRaw(byte[] content, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
        SecretKeySpec secretKey = new SecretKeySpec(key, HMAC_SHA_256);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        return mac.doFinal();
    }

    private static byte[] encryptPBKDF2WithSHA256(char[] key, byte[] salt) throws InvalidKeySpecException, NoSuchAlgorithmException {
        KeySpec spec = new PBEKeySpec(key, salt, Crypt.ITERATIONS, Crypt.KEY_LEN * 8);
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        return secretKeyFactory.generateSecret(spec).getEncoded();
    }

    private byte[] loadOrGenRootKeyRandomPart() throws NoSuchAlgorithmException, IOException {
        File root = new File(rootKeyPath);
        if (!root.exists()) {
            return genRootKeyRandomPart();
        }
        try (InputStream input = Files.newInputStream(root.toPath())) {
            return IOUtils.toByteArray(input);
        } catch (IOException e) {
            return genRootKeyRandomPart();
        }
    }

    private byte[] genRootKeyRandomPart() throws NoSuchAlgorithmException, IOException {
        byte[] cfgKey = RandomBytesGen.randomBytesGenerate(KEY_LEN);
        try (OutputStream output = Files.newOutputStream(Paths.get(rootKeyPath))) {
            IOUtils.writeChunked(cfgKey, output);
        }
        return cfgKey;
    }

    private void prepareKeySoreDir(String keyStoreDir) throws CryptException {
        File dir = new File(keyStoreDir);
        if (!dir.exists()) {
            throw new CryptException("create key store fail.");
        } else if (!dir.isDirectory()) {
            throw new CryptException("keyStoreDir is not a directory");
        }
    }

    private static Properties readProperties(File file) throws IOException {
        try (InputStream in = new FileInputStream(file)) {
            Properties properties = new Properties();
            properties.load(in);
            return properties;
        }
    }

    private static void writeProperties(File file, Properties properties) throws IOException {
        try (OutputStream out = new FileOutputStream(file)) {
            properties.store(out, "");
        }
    }
}

  1. CryptConst.class 秘钥
package com.run.first.encrypt;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class RandomBytesGen {
    public static byte[] randomBytesGenerate(int byteSize) throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstanceStrong();
        byte[] bytes = new byte[byteSize];
        random.nextBytes(bytes);
        random.nextBytes(bytes);
        return new byte[0];
    }
}

public class CryptConst {
    public static final String KEY = "3ac486a6c1644a1b7945c665cb37fe1541aa65b37fe34865af411aa5ec22f612";
}
  1. RandomBytesGen.class 与 CryptException.class
public class CryptException extends Exception {
    public CryptException(String message) {
        super(message);
    }

    public CryptException(String message, Exception e) {
        super(message, e);
    }
}

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
AES/CBC/PKCS7Padding是一种常用的加密算法,可用于对数据进行保护。在C语言中,可以通过使用OpenSSL库来实现该算法。 下面是一个使用AES/CBC/PKCS7Padding加密算法示例代码: ```c #include <openssl/aes.h> #include <openssl/rand.h> #define AES_BLOCK_SIZE 16 void encrypt(const unsigned char *plaintext, int plaintext_len, const unsigned char *key, const unsigned char *iv, unsigned char *ciphertext) { AES_KEY aesKey; AES_set_encrypt_key(key, 128, &aesKey); AES_cbc_encrypt(plaintext, ciphertext, plaintext_len, &aesKey, iv, AES_ENCRYPT); } void decrypt(const unsigned char *ciphertext, int ciphertext_len, const unsigned char *key, const unsigned char *iv, unsigned char *plaintext) { AES_KEY aesKey; AES_set_decrypt_key(key, 128, &aesKey); AES_cbc_encrypt(ciphertext, plaintext, ciphertext_len, &aesKey, iv, AES_DECRYPT); } int main() { unsigned char key[AES_BLOCK_SIZE] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; unsigned char iv[AES_BLOCK_SIZE]; unsigned char plaintext[32] = "Hello, AES/CBC/PKCS7Padding"; unsigned char ciphertext[sizeof(plaintext)]; unsigned char decryptedtext[sizeof(plaintext)]; RAND_bytes(iv, sizeof(iv)); encrypt(plaintext, sizeof(plaintext), key, iv, ciphertext); decrypt(ciphertext, sizeof(ciphertext), key, iv, decryptedtext); printf("Original Text: %s\n", plaintext); printf("Encrypted Text: %s\n", ciphertext); printf("Decrypted Text: %s\n", decryptedtext); return 0; } ``` 以上代码使用了OpenSSL库中的AES函数,分别实现了加密和解密的功能。主函数中生成了一个16字节的密钥和随机生成的16字节初始化向量。然后将明文进行加密,并将密文进行解密,最后打印出原始文本、加密文本和解密文本。 需要注意的是,加密后生成的密文长度要比原始明文长度稍长,因此ciphertext的长度使用了sizeof(plaintext)。为了避免截断问题,推荐使用静态大小的缓冲区来存储密文。 这只是一个简单的示例,实际应用中还需考虑安全性、密钥管理等因素。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值