首页 > 编程语言 >2、【java数据安全】base64与报文摘要MD(md5、sha、mac)简单介绍及应用场景、示例

2、【java数据安全】base64与报文摘要MD(md5、sha、mac)简单介绍及应用场景、示例

时间:2023-06-20 14:32:50浏览次数:62  
标签:MD Exception String 示例 public 数据安全 byte data throws

(文章目录)


本文简单的介绍了Base64、消息摘要和其使用示例,并且使用示例以三种不同实现方式及测试 本文介绍三种实现方式,即JDK、apache commons.codec和bouncycastle三种。

一、maven依赖

<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>6.9.10</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.11</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.60</version>
		</dependency>

二、Base64

1、概念

内容传送编码是一种以任意8位字节列组合的描述形式,这种形式不容易被人直接识别。 经过base64编码后的数据会比原来数据长约1/3,经过base64编码后的字符串的字符数是以4为单位的整数倍。 实现base64的算法推荐使用apache的common codec类库。

2、应用场景

电子邮件传输、网络数据传输、密钥存储、数字证书存储。

3、示例

1)、JDK示例

1、实现源码

import java.util.Base64;

/**
 * @author alan 2018年11月15日 jdk
 */
public class Base64Coder {
	/**
	 * 字符编码
	 */
	public final static String ENCODING = "UTF-8";

	/**
	 * Base64编码
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static String encode(String data) throws Exception {
		Base64.Encoder encoder = Base64.getEncoder();
		byte[] b = data.getBytes(ENCODING);
		return encoder.encodeToString(b);
	}

	/**
	 * Base64解码
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static String decode(String data) throws Exception {
		Base64.Decoder decoder = Base64.getDecoder();
		byte[] b = decoder.decode(data);
		return new String(b, ENCODING);
	}
}

2、testNG测试

import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class Base64CoderTest {
	@Test
	public void test() throws Exception {
		String inputStr = "Java加密与解密";
		// 进行Base64编码
		String code = Base64Coder.encode(inputStr);
		System.err.println("Base64编码后:\n\t" + code);

		// 进行Base64解码
		String outputStr = Base64Coder.decode(code);
		System.err.println("Base64解码后:\n\t" + outputStr);

		// 验证Base64编码解码一致性
		assertEquals(inputStr, outputStr);
	}

	@Test
	public void demo() throws Exception {
		String str = "Java加密与解密";
		// Base64编码
		String data = Base64Coder.encode(str);
		System.err.println("编码后:\n\t" + data);

		// Base64解码
		String output = Base64Coder.decode(data);
		System.err.println("解码后:\n\t" + output);
	}
}

2)、bouncycastle示例

1、实现源码

import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.UrlBase64;

/**
 * @author alan 2018年11月15日
 */
public class Base64Coder {
	public final static String ENCODING = "UTF-8";

	/**
	 * Base64编码
	 * 
	 * @param data 待编码数据
	 * @return String 编码数据
	 * @throws Exception
	 */
	public static String encode(String data) throws Exception {
		// 执行编码
		byte[] b = Base64.encode(data.getBytes(ENCODING));
		return new String(b, ENCODING);
	}

	/**
	 * Base64解码
	 * 
	 * @param data 待解码数据
	 * @return String 解码数据
	 * @throws Exception
	 */
	public static String decode(String data) throws Exception {
		// 执行解码
		byte[] b = Base64.decode(data.getBytes(ENCODING));
		return new String(b, ENCODING);
	}

	/**
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static String urlEncode(String data) throws Exception {
		// 执行编码
		byte[] b = UrlBase64.encode(data.getBytes(ENCODING));
		return new String(b, ENCODING);
	}

	/**
	 * 
	 * @param data
	 * @return
	 * @throws Exception
	 */
	public static String urlDecode(String data) throws Exception {
		// 执行编码
		byte[] b = UrlBase64.encode(data.getBytes(ENCODING));
		return new String(b, ENCODING);
	}

}

2、testNG测试

import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class Base64CoderTest {
	@Test
	public final void test() throws Exception {
		String inputStr = "Java加密与解密";
		// 进行Base64编码
		String code = Base64Coder.encode(inputStr);
		System.err.println("编码后:\t" + code);
		// 进行Base64解码
		String outputStr = Base64Coder.decode(code);
		System.err.println("解码后:\t" + outputStr);
		// 验证Base64编码解码一致性
		assertEquals(inputStr, outputStr);

	}

	@Test
	public final void demo() throws Exception {
		String str = "Base64 编码";
		// Base64编码
		String data = Base64Coder.encode(str);
		System.err.println("编码后:\t" + new String(data));

		// Base64解码
		String output = Base64Coder.decode(data);
		System.err.println("解码后:\t" + new String(output));

	}

	@Test
	public final void demo2() throws Exception {
		String str = "Base64 编码";
		// Url Base64编码
		String data = Base64Coder.urlEncode(str);
		System.err.println("编码后:\t" + new String(data));

		// Url Base64解码
		String output = Base64Coder.urlDecode(data);
		System.err.println("解码后:\t" + new String(output));
	}
}

3)、apache commons示例

