Java AES加密如何使用zeropadding方式填充

前一久,在对接支付通道时,遇到上游使用AES加密方式,对方要求加密时使用CBC模式,zeropadding填充,偏移量为0000*4(即16个0),输出十六进制,字符集使用UTF-8。

本以为也没什么问题,可到实际开发时却发现Java虽然支持AES的CBC模式,但填充方式却没有zeropadding模式。通过查看文档,先梳理一下加密算法相关的知识。

JDK1.8支持的加密算法:

Cipher Algorithm Names

The following names can be specified as the algorithm component in a transformation when requesting an instance of Cipher.

Algorithm NameDescription
AES

Advanced Encryption Standard as specified by NIST in FIPS 197. Also known as the Rijndael algorithm by Joan Daemen and Vincent Rijmen, AES is a 128-bit block cipher supporting keys of 128, 192, and 256 bits.

To use the AES cipher with only one valid key size, use the format AES_<n>, where <n> can be 128, 192, or 256.

AESWrap

The AES key wrapping algorithm as described in RFC 3394.

To use the AESWrap cipher with only one valid key size, use the format AESWrap_<n>, where <n> can be 128, 192, or 256.

ARCFOURA stream cipher believed to be fully interoperable with the RC4 cipher developed by Ron Rivest. For more information, see K. Kaukonen and R. Thayer, "A Stream Cipher Encryption Algorithm 'Arcfour'", Internet Draft (expired), draft-kaukonen-cipher-arcfour-03.txt.
BlowfishThe Blowfish block cipher designed by Bruce Schneier.
DESThe Digital Encryption Standard as described in FIPS PUB 46-3.
DESedeTriple DES Encryption (also known as DES-EDE, 3DES, or Triple-DES). Data is encrypted using the DES algorithm three separate times. It is first encrypted using the first subkey, then decrypted with the second subkey, and encrypted with the third subkey.
DESedeWrapThe DESede key wrapping algorithm as described in RFC 3217 .
ECIESElliptic Curve Integrated Encryption Scheme
PBEWith<digest>And<encryption> PBEWith<prf>And<encryption>The password-based encryption algorithm found in (PKCS5), using the specified message digest (<digest>) or pseudo-random function (<prf>) and encryption algorithm (<encryption>). Examples:
RC2Variable-key-size encryption algorithms developed by Ron Rivest for RSA Data Security, Inc.
RC4Variable-key-size encryption algorithms developed by Ron Rivest for RSA Data Security, Inc. (See note prior for ARCFOUR.)
RC5Variable-key-size encryption algorithms developed by Ron Rivest for RSA Data Security, Inc.
RSAThe RSA encryption algorithm as defined in PKCS #1

支持的加密算法模式:

Cipher Algorithm Modes

The following names can be specified as the mode component in a transformation when requesting an instance of Cipher.

Algorithm NameDescription
NONENo mode.
CBCCipher Block Chaining Mode, as defined in FIPS PUB 81.
CCMCounter/CBC Mode, as defined in NIST Special Publication SP 800-38C.
CFB, CFBxCipher Feedback Mode, as defined in FIPS PUB 81.

Using modes such as CFB and OFB, block ciphers can encrypt data in units smaller than the cipher's actual block size. When requesting such a mode, you may optionally specify the number of bits to be processed at a time by appending this number to the mode name as shown in the "DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations. If no such number is specified, a provider-specific default is used. (For example, the SunJCE provider uses a default of 64 bits for DES.) Thus, block ciphers can be turned into byte-oriented stream ciphers by using an 8-bit mode such as CFB8 or OFB8.
CTRA simplification of OFB, Counter mode updates the input block as a counter.
CTSCipher Text Stealing, as described in Bruce Schneier's book Applied Cryptography-Second Edition, John Wiley and Sons, 1996.
ECBElectronic Codebook Mode, as defined in FIPS PUB 81 (generally this mode should not be used for multiple blocks of data).
GCMGalois/Counter Mode, as defined in NIST Special Publication SP 800-38D.
OFB, OFBxOutput Feedback Mode, as defined in FIPS PUB 81.

