(文章目录)
本文简单的介绍了数据安全的概念、加密解密常见的算法、数字签名介绍、数字证书的介绍及相关示例。 本文是该系列的第一篇,入门介绍。 本文部分图片来源互联网。
一、基本概念介绍
1、保密性
保密性是确保数据仅能被合法用户访问,即数据不能被未授权的第三方使用。一般通过对称加密来完成保密性,同时也能防窃听,对称加密当前安全性较高的算法有AES和PBE算法。
2、完整性
确保数据只能由授权方或以授权的方式进行修改,即数据在传输过程中不能被未授权方修改。一般通过两方面进行控制,即网络传输过程通过https;另一方面是针对传输的数据进行签名,首选需要使用第三方CA授权的数字证书,次选则是和业务相关的数字签名。数字签名使用算法首选使用ECDSA算法,次选RSA256/512。如果涉及国密的情况,则根据需要选择需要即可,国密算法没有那么多,都是有固定的用途的。SM2和ECDSA对应,都是椭圆曲线,SM2和RSA都是非对称加密。
3、可用性
确保所有的数据仅在适当的时候可以由授权方访问。针对接口做到高可用,一般是通过服务端的高可用技术实现,比如通过keepalived+LVS中间件来控制心跳,一旦一台服务器出现故障则实时进行切换。
4、可靠性
确保系统能在规定的条件下、规定的时间内、完成规定功能时具有稳定的概率。在确保系统正常运行的同时,提高服务对外提供服务的能力,比如说服务的正确性、规定时间内停机时长等要求。
5、抗否认性
也称抗抵赖性,确保发送方与接收方在执行各自操作后,对所做的操作不可否认。抗抵赖性一般通过数字签名来实现,发送方通过私钥签名,接收方通过公钥验证,通过则表示是发送方的数据。为保证私钥的泄漏,则建议使用一次性私钥,即每次私钥使用一次,用完即毁且不留存任何日志及传输。数字签名使用非对称针对报文摘要进行加密,一般推荐算法是ECDSA。针对PC端的抗抵赖性,一般可以通过数字信封来保证其抗抵赖性,即针对传递的消息进行签名获得其公钥,使用随机数作为密钥对传递消息进行加密,然后利用公钥对随机数密钥进行对称加密,最后将密文、公钥等信息传递给接收方,接收方的操作则是反过来的。为了实际应用的更加安全,在生成密钥的时候,可以把设备号或mac作为密钥生成因子,保证一定程度上的公钥的安全性。
6、可控性
对信息及信息系统实施安全监控。通过接口平台的监控功能实现接口的监控,出现异常情况时则做到一定的容错及及时报警。
7、可审查性
通过审计、监控、抗抵赖性等安全机制,确保数据访问者的行为有证可查,当网络出现安全异常时,提供调查的依据和手段。一般通过日志、数字签名等等方式来保证数据的有据可查以及抗抵赖性。
8、认证(鉴别)
确保数据访问者和信息服务者的身份真实有效。一般通过数字签名来保证信息的认证和鉴别。数字签名不再赘述。
9、访问控制
确保数据不被非授权方或以未授权的方式使用。确保接口的访问都是通过授权访问的,比如需要登录后才能访问,ip白名单等控制方式。
10、OSI参考模型
在OSI安全体系结构中通过数据加密确保数据的保密性,在TCP/IP安全体系结构中以加密算法为基础构建SSL/TSL协议。
11、8类安全机制
八类安全机制包括加密机制、数字签名机制、访问控制机制、数据完整性机制、认证机制、业务流填充机制、路由控制机制和公证机制。
- 加密机制,加密机制对应数据保密性服务。加密是提高数据安全性的最简单方法,通过对数据进行加密,有效提高了数据的保密性,能防止数据在传输过程中被窃取,常用的加密算法有对称加密和非对称加密。典型的对称加密算法有DES、ASE等,非对称加密算法有RSA等。
- 数字签名机制,数字签名机制对应的是认证(鉴别)服务,数字签名是有效的鉴别方法,利用数字签名技术可以实施用户身份认证和消息认证,他具有借鉴收发双方纠纷的能力,是认证服务最核心的技术。在数字签名技术的基础上,为了鉴别软件的有效性,又产生了代码签名技术。常用的签名算法有RSA、DSA、SM2等算法。
- 访问控制机制,访问控制机制对应访问控制服务,通过预先设定的规则对用户所访问的数据进行限制。通常,首先是通过用户的用户名和口令进行验证,其次是通过用户的角色、用户组等规则进行验证,最后是用户才能访问的限制资源。一般的应用常使用基于用户角色的访问控制方式,如RBAC。
- 数据完整性机制,数据完整性机制对应数据完整性服务,数据完整性的作用是为了避免数据在传输过程中受到干扰,同时防止数据在传输过程中被篡改,以提高数据传输完整性。通常可以用单向加密算法对数据进行加密,生成唯一的验证码,用以校验数据的完整性。常用的加密算法有MD5和SHA算法。
- 认证机制,认证机制对应认证(鉴别)服务,认证的目的在于验证接收方所接收到的数据是否来源于所期望的发送方,通常使用数字签名来认证。常用的算法有RSA和DSA等。
- 业务流填充机制,或传输流填充机制,对应数据保密性服务,业务流填充机制通过在数据传输过程中传送随机数的方式混淆真实的数据,加大数据破译难度,提高数据的保密性。
- 路由控制机制,对应访问控制服务,路由控制机制为数据发送方选择安全网络通信路径,避免发送方使用不安全路径发送数据,提高数据的安全性。
- 公证机制,对应抗否认性服务,公证机制的左右在于解决收发双方的纠纷问题,确保两方利益不受到损害。比如双方签订的合同,需要到第三方公证机构进行公证。
12、加密算法分类
加密算法大致上分为单向加密算法、对称加密算法和非对称加密算法。
- 单向加密算法,是数据完整性验证的常用算法,包括MD5、SHA算法等
- 对称加密算法,是数据存储加密的常用算法,加密与解密的密钥相同,包括DES、ASE等
- 非对称加密算法,是数据传输加密的常用算法,加密和解密的密钥不同,包括RSA等。用公钥加密用私钥解密或用私钥加密用公钥解密。 对称加密也可用在数据传输上,但非对称加密在密钥管理上更有优势,相对对称加密,非对称加密在安全级别上等级更高,但时间效率上不如对称加密。
13、java与密码
java对密码支持比较充分,可从以下三个方面来说
- java api支持常用的加密算法,MessageDigest类可以构建MD5、SHA;Mac类可以构建HMAC;Cipher类可以构建DES、AES、Blowfish等对称加密算法和RSA、DSA、DH等非对称加密;Signature类可以用于数字签名和验证;Certificate类可以操作数字证书等。同时还有其他开源类库支持,比如bc等,在接下来的系列文中均有详细的示例。
- jps容器支持,比如tomcat可以简单的配置https,一般常见的web容器都会支持https
- java工具支持,可以通过jdk自带的工具keytool完成密钥管理、证书管理;通过jarsigner可以完成代码签名
14、Kerckhoffs principle(科克霍夫原则)
Kerckhoffs principle:数据的安全基于密钥而不是算法的保密,即数据的安全取决于对密钥的保密,对算法公开。该原则也是现代密码学设计的基本原则。 密码算法的公开有助于提高算法的安全性,避免算法自身的漏洞。
二、加密算法简单介绍及应用场景、示例
1、base64
1)、定义
内容传送编码是一种以任意8位字节列组合的描述形式,这种形式不容易被人直接识别。经过base64编码后的数据会比原来数据长约1/3,经过base64编码后的字符串的字符数是以4为单位的整数倍。实现base64的算法推荐使用apache的common codec类库。
2)、应用场景
电子邮件传输、网络数据传输、密钥存储、数字证书存储
3)、示例
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、消息摘要(Message Digest)
消息摘要算法包含三大系列,即MD 、SHA 和MC,常用于验证数据的完整性,是数字签名的核心算法。 任何消息经过散列函数处理后都会获得唯一的散列值(hashcode),该过程称为消息摘要,其散列值成为数字指纹,其算法即是消息摘要算法。 消息摘要算法又称为散列算法,其核心在于散列函数的单向性,即通过散列函数可获得对应的散列值,但不可通过该散列值获得其原始信息。
- 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
1)、MD
- 定义 MD5是由MD2、MD3、MD4改进而来,是典型的消息摘要算法。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。如果将这个128位的信息摘要信息换算成十六进制,则可以得到一个32位的字符串(32位的数字字母混合码)。
- 应用场景 MD5之后的推荐替代应该是SHA,已经不适合安全性要求较高的场景。
- 代码示例
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);
}
}
2)、SHA
- 定义 SHA算法是基于MD4算法的,已经成为消息摘要的首选,与MD不同的是其摘要更长,安全性更高。 SHA算法有SHA-1、SHA-224、SHA-256、SHA-384、SHA-512,除了SHA-1外,其他都是根据信息摘要的长度命名的。SHA-224是为了符合3DES的需要而定义的。
- 应用场景 需要报文摘要功能,且安全要求比较高的应用场景,目前很多数字签名都是使用SHA的算法。
- 示例 SHA-224是由BouncyCastleProvider实现,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);
}
}
3)、MAC
-
定义 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位。
-
应用场景 在MD和SHA均不满足使用场景要求的时候,MAC是一个有效的补充。
-
示例
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);
}
}
3、对称加密
1)、定义
对称加密就是加密与解密拥有相同的密钥,根据加密方式可分为密码和分组密码。分组密码工作模式可分为ECB、CBC、CFB、OFB和CTR等,密钥长度决定了加密算法的安全性。常见的加密算法有DES或3DES、AES和RC系列算法,除此之外,还有Blowfish、Twofish、Serpent、IDEA和PBE等。
2)、DES
DES(Data Encryption Standard)数据加密标准,其衍生算法有DESede(3DES),由于安全性的原因AES逐步替代DES。
- 示例
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
/**
* @author alan 2018年11月16日
*/
public class DESCoder {
/**
* 密钥算法 <br>
* Java 6 只支持56bit密钥 <br>
* Bouncy Castle 支持64bit密钥
*/
public static final String KEY_ALGORITHM = "DES";
/**
* 加密/解密算法 / 工作模式 / 填充方式
*/
public static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5PADDING";
/**
* 转换密钥
*
* @param key
* 二进制密钥
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
// 实例化DES密钥材料
DESKeySpec dks = new DESKeySpec(key);
// 实例化秘密密钥工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
// 生成秘密密钥
SecretKey secretKey = keyFactory.generateSecret(dks);
return secretKey;
}
/**
* 解密
*
* @param data
* 待解密数据
* @param key
* 密钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
// 还原密钥
Key k = toKey(key);
// 实例化
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 加密
*
* @param data
* 待加密数据
* @param key
* 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
// 还原密钥
Key k = toKey(key);
// 实例化
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 生成密钥 <br>
* Java 6 只支持56bit密钥 <br>
* Bouncy Castle 支持64bit密钥 <br>
*
* @return byte[] 二进制密钥
* @throws Exception
*/
public static byte[] initKey() throws Exception {
/*
* 实例化密钥生成器
*
* 若要使用64bit密钥注意替换 将下述代码中的KeyGenerator.getInstance(CIPHER_ALGORITHM);
* 替换为KeyGenerator.getInstance(CIPHER_ALGORITHM, "BC");
*/
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
/*
* 初始化密钥生成器 若要使用64bit密钥注意替换 将下述代码kg.init(56); 替换为kg.init(64);
*/
kg.init(56, new SecureRandom());
// 生成秘密密钥
SecretKey secretKey = kg.generateKey();
// 获得密钥的二进制编码形式
return secretKey.getEncoded();
}
}
3)、AES
AES(Advanced Encryption Standard)高级数据加密标准,作为DES算法的替代。AES算法具有密钥建立时间短、灵敏性好、内存需求低等优点,应用比较广泛。
- 示例
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* @author alan 2018年11月16日
*/
public class AESCoder {
public static final String KEY_ALGORITHM = "AES";
/**
* 加密/解密算法 / 工作模式 / 填充方式 Java 6支持PKCS5Padding填充方式 Bouncy
* Castle支持PKCS7Padding填充方式
*/
public static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
/**
* 转换密钥
*
* @param key
* 二进制密钥
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
// 实例化AES密钥材料
SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
return secretKey;
}
/**
* 解密
*
* @param data
* 待解密数据
* @param key
* 密钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
// 还原密钥
Key k = toKey(key);
/*
* 实例化 使用PKCS7Padding填充方式,按如下方式实现 Cipher.getInstance(CIPHER_ALGORITHM, "BC");
*/
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 加密
*
* @param data
* 待加密数据
* @param key
* 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
// 还原密钥
Key k = toKey(key);
/*
* 实例化 使用PKCS7Padding填充方式,按如下方式实现 Cipher.getInstance(CIPHER_ALGORITHM, "BC");
*/
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 生成密钥 <br>
*
* @return byte[] 二进制密钥
* @throws Exception
*/
public static byte[] initKey() throws Exception {
// 实例化
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
/*
* AES 要求密钥长度为 128位、192位或 256位
*/
kg.init(256);
// 生成秘密密钥
SecretKey secretKey = kg.generateKey();
// 获得密钥的二进制编码形式
return secretKey.getEncoded();
}
}
4)、IDEA
IDEA(International Data Encryption Algorithm)国际数据加密算法是一种对称分组密码,其密钥长度为128位,数据块大小为64位,目前常用的场景是邮件加密算法。
- 示例
import java.security.Key;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
*
* @author alan 2018年11月16日
*/
public abstract class IDEACoder {
public static final String KEY_ALGORITHM = "IDEA";
//加密/解密算法 / 工作模式 / 填充方式
public static final String CIPHER_ALGORITHM = "IDEA/ECB/PKCS5Padding";
/**
* 转换密钥
*
* @param key
* 二进制密钥
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(byte[] key) throws Exception {
// 生成秘密密钥
SecretKey secretKey = new SecretKeySpec(key, KEY_ALGORITHM);
return secretKey;
}
/**
* 解密
*
* @param data
* 待解密数据
* @param key
* 密钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 还原密钥
Key k = toKey(key);
// 实例化
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 加密
*
* @param data
* 待加密数据
* @param key
* 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 还原密钥
Key k = toKey(key);
// 实例化
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
// 初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k);
// 执行操作
return cipher.doFinal(data);
}
/**
* 生成密钥
*
* @return byte[] 二进制密钥
* @throws Exception
*/
public static byte[] initKey() throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 实例化
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
// 初始化
kg.init(128);
// 生成秘密密钥
SecretKey secretKey = kg.generateKey();
// 获得密钥的二进制编码形式
return secretKey.getEncoded();
}
}
5)、PBE
PBE(Password Based Encryption)基于密码加密算法是一种基于密码的加密算法,其特点是密码(password)由用户自己保管,采用随机数(加盐)杂凑多重加密等方法保证数据的安全性。PBE没有密钥,其密码(password)代替了密钥,为了增加密码的安全性,采用了加盐的处理方式。PBE是对称加密算法的综合性算法,常见的算法有PBEWithMD5AndDES,该算法是使用了MD5和DES构建PBE算法。
- 示例
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
/**
* @author alan
* 2018年11月16日
*/
public class PBECoder {
/**
* Java 6 支持以下任意一种算法
*
* <pre>
* PBEWithMD5AndDES
* PBEWithMD5AndTripleDES
* PBEWithSHA1AndDESede
* PBEWithSHA1AndRC2_40
* </pre>
*/
public static final String ALGORITHM = "PBEWithMD5AndTripleDES";
/**
* 盐初始化<br>
* 盐长度必须为8字节
*
* @return byte[] 盐
* @throws Exception
*/
public static byte[] initSalt() throws Exception {
SecureRandom random = new SecureRandom();
return random.generateSeed(8);
}
/**
* 转换密钥
*
* @param password
* 密码
* @return Key 密钥
* @throws Exception
*/
private static Key toKey(String password) throws Exception {
// 密钥材料转换
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
// 实例化
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
// 生成密钥
SecretKey secretKey = keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
* 加密
*
* @param data
* 数据
* @param password
* 密码
* @param salt
* 盐
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String password, byte[] salt)
throws Exception {
// 转换密钥
Key key = toKey(password);
// 实例化PBE参数材料
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
// 实例化
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
// 执行操作
return cipher.doFinal(data);
}
/**
* 解密
*
* @param data
* 数据
* @param password
* 密码
* @param salt
* 盐
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String password, byte[] salt)
throws Exception {
// 转换密钥
Key key = toKey(password);
// 实例化PBE参数材料
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
// 实例化
Cipher cipher = Cipher.getInstance(ALGORITHM);
// 初始化
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
// 执行操作
return cipher.doFinal(data);
}
}
4、非对称加密
1)、定义
非对称加密算法是加密和解密使用不同的密钥。公钥和私钥成对出现,公钥加密私钥解密,或者私钥加密公钥解密。由于其时间效率上不高,一般应用在安全性要求较高的场合。 常用的对称加密算法有两类,即基于因子分解难题的RSA和基于离散对数难题的DSA。RSA是应用最广泛的非对称加密算法,也是第一个既能用于数据加密也能用于数字签名的算法。DSA是由EIGamal算法演变而来的,用于数字签名。
2)、DH
DH(Diffie-Hellman)密钥交换算法主要是为了解决密钥交换而发展出来的。DH算法是一个密钥协商算法,仅能用于密钥分配,不能用于加密或解密。DH算法的安全性基于有限域上的离散对数难题,基于这种安全性,通过DH算法进行密钥分配,使得消息的收发双方可以安全地交换一个秘密密钥,再通过这个密钥对数据进行加密和解密处理。
- 示例
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* @author alan 2018年11月16日
*/
public class DHCoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "DH";
/**
* 本地密钥算法,即对称加密密钥算法,可选DES、DESede和AES算法
*/
public static final String SECRET_KEY_ALGORITHM = "AES";
/**
* 默认密钥长度
*
* DH算法默认密钥长度为1024 密钥长度必须是64的倍数,其范围在512到1024位之间。
*/
private static final int KEY_SIZE = 512;
/**
* 公钥
*/
private static final String PUBLIC_KEY = "DHPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "DHPrivateKey";
/**
* 初始化甲方密钥
*
* @return Map 甲方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对生成器
keyPairGenerator.initialize(KEY_SIZE);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 甲方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 甲方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 初始化乙方密钥
*
* @param key
* 甲方公钥
* @return Map 乙方密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey(byte[] key) throws Exception {
// 解析甲方公钥
// 转换公钥材料
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 由甲方公钥构建乙方密钥
DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();
// 实例化密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
// 初始化密钥对生成器
keyPairGenerator.initialize(dhParamSpec);
// 产生密钥对
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 乙方公钥
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 乙方私钥
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
// 将密钥对存储在Map中
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 加密
*
* @param data
* 待加密数据
* @param key
* 密钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
// 生成本地密钥
SecretKey secretKey = new SecretKeySpec(key, SECRET_KEY_ALGORITHM);
// 数据加密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 解密<br>
*
* @param data
* 待解密数据
* @param key
* 密钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
// 生成本地密钥
SecretKey secretKey = new SecretKeySpec(key, SECRET_KEY_ALGORITHM);
// 数据解密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(data);
}
/**
* 构建密钥
*
* @param publicKey
* 公钥
* @param privateKey
* 私钥
* @return byte[] 本地密钥
* @throws Exception
*/
public static byte[] getSecretKey(byte[] publicKey, byte[] privateKey) throws Exception {
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 初始化公钥
// 密钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
// 产生公钥
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 初始化私钥
// 密钥材料转换
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 产生私钥
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化
KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
// 初始化
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true);
// 生成本地密钥
SecretKey secretKey = keyAgree.generateSecret(SECRET_KEY_ALGORITHM);
return secretKey.getEncoded();
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
}
3)、RSA
RSA是三个创建者的姓名缩写,是唯一被广泛接受并实现的通用公开加密算法,目前已经成为非对称加密的国际标准,也可以用于数据加密,也可以用于数据签名。RSA算法公钥长度远小于私钥长度,并遵循“公钥加密私钥解密”和“私钥加密公钥解密”两项加密解密原则。
- 示例
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
/**
* @author alan 2018年11月20日
*/
public class RSACoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA密钥长度 默认1024位, 密钥长度必须是64的倍数, 范围在512至65536位之间。
*/
private static final int KEY_SIZE = 512;
/**
* 私钥解密
*
* @param data
* 待解密数据
* @param key
* 私钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data
* 待解密数据
* @param key
* 公钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
*
* @param data
* 待加密数据
* @param key
* 公钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 私钥加密
*
* @param data
* 待加密数据
* @param key
* 私钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成私钥
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 初始化密钥
*
* @return Map 密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化密钥对生成器
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对生成器
keyPairGen.initialize(KEY_SIZE);
// 生成密钥对
KeyPair keyPair = keyPairGen.generateKeyPair();
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 封装密钥
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
}
4)、EIGamal/DSA
基于离散对数问题的算法有EIGamal和ECC,是常用的非对称加密算法,DSA(Digital Signature Algorithm)是EIGamal的一个变形。遵循“公钥加密,私钥解密”的加密解密方式。与RSA相比比较接近,但只实现了“公钥加密,私钥解密”部分。EIGamal算法的公私钥长度基本一致。 ECC(Elliptical Curve Cryptography)椭圆曲线加密算法是以椭圆曲线理论为基础实现的,通过椭圆曲线方程式的性质产生密钥,而不是采用传统的方法利用大质数的积来产生。在创建密钥时更快、更小。
- 示例
import java.security.AlgorithmParameterGenerator;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.DHParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* @author alan
* 2018年11月20日
*/
public class ElGamalCoder {
/**
* 非对称加密密钥算法
*/
public static final String KEY_ALGORITHM = "ElGamal";
/**
* 密钥长度
*
* ElGamal算法默认密钥长度为1024 密钥长度范围在160位至16384位不等。
*/
private static final int KEY_SIZE = 256;
/**
* 公钥
*/
private static final String PUBLIC_KEY = "ElGamalPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "ElGamalPrivateKey";
/**
* 用私钥解密
*
* @param data
* 待解密数据
* @param key
* 私钥
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 私钥材料转换
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成私钥
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 用公钥加密
*
* @param data
* 待加密数据
* @param key
* 公钥
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 公钥材料转换
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 生成密钥
*
* @return Map 密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 实例化算法参数生成器
AlgorithmParameterGenerator apg = AlgorithmParameterGenerator.getInstance(KEY_ALGORITHM);
// 初始化算法参数生成器
apg.init(KEY_SIZE);
// 生成算法参数
AlgorithmParameters params = apg.generateParameters();
// 构建参数材料
DHParameterSpec elParams = (DHParameterSpec) params.getParameterSpec(DHParameterSpec.class);
// 实例化密钥对儿生成器
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对儿生成器
kpg.initialize(elParams, new SecureRandom());
// 生成密钥对儿
KeyPair keys = kpg.genKeyPair();
// 取得密钥
PublicKey publicKey = keys.getPublic();
PrivateKey privateKey = keys.getPrivate();
// 封装密钥
Map<String, Object> map = new HashMap<String, Object>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
}
三、数字签名算法介绍、应用场景和示例
1、介绍
数字签名算法是一种带有密钥的消息摘要算法,使用的是非对称加密算法与消息摘要算法的结合。 数字签名算法要求能够验证数据的完整性、数据的来源以及防抵赖。消息摘要算法是验证数据完整性的最佳算法;防抵赖靠签名来确保,也是基于消息摘要的。 数字签名算法包含签名和验证两个重要步骤,遵循“私钥签名,公钥验证”的签名验证方式。签名时需要私钥和待签名的数据;验证时需要公钥、签名值和待签名数据,其核心就是消息摘要信息,签名值与消息摘要值一样,通常以十六进制字符串来表示。简单来说先对原始数据做摘要处理,然后使用私钥对摘要值签名;验证时使用公钥验证消息的摘要值。 数字签名常用算法有RSA、DSA、ECDSA,其中RSA是基于整数因子分解问题,DSA和ECDSA则是基于离散对数问题。RSA是目前数字签名应用最广泛的算法。
2、RSA签名
1)、介绍
RSA数字签名算法与加密算法一致,统称RSA。RSA数字签名算法分为MD和SHA两个系列,MD系列主要包含MD2withRSA和MD5withRSA;SHA系列包含SHA1withRSA、SHA224withRSA、SHA256withRSA、SHA384withRSA、SHA512withRSA。
2)、示例
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* @author alan 2018年11月20日
*/
public class RSACoder {
/**
* 数字签名 密钥算法
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 数字签名 签名/验证算法
*/
public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA密钥长度 默认1024位, 密钥长度必须是64的倍数, 范围在512至65536位之间。
*/
private static final int KEY_SIZE = 512;
/**
* 签名
*
* @param data
* 待签名数据
* @param privateKey
* 私钥
* @return byte[] 数字签名
* @throws Exception
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
// 签名
return signature.sign();
}
/**
* 校验
*
* @param data
* 待校验数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
*
* @return boolean 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
// 转换公钥材料
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
/**
* 取得私钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 初始化密钥
*
* @return Map 密钥对儿 Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 实例化密钥对儿生成器
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对儿生成器
keyPairGen.initialize(KEY_SIZE);
// 生成密钥对儿
KeyPair keyPair = keyPairGen.generateKeyPair();
// 公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 封装密钥
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
}
2、DSA签名
1)、介绍
DSA本质上是EIGamal数字签名算法,与RSA算法是数字证书中两种常见的算法,与RSA不同的是DSA仅仅包含数字签名算法,使用DSA算法的数字证书无法进行加密通信,而RSA算法即可加密也可签名。 DSA算法签名长度与密钥长度无关。
2)、示例
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* @author alan 2018年11月20日
*/
public class DSACoder {
/**
* 数字签名密钥算法
*/
public static final String ALGORITHM = "DSA";
/**
* 数字签名 签名/验证算法
*/
public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "DSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "DSAPrivateKey";
/**
* DSA密钥长度 默认1024位, 密钥长度必须是64的倍数, 范围在512至1024位之间(含)
*/
private static final int KEY_SIZE = 1024;
/**
* 签名
*
* @param data
* 待签名数据
* @param privateKey
* 私钥
* @return byte[] 数字签名
* @throws Exception
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 还原私钥
// 转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 生成私钥对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
// 签名
return signature.sign();
}
/**
* 校验
*
* @param data
* 待校验数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
*
* @return boolean 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
// 还原公钥
// 转换公钥材料
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 实例话Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
/**
* 生成密钥
*
* @return 密钥对象
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 初始化密钥对儿生成器
KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
// 实例化密钥对儿生成器
keygen.initialize(KEY_SIZE, new SecureRandom());
// 实例化密钥对儿
KeyPair keys = keygen.genKeyPair();
DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
// 封装密钥
Map<String, Object> map = new HashMap<String, Object>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
}
3、ECDSA签名
1)、介绍
ECDSA相对于传统的签名算法,速度更快、强度更高、签名更短优点,其用途越来越广泛。
2)、示例
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* @author alan 2018年11月20日
*/
public class ECDSACoder {
/**
* 数字签名 密钥算法
*/
private static final String KEY_ALGORITHM = "ECDSA";
/**
* 数字签名 签名/验证算法
*
* Bouncy Castle支持以下7种算法 NONEwithECDSA RIPEMD160withECDSA SHA1withECDSA
* SHA224withECDSA SHA256withECDSA SHA384withECDSA SHA512withECDSA
*/
private static final String SIGNATURE_ALGORITHM = "SHA512withECDSA";
/**
* 公钥
*/
private static final String PUBLIC_KEY = "ECDSAPublicKey";
/**
* 私钥
*/
private static final String PRIVATE_KEY = "ECDSAPrivateKey";
/**
* 初始化密钥
*
* @return Map 密钥Map
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
BigInteger p = new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839");
ECFieldFp ecFieldFp = new ECFieldFp(p);
BigInteger a = new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16);
BigInteger b = new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16);
EllipticCurve ellipticCurve = new EllipticCurve(ecFieldFp, a, b);
BigInteger x = new BigInteger("110282003749548856476348533541186204577905061504881242240149511594420911");
BigInteger y = new BigInteger("869078407435509378747351873793058868500210384946040694651368759217025454");
ECPoint g = new ECPoint(x, y);
BigInteger n = new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307");
ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g, n, 1);
// 实例化密钥对儿生成器
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KEY_ALGORITHM);
// 初始化密钥对儿生成器
kpg.initialize(ecParameterSpec, new SecureRandom());
// 生成密钥对儿
KeyPair keypair = kpg.generateKeyPair();
ECPublicKey publicKey = (ECPublicKey) keypair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keypair.getPrivate();
// 封装密钥
Map<String, Object> map = new HashMap<String, Object>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 取得私钥
*
* @param keyMap
* 密钥Map
* @return byte[] 私钥
* @throws Exception
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公钥
*
* @param keyMap
* 密钥Map
* @return byte[] 公钥
* @throws Exception
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
/**
* 签名
*
* @param data
* 待签名数据
* @param privateKey
* 私钥
* @return byte[] 数字签名
* @throws Exception
*/
public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initSign(priKey);
// 更新
signature.update(data);
// 签名
return signature.sign();
}
/**
* 校验
*
* @param data
* 待校验数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
* @return boolean 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
// 加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
// 转换公钥材料
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
// 实例化密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(keySpec);
// 实例化Signature
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
// 初始化Signature
signature.initVerify(pubKey);
// 更新
signature.update(data);
// 验证
return signature.verify(sign);
}
}
四、数字证书
1、介绍
数字证书有加密解密的必要信息,包含签名算法,可用于网络数据加密解密交互、标识网络用户身份,也是加密算法与公钥的载体。 目前数字证书中最常用的非对称加密算法是RSA,与之配套的签名算法是SHA1withRSA 数字证书(Digital Certificate)也称为电子证书,用于标识网络中的用户身份,一般有数字证书颁发认证机构(Certificate Authority CA)签发,VeriSign、VeoTrust和Thawte是三大CA机构。 通过CA机构颁发的数字证书,可以对网络上传输的数据进行加密、解密、签名和验证操作,确保数据的机密性、完整性和防抵赖,同时数字证书包含的用户信息可以明确的标识用户身份,可以保证交易者的真实身份。 数字证书采用了公钥基础设施(public key infrastructure pki),使用相应的加密算法确保网络应用的安全,主要体现在如下几个方面。
- 使用非对称加密算法对数据进行加密解密,确保数据的机密性
- 使用数字签名算法对数据进行签名和验证,确保数据的完整性与防抵赖
- 使用消息摘要算法对数字证书本身做摘要,确保数字证书的完整性
数字证书编码格式有多种,主要包含CER和DER。 数字证书需要符合PKI制定的ITU-T X509国际标准。
2、证书签发过程
其文字描述如下: 1、数字证书申请者生成密钥对 2、数字证书申请者将算法、公钥和证书申请者身份信息发送至CA 3、CA核实用户身份 4、CA颁发数字证书给申请者
3、示例
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
/**
* @author alan 2018年11月21日
*/
public class CertificateCoder {
/**
* 类型证书X509
*/
public static final String CERT_TYPE = "X.509";
/**
* 由KeyStore获得私钥
*
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return PrivateKey 私钥
* @throws Exception
*/
private static PrivateKey getPrivateKeyByKeyStore(String keyStorePath, String alias, String password) throws Exception {
// 获得密钥库
KeyStore ks = getKeyStore(keyStorePath, password);
// 获得私钥
return (PrivateKey) ks.getKey(alias, password.toCharArray());
}
/**
* 由Certificate获得公钥
*
* @param certificatePath 证书路径
* @return PublicKey 公钥
* @throws Exception
*/
private static PublicKey getPublicKeyByCertificate(String certificatePath) throws Exception {
// 获得证书
Certificate certificate = getCertificate(certificatePath);
// 获得公钥
return certificate.getPublicKey();
}
/**
* 获得Certificate
*
* @param certificatePath 证书路径
* @return Certificate 证书
* @throws Exception
*/
private static Certificate getCertificate(String certificatePath) throws Exception {
// 实例化证书工厂
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERT_TYPE);
// 取得证书文件流
FileInputStream in = new FileInputStream(certificatePath);
// 生成证书
Certificate certificate = certificateFactory.generateCertificate(in);
// 关闭证书文件流
in.close();
return certificate;
}
/**
* 获得Certificate
*
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return Certificate 证书
* @throws Exception
*/
private static Certificate getCertificate(String keyStorePath, String alias, String password) throws Exception {
// 获得密钥库
KeyStore ks = getKeyStore(keyStorePath, password);
// 获得证书
return ks.getCertificate(alias);
}
/**
* 获得KeyStore
*
* @param keyStorePath 密钥库路径
* @param password 密码
* @return KeyStore 密钥库
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password) throws Exception {
// 实例化密钥库
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// 获得密钥库文件流
FileInputStream is = new FileInputStream(keyStorePath);
// 加载密钥库
ks.load(is, password.toCharArray());
// 关闭密钥库文件流
is.close();
return ks;
}
/**
* 私钥加密
*
* @param data 待加密数据
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias, password);
// 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*
* @param data 待解密数据
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias, password);
// 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
*
* @param data 待加密数据
* @param certificatePath 证书路径
* @return byte[] 加密数据
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String certificatePath) throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data 待解密数据
* @param certificatePath 证书路径
* @return byte[] 解密数据
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String certificatePath) throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKeyByCertificate(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 签名
*
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return byte[] 签名
* @throws Exception
*/
public static byte[] sign(byte[] sign, String keyStorePath, String alias, String password) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password);
// 构建签名,由证书指定签名算法
Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
// 获取私钥
PrivateKey privateKey = getPrivateKeyByKeyStore(keyStorePath, alias, password);
// 初始化签名,由私钥构建
signature.initSign(privateKey);
signature.update(sign);
return signature.sign();
}
/**
* 验证签名
*
* @param data 数据
* @param sign 签名
* @param certificatePath 证书路径
* @return boolean 验证通过为真
* @throws Exception
*/
public static boolean verify(byte[] data, byte[] sign, String certificatePath) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
// 由证书构建签名
Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
// 由证书初始化签名,实际上是使用了证书中的公钥
signature.initVerify(x509Certificate);
signature.update(data);
return signature.verify(sign);
}
}
以上,简单的介绍了数据安全的概念、加密解密常见的算法、数字签名介绍、数字证书的介绍及相关示例,接下来会有针对每个具体应用详细介绍。