1、实现源码

import org.apache.commons.codec.binary.Base64;

/**
 * @author alan 2018年11月15日
 */
public class Base64Coder {
	public final static String ENCODING = "UTF-8";

	/**
	 * Base64编码
	 * 
	 * @param data 待编码数据
	 * @return String 编码数据
	 * @throws Exception
	 */
	public static String encode(String data) throws Exception {
		// 执行编码
		byte[] b = Base64.encodeBase64(data.getBytes(ENCODING));
		return new String(b, ENCODING);
	}

	/**
	 * Base64安全编码<br>
	 * 遵循RFC 2045实现
	 * 
	 * @param data 待编码数据
	 * @return String 编码数据
	 * 
	 * @throws Exception
	 */
	public static String encodeSafe(String data) throws Exception {
		// 执行编码
		byte[] b = Base64.encodeBase64(data.getBytes(ENCODING), true);
		return new String(b, ENCODING);
	}

	/**
	 * Base64解码
	 * 
	 * @param data 待解码数据
	 * @return String 解码数据
	 * @throws Exception
	 */
	public static String decode(String data) throws Exception {
		// 执行解码
		byte[] b = Base64.decodeBase64(data.getBytes(ENCODING));
		return new String(b, ENCODING);
	}
}

2、testNG测试

import static org.testng.Assert.assertEquals;

import org.apache.commons.codec.binary.Base64;
import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class Base64CoderTest {
	@Test
	public final void test() throws Exception {
		String inputStr = "Java加密与解密";

		// 进行Base64编码
		String code = Base64Coder.encode(inputStr);
		System.err.println("编码后:\t" + code);

		// 进行Base64解码
		String outputStr = Base64Coder.decode(code);
		System.err.println("解码后:\t" + outputStr);

		// 验证Base64编码解码一致性
		assertEquals(inputStr, outputStr);
	}

	@Test
	public final void testSafe() throws Exception {
		String inputStr = "Java加密与解密";

		// 进行Base64编码
		String code = Base64Coder.encodeSafe(inputStr);
		System.err.println("编码后:\t" + code);

		// 进行Base64解码
		String outputStr = Base64Coder.decode(code);
		System.err.println("解码后:\t" + outputStr);

		// 验证Base64编码解码一致性
		assertEquals(inputStr, outputStr);
	}

	@Test
	public final void demo() throws Exception {
		String str = "Base64 编码1";

		// Base64编码
		String data = Base64Coder.encode(str);
		System.err.println("编码后:\t" + new String(data));

		// Base64解码
		String output = Base64Coder.decode(data);
		System.err.println("解码后:\t" + new String(output));
	}

	@Test
	public final void demo2() throws Exception {
		String str = "Base64 编码2";
		byte[] input = str.getBytes();

		// Base64编码
		byte[] data = Base64.encodeBase64(input);
		System.err.println("编码后:\t" + new String(data));

		// Base64解码
		byte[] output = Base64.decodeBase64(data);
		System.err.println("解码后:\t" + new String(output));

	}

	@Test
	public final void demo3() throws Exception {
		String str = "Base64 编码3";

		// Base64编码
		String data = Base64Coder.encodeSafe(str);
		System.err.println("编码后:\t" + new String(data));

		// Base64解码
		byte[] output = Base64.decodeBase64(data);
		System.err.println("解码后:\t" + new String(output));

	}
}

三、消息摘要(message digest MD)

1、介绍

任何消息经过散列函数处理后都会获得唯一的散列值(hashcode),该过程称为消息摘要,其散列值成为数字指纹,其算法即是消息摘要算法。

消息摘要算法又称为散列算法,其核心在于散列函数的单向性,即通过散列函数可获得对应的散列值,但不可通过该散列值获得其原始信息。

消息摘要算法包含三大系列,即MD 、SHA 和MC,常用于验证数据的完整性,是数字签名的核心算法。

  • MD,message digest,消息摘要算法,包括MD2、MD4、MD5
  • SHA,secure hash algorithm,安全散列算法,包括SHA-224、SHA-256、SHA-384、SHA-512
  • MAC,message authentication code,消息认证码算法,综合了MD和SHA算法,包括HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384、HmacSHA512

2、MD

1)、介绍