Using modes such as CFB and OFB, block ciphers can encrypt data in units smaller than the cipher's actual block size. When requesting such a mode, you may optionally specify the number of bits to be processed at a time by appending this number to the mode name as shown in the "DES/CFB8/NoPadding" and "DES/OFB32/PKCS5Padding" transformations. If no such number is specified, a provider-specific default is used. (For example, the SunJCE provider uses a default of 64 bits for DES.) Thus, block ciphers can be turned into byte-oriented stream ciphers by using an 8-bit mode such as CFB8 or OFB8.
PCBCPropagating Cipher Block Chaining, as defined by Kerberos V4.

这里简单说一下加密算法模式,不要把它与加密算法混淆起来,加密算法就是使用何种方式加密,比如:DES、AES、RSA,而加密算法模式,是用来描述加密算法(此处特指分组密码,不包括流密码,)在加密时对明文分组的模式,它代表了不同的分组方式,如常见的:

ECB模式:电子密码本模式

CBC模式:密码分组连接模式

CFB模式:密文反馈模式

OFB模式:输出反馈模式

CTR模式:计数器模式

感兴趣的童鞋可以自行查找资料研究。

支持的加密算法填充方式:

Cipher Algorithm Padding

The following names can be specified as the padding component in a transformation when requesting an instance of Cipher.

Algorithm NameDescription
NoPaddingNo padding.
ISO10126PaddingThis padding for block ciphers is described in 5.2 Block Encryption Algorithms in the W3C's "XML Encryption Syntax and Processing" document.
OAEPPadding, OAEPWith<digest>And<mgf>PaddingOptimal Asymmetric Encryption Padding scheme defined in PKCS1, where <digest> should be replaced by the message digest and <mgf> by the mask generation function. Examples: OAEPWithMD5AndMGF1Padding and OAEPWithSHA-512AndMGF1Padding.

If OAEPPadding is used, Cipher objects are initialized with a javax.crypto.spec.OAEPParameterSpec object to supply values needed for OAEPPadding.
PKCS1PaddingThe padding scheme described in PKCS #1, used with the RSA algorithm.
PKCS5PaddingThe padding scheme described in RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," version 1.5, November 1993.
SSL3PaddingThe padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher):
    block-ciphered struct {
        opaque content[SSLCompressed.length];
        opaque MAC[CipherSpec.hash_size];
        uint8 padding[
            GenericBlockCipher.padding_length];
        uint8 padding_length;
    } GenericBlockCipher;
The size of an instance of a GenericBlockCipher must be a multiple of the block cipher's block length.

The padding length, which is always present, contributes to the padding, which implies that if:
    sizeof(content) + sizeof(MAC) % block_length = 0, 
padding has to be (block_length - 1) bytes long, because of the existence of padding_length.

This makes the padding scheme similar (but not quite) to PKCS5Padding, where the padding length is encoded in the padding (and ranges from 1 to block_length). With the SSL scheme, the sizeof(padding) is encoded in the always present padding_length and therefore ranges from 0 to block_length-1.

说一下什么是填充方式,填充方式是在分组密码中,当明文长度不是分组长度的整数倍时,需要在最后一个分组中填充一些数据使其凑满一个分组的长度。

看到这,发现根本没有对方要求的zeropadding填充方式,那咋整?还有偏移量是什么鬼?此处。。。。烦躁

最后,查找到一个类似的案例解决了此问题,事后也查了下相关资料,现在先来回答上面两个问题,之后贴出代码。

一、zeropadding填充方式,它是使用“0”作为填充数据的填充方式,也就是说在分组时,最后一组明文的长度没有达到分组长度,那么就用“0”来补足。.Net 平台提供了这种方式,但Java没有提供,但Java提供NoPadding这中方式,意思是不做填充,这肯定不行,那怎么办?其实要的就是你不填充,这样我才能自己填充,我想填什么就填什么,如何填充呢?

二、偏移量,也叫初始化向量(IV),这个概念涉及到CBC模式的原理,那就顺便说一下,方便大家理解,CBC模式在对明文分组加密时,会将明文分组与前一个密文分组进行XOR运算(即异或运算),但是加密第一个明文分组时不存在“前一个密文分组”,因此需要事先准备一个与分组长度相等的比特序列来代替,这个比特序列就是偏移量。