MD5是由MD2、MD3、MD4改进而来,是典型的消息摘要算法。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。如果将这个128位的信息摘要信息换算成十六进制,则可以得到一个32位的字符串(32位的数字字母混合码)。

2)、应用场景

MD5之后的推荐替代应该是SHA,已经不适合安全性要求较高的场景。

3)、示例

1、jdk示例

  • 实现源码
import java.security.MessageDigest;

/**
 * @author alan 2018年11月15日 jdk
 */
public class MDCoder {
	public static byte[] encodeMD2(byte[] data) throws Exception {
		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("MD2");
		// 执行消息摘要
		return md.digest(data);
	}

	public static byte[] encodeMD5(byte[] data) throws Exception {
		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("MD5");
		// 执行消息摘要
		return md.digest(data);
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class MDCoderTest {
	@Test
	public final void testEncodeMD2() throws Exception {
		String str = "MD2消息摘要";

		// 获得摘要信息
		byte[] data1 = MDCoder.encodeMD2(str.getBytes());
		byte[] data2 = MDCoder.encodeMD2(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeMD5() throws Exception {
		String str = "MD5消息摘要";

		// 获得摘要信息
		byte[] data1 = MDCoder.encodeMD5(str.getBytes());
		byte[] data2 = MDCoder.encodeMD5(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}
}

2、bouncycastle示例

  • 实现源码
import java.security.MessageDigest;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

/**
 * @author alan 2018年11月15日
 */
public class MD4Coder {
	public static byte[] encodeMD4(byte[] data) throws Exception {
		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("MD4");

		// 执行消息摘要
		return md.digest(data);
	}

	public static String encodeMD4Hex(byte[] data) throws Exception {
		// 执行消息摘要
		byte[] b = encodeMD4(data);

		// 做十六进制编码处理
		return new String(Hex.encode(b));
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class MD4CoderTest {
	@Test
	public final void testEncodeMD4() throws Exception {
		String str = "MD4消息摘要";

		// 获得摘要信息
		byte[] data1 = MD4Coder.encodeMD4(str.getBytes());
		byte[] data2 = MD4Coder.encodeMD4(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeMD4Hex() throws Exception {
		String str = "MD4Hex消息摘要";

		// 获得摘要信息
		String data1 = MD4Coder.encodeMD4Hex(str.getBytes());
		String data2 = MD4Coder.encodeMD4Hex(str.getBytes());

		System.err.println("原文:\t" + str);
		System.err.println("MD4Hex-1:\t" + data1);
		System.err.println("MD4Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}
}

3、apache commons示例

  • 实现源码
import org.apache.commons.codec.digest.DigestUtils;

/**
 * @author alan 2018年11月15日
 */
public class MD5Coder {
	public static byte[] encodeMD5(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.md5(data);
	}

	public static String encodeMD5Hex(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.md5Hex(data);
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class MD5CoderTest {
	@Test
	public final void testEncodeMD5() throws Exception {
		String str = "MD5消息摘要";

		// 获得摘要信息
		byte[] data1 = MD5Coder.encodeMD5(str);
		byte[] data2 = MD5Coder.encodeMD5(str);

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeMD5Hex() throws Exception {
		String str = "MD5Hex消息摘要";

		// 获得摘要信息
		String data1 = MD5Coder.encodeMD5Hex(str);
		String data2 = MD5Coder.encodeMD5Hex(str);

		System.err.println("原文:\t" + str);
		System.err.println("MD5Hex-1:\t" + data1);
		System.err.println("MD5Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}
}

3、SHA

1)、介绍

SHA算法是基于MD4算法的,已经成为消息摘要的首选,与MD不同的是其摘要更长,安全性更高。 SHA算法有SHA-1、SHA-224、SHA-256、SHA-384、SHA-512,除了SHA-1外,其他都是根据信息摘要的长度命名的。SHA-224是为了符合3DES的需要而定义的。

2)、应用场景

需要报文摘要功能,且安全要求比较高的应用场景,目前很多数字签名都是使用SHA的算法。

3)、示例

SHA-224是由BouncyCastleProvider实现,jdk本身没有实现。

1、jdk示例

  • 实现源码
import java.security.MessageDigest;

/**
 * @author alan 2018年11月15日
 */
public class SHACoder {
	/**
	 * SHA-1加密
	 */
	public static byte[] encodeSHA(byte[] data) throws Exception {
		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("SHA");

		// 执行消息摘要
		return md.digest(data);
	}

	/**
	 * SHA-256加密
	 */
	public static byte[] encodeSHA256(byte[] data) throws Exception {
		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("SHA-256");

		// 执行消息摘要
		return md.digest(data);
	}

	/**
	 * SHA-384加密
	 */
	public static byte[] encodeSHA384(byte[] data) throws Exception {
		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("SHA-384");

		// 执行消息摘要
		return md.digest(data);
	}

	/**
	 * SHA-512加密
	 */
	public static byte[] encodeSHA512(byte[] data) throws Exception {
		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("SHA-512");

		// 执行消息摘要
		return md.digest(data);
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class SHACoderTest {
	@Test
	public final void testEncodeSHA() throws Exception {
		String str = "SHA1消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA(str.getBytes());
		byte[] data2 = SHACoder.encodeSHA(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeSHA256() throws Exception {
		String str = "SHA256消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA256(str.getBytes());
		byte[] data2 = SHACoder.encodeSHA256(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeSHA384() throws Exception {
		String str = "SHA384消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA384(str.getBytes());
		byte[] data2 = SHACoder.encodeSHA384(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeSHA512() throws Exception {
		String str = "SHA512消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA512(str.getBytes());
		byte[] data2 = SHACoder.encodeSHA512(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}
}

2、bouncycastle示例

  • 实现源码
import java.security.MessageDigest;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

/**
 * @author alan 2018年11月15日
 */
public class SHA224Coder {
	public static byte[] encodeSHA224(byte[] data) throws Exception {
		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 初始化MessageDigest
		MessageDigest md = MessageDigest.getInstance("SHA-224");

		// 执行消息摘要
		return md.digest(data);
	}

	public static String encodeSHA224Hex(byte[] data) throws Exception {
		// 执行消息摘要
		byte[] b = encodeSHA224(data);

		// 做十六进制编码处理
		return new String(Hex.encode(b));

	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan
 * 2018年11月15日
 */
public class SHA224CoderTest {
	@Test
	public final void testEncodeSHA224() throws Exception {
		String str = "SHA224消息摘要";

		// 获得摘要信息
		byte[] data1 = SHA224Coder.encodeSHA224(str.getBytes());
		byte[] data2 = SHA224Coder.encodeSHA224(str.getBytes());

		// 校验
		assertEquals(data1, data2);
	}

	@Test
	public final void testEncodeSHA224Hex() throws Exception {
		String str = "SHA224Hex消息摘要";

		// 获得摘要信息
		String data1 = SHA224Coder.encodeSHA224Hex(str.getBytes());
		String data2 = SHA224Coder.encodeSHA224Hex(str.getBytes());

		System.err.println("原文:\t" + str);
		System.err.println("SHA224Hex-1:\t" + data1);
		System.err.println("SHA224Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}
}

3、apache commons示例

  • 实现源码
import org.apache.commons.codec.digest.DigestUtils;

/**
 * @author alan
 * 2018年11月15日
 */
public class SHACoder {
	/**
	 * SHA1加密
	 */
	public static byte[] encodeSHA(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha(data);
	}

	/**
	 * SHA1Hex加密
	 */
	public static String encodeSHAHex(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.shaHex(data);
	}

	/**
	 * SHA256加密
	 */
	public static byte[] encodeSHA256(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha256(data);
	}

	/**
	 * SHA256Hex加密
	 */
	public static String encodeSHA256Hex(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha256Hex(data);
	}

	/**
	 * SHA384加密
	 */
	public static byte[] encodeSHA384(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha384(data);
	}

	/**
	 * SHA384Hex加密
	 */
	public static String encodeSHA384Hex(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha384Hex(data);
	}

	/**
	 * SHA512Hex加密
	 */
	public static byte[] encodeSHA512(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha512(data);
	}

	/**
	 * SHA512Hex加密
	 */
	public static String encodeSHA512Hex(String data) throws Exception {
		// 执行消息摘要
		return DigestUtils.sha512Hex(data);
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class SHACoderTest {
	/**
	 * 测试SHA-1
	 */
	@Test
	public final void testEncodeSHA() throws Exception {
		String str = "SHA1消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA(str);
		byte[] data2 = SHACoder.encodeSHA(str);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-1Hex
	 */
	@Test
	public final void testEncodeSHAHex() throws Exception {
		String str = "SHA-1Hex消息摘要";

		// 获得摘要信息
		String data1 = SHACoder.encodeSHAHex(str);
		String data2 = SHACoder.encodeSHAHex(str);

		System.err.println("原文:\t" + str);
		System.err.println("SHA1Hex-1:\t" + data1);
		System.err.println("SHA1Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-256
	 */
	@Test
	public final void testEncodeSHA256() throws Exception {
		String str = "SHA256消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA256(str);
		byte[] data2 = SHACoder.encodeSHA256(str);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-256Hex
	 */
	@Test
	public final void testEncodeSHA256Hex() throws Exception {
		String str = "SHA256Hex消息摘要";

		// 获得摘要信息
		String data1 = SHACoder.encodeSHA256Hex(str);
		String data2 = SHACoder.encodeSHA256Hex(str);

		System.err.println("原文:\t" + str);
		System.err.println("SHA256Hex-1:\t" + data1);
		System.err.println("SHA256Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-384
	 */
	@Test
	public final void testEncodeSHA384() throws Exception {
		String str = "SHA384消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA384(str);
		byte[] data2 = SHACoder.encodeSHA384(str);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-384Hex
	 */
	@Test
	public final void testEncodeSHA384Hex() throws Exception {
		String str = "SHA384Hex消息摘要";

		// 获得摘要信息
		String data1 = SHACoder.encodeSHA384Hex(str);
		String data2 = SHACoder.encodeSHA384Hex(str);

		System.err.println("原文:\t" + str);
		System.err.println("SHA384Hex-1:\t" + data1);
		System.err.println("SHA384Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-512
	 */
	@Test
	public final void testEncodeSHA512() throws Exception {
		String str = "SHA512消息摘要";

		// 获得摘要信息
		byte[] data1 = SHACoder.encodeSHA512(str);
		byte[] data2 = SHACoder.encodeSHA512(str);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试SHA-512Hex
	 */
	@Test
	public final void testEncodeSHA512Hex() throws Exception {
		String str = "SHA512Hex消息摘要";

		// 获得摘要信息
		String data1 = SHACoder.encodeSHA512Hex(str);
		String data2 = SHACoder.encodeSHA512Hex(str);

		System.err.println("原文:\t" + str);
		System.err.println("SHA512Hex-1:\t" + data1);
		System.err.println("SHA512Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}
}

4、MAC

1)、定义

MAC是含有密钥散列函数算法,包含了MD和SHA的特性,并在此基础上加入了密钥,通常也会把MAC成为HMAC(keyed-Hash Message Authentication Code)。MAC算法集合了MD和SHA两大系列消息摘要算法,MD系列有HmacMD2、HmacMD4、HmacMD5,SHA系列有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512。 经MAC算法得到的摘要值可以使用十六进制编码表示,其摘要值长度与参与实现的算法摘要值长度相同,比如HmacSHA1算法得到的摘要长度就是SHA1算法得到摘要长度,都是160位二进制数,换算成十六进制编码为40位。

2)、应用场景

在MD和SHA均不满足使用场景要求的时候,MAC是一个有效的补充。

3)、示例

1、jdk示例

  • 实现源码
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * @author alan 2018年11月15日
 */
public class MACCoder {
	/**
	 * 初始化HmacMD5密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static byte[] initHmacMD5Key() throws Exception {
		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();
		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacMD5加密
	 * 
	 * @param data
	 *            待加密数据
	 * @param key
	 *            密钥
	 * @return byte[] 消息摘要
	 * 
	 * @throws Exception
	 */
	public static byte[] encodeHmacMD5(byte[] data, byte[] key) throws Exception {
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacMD5");
		// 实例化Mac "SslMacMD5"
		Mac mac = Mac.getInstance("SslMacMD5");// secretKey.getAlgorithm());
		// 初始化Mac
		mac.init(secretKey);
		// 执行消息摘要
		return mac.doFinal(data);
	}

	/**
	 * 初始化HmacSHA1密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static byte[] initHmacSHAKey() throws Exception {
		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HMacTiger");
		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();
		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacSHA1加密
	 * 
	 * @param data
	 *            待加密数据
	 * @param key
	 *            密钥
	 * @return byte[] 消息摘要
	 * 
	 * @throws Exception
	 */
	public static byte[] encodeHmacSHA(byte[] data, byte[] key) throws Exception {
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HMacTiger");
		// 实例化Mac SslMacMD5
		Mac mac = Mac.getInstance("SslMacMD5");// secretKey.getAlgorithm());
		// 初始化Mac
		mac.init(secretKey);
		// 执行消息摘要
		return mac.doFinal(data);
	}

	// // 根据所安装的 JCE 仲裁策略文件,返回指定转换的最大密钥长度。
	// public final static int getMaxAllowedKeyLength(String transformation)

	/**
	 * 初始化HmacSHA256密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static byte[] initHmacSHA256Key() throws Exception {
		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256");
		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();
		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacSHA256加密
	 * 
	 * @param data
	 *            待加密数据
	 * @param key
	 *            密钥
	 * @return byte[] 消息摘要
	 * 
	 * @throws Exception
	 */
	public static byte[] encodeHmacSHA256(byte[] data, byte[] key) throws Exception {
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA256");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		// 初始化Mac
		mac.init(secretKey);
		// 执行消息摘要
		return mac.doFinal(data);
	}

	/**
	 * 初始化HmacSHA384密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static byte[] initHmacSHA384Key() throws Exception {
		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA384");
		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();
		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacSHA384加密
	 * 
	 * @param data
	 *            待加密数据
	 * @param key
	 *            密钥
	 * @return byte[] 消息摘要
	 * 
	 * @throws Exception
	 */
	public static byte[] encodeHmacSHA384(byte[] data, byte[] key) throws Exception {
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA384");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		// 初始化Mac
		mac.init(secretKey);
		// 执行消息摘要
		return mac.doFinal(data);
	}

	/**
	 * 初始化HmacSHA512密钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static byte[] initHmacSHA512Key() throws Exception {
		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA512");
		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();
		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacSHA512加密
	 * 
	 * @param data
	 *            待加密数据
	 * @param key
	 *            密钥
	 * @return byte[] 消息摘要
	 * 
	 * @throws Exception
	 */
	public static byte[] encodeHmacSHA512(byte[] data, byte[] key) throws Exception {
		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA512");
		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());
		// 初始化Mac
		mac.init(secretKey);
		// 执行消息摘要
		return mac.doFinal(data);
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class MACCoderTest {
	/**
	 * 测试HmacMD5
	 */
	@Test
	public final void testEncodeHmacMD5() throws Exception {
		String str = "HmacMD5消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacMD5Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacMD5(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacMD5(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacSHA1
	 */
	@Test
	public final void testEncodeHmacSHA() throws Exception {
		String str = "HmacSHA1消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacSHAKey();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacSHA(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacSHA(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacSHA256
	 */
	@Test
	public final void testEncodeHmacSHA256() throws Exception {
		String str = "HmacSHA256消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacSHA256Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacSHA256(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacSHA256(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacSHA384
	 */
	@Test
	public final void testEncodeHmacSHA384() throws Exception {
		String str = "HmacSHA384消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacSHA384Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacSHA384(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacSHA384(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacSHA512
	 */
	@Test
	public final void testEncodeHmacSHA512() throws Exception {
		String str = "HmacSHA512消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacSHA512Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacSHA512(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacSHA512(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

}

2、bouncycastle示例

  • 实现源码
import java.security.Security;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

/**
 * @author alan 2018年11月15日
 */
public class MACCoder {
	/**
	 * 初始化HmacMD2密钥
	 */
	public static byte[] initHmacMD2Key() throws Exception {
		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD2");

		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();

		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacMD2消息摘要
	 * 
	 * @param data
	 *            待做消息摘要处理的数据
	 * @param byte[]
	 *            密钥
	 * @return byte[] 消息摘要
	 * @throws Exception
	 */
	public static byte[] encodeHmacMD2(byte[] data, byte[] key) throws Exception {

		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacMD2");

		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());

		// 初始化Mac
		mac.init(secretKey);

		// 执行消息摘要
		return mac.doFinal(data);
	}

	/**
	 * HmacMD2Hex消息摘要
	 * 
	 * @param data
	 *            待做消息摘要处理的数据
	 * @param String
	 *            密钥
	 * @return byte[] 消息摘要
	 * @throws Exception
	 */
	public static String encodeHmacMD2Hex(byte[] data, byte[] key) throws Exception {

		// 执行消息摘要
		byte[] b = encodeHmacMD2(data, key);

		// 做十六进制转换
		return new String(Hex.encode(b));
	}

	/**
	 * 初始化HmacMD4密钥
	 * 
	 * @return byte[] 密钥
	 * @throws Exception
	 */
	public static byte[] initHmacMD4Key() throws Exception {

		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD4");

		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();

		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacMD4消息摘要
	 * 
	 * @param data
	 *            待做消息摘要处理的数据
	 * @param byte[]
	 *            密钥
	 * @return byte[] 消息摘要
	 * @throws Exception
	 */
	public static byte[] encodeHmacMD4(byte[] data, byte[] key) throws Exception {

		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacMD4");

		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());

		// 初始化Mac
		mac.init(secretKey);

		// 执行消息摘要
		return mac.doFinal(data);
	}

	/**
	 * HmacMD4Hex消息摘要
	 * 
	 * @param data
	 *            待做消息摘要处理的数据
	 * @param byte[]
	 *            密钥
	 * @return String 消息摘要
	 * @throws Exception
	 */
	public static String encodeHmacMD4Hex(byte[] data, byte[] key) throws Exception {

		// 执行消息摘要
		byte[] b = encodeHmacMD4(data, key);

		// 做十六进制转换
		return new String(Hex.encode(b));
	}

	/**
	 * 初始化HmacSHA224密钥
	 * 
	 * @return byte[] 密钥
	 * @throws Exception
	 */
	public static byte[] initHmacSHA224Key() throws Exception {

		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 初始化KeyGenerator
		KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA224");

		// 产生秘密密钥
		SecretKey secretKey = keyGenerator.generateKey();

		// 获得密钥
		return secretKey.getEncoded();
	}

	/**
	 * HmacSHA224消息摘要
	 * 
	 * @param data
	 *            待做消息摘要处理的数据
	 * @param byte[]
	 *            密钥
	 * @return byte[] 消息摘要
	 * @throws Exception
	 */
	public static byte[] encodeHmacSHA224(byte[] data, byte[] key) throws Exception {

		// 加入BouncyCastleProvider支持
		Security.addProvider(new BouncyCastleProvider());

		// 还原密钥
		SecretKey secretKey = new SecretKeySpec(key, "HmacSHA224");

		// 实例化Mac
		Mac mac = Mac.getInstance(secretKey.getAlgorithm());

		// 初始化Mac
		mac.init(secretKey);

		// 执行消息摘要
		return mac.doFinal(data);
	}

	/**
	 * HmacSHA224Hex消息摘要
	 * 
	 * @param data
	 *            待做消息摘要处理的数据
	 * @param byte[]
	 *            密钥
	 * @return String 消息摘要
	 * @throws Exception
	 */
	public static String encodeHmacSHA224Hex(byte[] data, byte[] key) throws Exception {

		// 执行消息摘要
		byte[] b = encodeHmacSHA224(data, key);

		// 做十六进制转换
		return new String(Hex.encode(b));
	}
}
  • testNG测试
import static org.testng.Assert.assertEquals;

import org.testng.annotations.Test;

/**
 * @author alan 2018年11月15日
 */
public class MACCoderTest {
	@Test
	public final void testEncodeHmacMD2() throws Exception {
		String str = "HmacMD2消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacMD2Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacMD2(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacMD2(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacMD2Hex
	 * 
	 * @throws Exception
	 */
	@Test
	public final void testEncodeHmacMD2Hex() throws Exception {
		String str = "HmacMD2Hex消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacMD2Key();

		// 获得摘要信息
		String data1 = MACCoder.encodeHmacMD2Hex(str.getBytes(), key);
		String data2 = MACCoder.encodeHmacMD2Hex(str.getBytes(), key);

		System.err.println("原文:\t" + str);
		System.err.println("HmacMD2Hex-1:\t" + data1);
		System.err.println("HmacMD2Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacMD4
	 * 
	 * @throws Exception
	 */
	@Test
	public final void testEncodeHmacMD4() throws Exception {
		String str = "HmacMD4消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacMD4Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacMD4(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacMD4(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacMD4Hex
	 * 
	 * @throws Exception
	 */
	@Test
	public final void testEncodeHmacMD4Hex() throws Exception {
		String str = "HmacMD4Hex消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacMD4Key();

		// 获得摘要信息
		String data1 = MACCoder.encodeHmacMD4Hex(str.getBytes(), key);
		String data2 = MACCoder.encodeHmacMD4Hex(str.getBytes(), key);

		System.err.println("原文:\t" + str);
		System.err.println("HmacMD4Hex-1:\t" + data1);
		System.err.println("HmacMD4Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacSHA224
	 * 
	 * @throws Exception
	 */
	@Test
	public final void testEncodeHmacSHA224() throws Exception {
		String str = "HmacSHA224消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacSHA224Key();

		// 获得摘要信息
		byte[] data1 = MACCoder.encodeHmacSHA224(str.getBytes(), key);
		byte[] data2 = MACCoder.encodeHmacSHA224(str.getBytes(), key);

		// 校验
		assertEquals(data1, data2);
	}

	/**
	 * 测试HmacSHA224Hex
	 * 
	 * @throws Exception
	 */
	@Test
	public final void testEncodeHmacSHA224Hex() throws Exception {
		String str = "HmacSHA224Hex消息摘要";

		// 初始化密钥
		byte[] key = MACCoder.initHmacSHA224Key();

		// 获得摘要信息
		String data1 = MACCoder.encodeHmacSHA224Hex(str.getBytes(), key);
		String data2 = MACCoder.encodeHmacSHA224Hex(str.getBytes(), key);

		System.err.println("原文:\t" + str);
		System.err.println("HmacSHA224Hex-1:\t" + data1);
		System.err.println("HmacSHA224Hex-2:\t" + data2);

		// 校验
		assertEquals(data1, data2);
	}
}

3、apache commons示例

  • 实现源码
在这里插入代码片
  • testNG测试
在这里插入代码片

以上,简单的介绍了Base64、消息摘要和其使用示例,并且使用示例以三种不同实现方式及测试。

标签:MD,Exception,String,示例,public,数据安全,byte,data,throws
From: https://blog.51cto.com/alanchan2win/6522464

相关文章

  • 趋势分享 | 多云时代数据安全面临的挑战
    IT 和数据管理研究和咨询公司EMA(Enterprise Management Associates)早前发布的一份《多云环境下的数据安全》(Data Security in a Multi-Cloud World)研究报告,调查了来自十个以上不同行业垂直领域、公司规模在 500 人以上的 204 名受访人员。调查结果发现,数据安全保护已......
  • MD204L文本modbus rtu控制监控变频器资料效果可以看视频,有两种文本都试过可以的,两条通
    MD204L文本modbusrtu控制监控变频器资料效果可以看视频,有两种文本都试过可以的,两条通讯线就能控制正反转,停止及频率设定,以及对运行参数的监控,方便又节约成本,内容包括软件,文本的说明书,程序,接线和变频器参数设置说明,视频教程这段话涉及到的知识点和领域范围主要包括以下内容:MD204L......
  • 昆仑通泰mcgs触摸屏和台达VFD-M变频器的rtu通讯示例硬件:mcgs触摸屏(没屏电脑也可实现)
    昆仑通泰mcgs触摸屏和台达VFD-M变频器的rtu通讯示例硬件:mcgs触摸屏(没屏电脑也可实现),台达vfd-m变频器。通过modbusrtu功能,在触摸屏(或者电脑通过在线)控制台达变频器的正反转和停止,频率设定,加减速......
  • [纵横网络靶场社区]隐信道数据安全分析
    附件flag-woody.mp3是一首歌,mp3格式,听了一下,Audacity打开看了一下没发现什么异常;mp3隐写试了一下也没什么线索。回到题目名称和描述。信道隐写,某种private的方式将信息传递出去。使用010Editor打开,分析文件结构。可以发现在每个MPEG_FRAMEmf下的4字节MPEG_HEADERmpeg_hdr中的......
  • python3 subprocess.getoutput(cmd) 执行linux命令进入交互模式后一直卡住了
    进入交互模式是我们预期之外的,记录一下。进入交互之后linux一直等待你的输入,所有subprocess.getoutput()就一直卡着呢~,我们加入timeout通过学习subprocess中支持timeout有:getoutput并不支持timeout参数尝试了callcheck_allcheck_output这几个方法之后并不能解决Linux交......
  • 【电商平台api接口】获取lazada商品评论获取评论内容、评论日期、买家昵称、评论商品
    ​ 请求获取Lazada商品评论(免费获取测试)的作用如下:1.了解商品质量:通过获取Lazada商品评论,可以了解商品的质量、使用感受等信息,从而更好地了解商品的优缺点,为购买者提供更为准确的参考。2.搜集用户反馈:通过搜集Lazada商品评论,可以了解用户对商品的反馈和意见,了解用户的需求和......
  • 各大电商平台关于API接口的一些知识分享和示例
    随着互联网的发展,电子商务也成为了内部的一个热门话题,各大电商平台在这种情况下开发了各种API接口,为各种企业提供了基础数据的支持,加速了市场竞争。本文将重点讨论电商API接口相关的一些知识分享。一、电商API接口的重要性对于电商平台来说,API接口就像是一座桥梁,它可以将......
  • API接口开发系列(获得京东JD商品详情原数据java源代码调用示例)
    ​请求获取京东商品详情原数据(免费获取调用)的作用:1.提供商品信息:京东商品详情中的原数据可以提供商品的基本信息,包括商品名称、价格、品牌、规格参数、图片等,让消费者更好地了解商品。2.帮助搜索引擎识别:京东商品详情的原数据可以被搜索引擎识别,在搜索结果页中显示更为详细丰富......
  • API接口开发系列(获得京东JD商品详情原数据java源代码调用示例)
    请求获取京东商品详情原数据(免费获取调用请私信)的作用:1.提供商品信息:京东商品详情中的原数据可以提供商品的基本信息,包括商品名称、价格、品牌、规格参数、图片等,让消费者更好地了解商品。2.帮助搜索引擎识别:京东商品详情的原数据可以被搜索引擎识别,在搜索结果页中显示更为详细丰富......
  • 2023跟我一起学设计模式:Golang 抽象工厂模式讲解和代码示例
    Golang抽象工厂模式讲解和代码示例抽象工厂是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。抽象工厂定义了用于创建不同产品的接口,但将实际的创建工作留给了具体工厂类。每个工厂类型都对应一个特定的产品变体。在创建产品时,客户端代码调用的是工厂对象的......