好了,明白这了这两个概念,那么下面就是根据zeropadding填充规则和对方要求的偏移量来自己用代码实现了,直接上代码

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESEncrypt {

	public static String encrypt(String data, String key) {

        // 偏移量
		String ivString = "0000000000000000";
                
		byte[] iv = ivString.getBytes();
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
			int blockSize = cipher.getBlockSize();
			byte[] dataBytes = data.getBytes();
			int length = dataBytes.length;
            // 计算需填充长度
			if (length % blockSize != 0) {
				length = length + (blockSize - (length % blockSize));
			}
			byte[] plaintext = new byte[length];
            // 填充
			System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
			SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
            // 设置偏移量参数
			IvParameterSpec ivSpec = new IvParameterSpec(iv);
			cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
			byte[] encryped = cipher.doFinal(plaintext);

			return parseByte2HexStr(encryped);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}

	public static String desEncrypt(String data, String key) {

		String ivString = "0000000000000000";
		byte[] iv = ivString.getBytes();

		try {
			byte[] encryp = parseHexStr2Byte(data);
			Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
			SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
			IvParameterSpec ivSpec = new IvParameterSpec(iv);
			cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
			byte[] original = cipher.doFinal(encryp);
			return new String(original);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return null;
	}

	public static String parseByte2HexStr(byte[] buf) {
		StringBuffer sb = new StringBuffer();

		for (int i = 0; i < buf.length; ++i) {
			String hex = Integer.toHexString(buf[i] & 255);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			sb.append(hex.toUpperCase());
		}

		return sb.toString();
	}

	public static byte[] parseHexStr2Byte(String hexStr) {
		if (hexStr.length() < 1) {
			return null;
		} else {
			byte[] result = new byte[hexStr.length() / 2];

			for (int i = 0; i < hexStr.length() / 2; ++i) {
				int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
				int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
				result[i] = (byte) (high * 16 + low);
			}

			return result;
		}
	}

	public static void main(String[] args) {
		String data = "123456";
		String key = "186751244B391A6DCA84778E0D6A8910";
		String encrypt = encrypt(data, key);
		System.out.println("加密前:" + data);
		System.out.println("加密后:" + encrypt);
		String desEncrypt = desEncrypt(encrypt, key);
		System.out.println("解密后:" + desEncrypt);
	}

}

到这里也算完了,其实对加密这一块大部分人都不是特别熟悉,基本处在会用得阶段,我也一样,但这一块其实还挺有意思的,以后有空要研究一下。

  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Java使用 Zero Padding 填充可以使用 javax.crypto.Cipher 类的 `getInstance` 方法中传入参数 "AES/ECB/NoPadding",然后在加密和解密时使用 `doFinal` 方法即可。 以下是使用 Zero Padding 填充Java 代码示例: ```java import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class AESUtil { // 加密 public static String encrypt(String data, String key) throws Exception { // 创建 AES 密钥 SecretKeySpec aesKey = new SecretKeySpec(key.getBytes(), "AES"); // 创建 Cipher 对象 Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, aesKey); // 对数据进行 Zero Padding 填充 byte[] dataBytes = data.getBytes(); int length = dataBytes.length; int paddingLength = 16 - length % 16; byte[] paddingData = new byte[length + paddingLength]; System.arraycopy(dataBytes, 0, paddingData, 0, length); // 加密数据 byte[] encrypted = cipher.doFinal(paddingData); // 将加密结果进行 Base64 编码并返回 return Base64.getEncoder().encodeToString(encrypted); } // 解密 public static String decrypt(String data, String key) throws Exception { // 创建 AES 密钥 SecretKeySpec aesKey = new SecretKeySpec(key.getBytes(), "AES"); // 创建 Cipher 对象 Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, aesKey); // 解密数据 byte[] encrypted = Base64.getDecoder().decode(data); byte[] decrypted = cipher.doFinal(encrypted); // 去除 Zero Padding 填充 int paddingLength = 0; for (int i = decrypted.length - 1; i >= 0; i--) { if (decrypted[i] == 0) { paddingLength++; } else { break; } } byte[] original = new byte[decrypted.length - paddingLength]; System.arraycopy(decrypted, 0, original, 0, original.length); // 将解密结果转为字符串并返回 return new String(original, "UTF-8"); } } ``` 在使用时,可以调用 `encrypt` 方法对数据进行加密,调用 `decrypt` 方法对数据进行解密,示例如下: ```java public static void main(String[] args) throws Exception { String data = "Hello, world!"; // 待加密数据 String key = "1234567890123456"; // 加密密钥 String encrypted = AESUtil.encrypt(data, key); System.out.println(encrypted); // 输出加密后的数据 String decrypted = AESUtil.decrypt(encrypted, key); System.out.println(decrypted); // 输出解密后的数据 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值