各种Java加密算法 Evergreen 2021年01月08日 加密 预计阅读 32 分钟 原文地址:https://www.open-open.com/lib/view/open1397274257325.html 如基本的单向加密算法: - BASE64 严格地说,属于编码格式,而非加密算法 - MD5(Message Digest algorithm 5,信息摘要算法) - SHA(Secure Hash Algorithm,安全散列算法) - HMAC(Hash Message Authentication Code,散列消息鉴别码) 复杂的对称加密(DES、PBE)、非对称加密算法: - DES(Data Encryption Standard,数据加密算法) - PBE(Password-based encryption,基于密码验证) - RSA(算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman) - DH(Diffie-Hellman算法,密钥一致协议) - DSA(Digital Signature Algorithm,数字签名) - ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学) 本篇内容简要介绍**BASE64**、**MD5**、**SHA**、**HMAC**几种方法。 **MD5**、**SHA**、**HMAC**这三种加密算法,可谓是非可逆加密,就是不可解密的加密方法。我们通常只把他们作为加密的基础。单纯的以上三种的加密并不可靠。 #### BASE64 按 照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.) > `常见于邮件、http加密,截取http信息,你就会发现登录操作的用户名、密码字段通过BASE64加密的`。  java实现: ```java /** * BASE64解密 * * @param key * @return * @throws Exception */ public static byte[] decryptBASE64(String key) throws Exception { return (new BASE64Decoder()).decodeBuffer(key); } /** * BASE64加密 * * @param key * @return * @throws Exception */ public static String encryptBASE64(byte[] key) throws Exception { return (new BASE64Encoder()).encodeBuffer(key); } ``` 主要就是BASE64Encoder、BASE64Decoder两个类,我们只需要知道使用对应的方法即可。另外,BASE加密后产生的字节位数是`8的倍数`,如果`不够`位数以`=符号`填充。 #### MD5 MD5 -- message-digest algorithm 5 (信息-摘要算法)缩写,广泛`用于加密和解密技术`,`常用于文件校验`。校验不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文件是否一致的。  java实现: ```Java public static final String KEY_MD5 = "MD5"; /** * MD5加密 * * @param data * @return * @throws Exception */ public static byte[] encryptMD5(byte[] data) throws Exception { MessageDigest md5 = MessageDigest.getInstance(KEY_MD5); md5.update(data); return md5.digest(); } ``` 通常我们`不直接使用`上述MD5加密。通常将MD5产生的字节数组`交给BASE64再加密一把`,得到相应的字符串。 #### SHA SHA(Secure Hash Algorithm,安全散列算法),`数字签名等密码学应用中`重要的工具,被广泛地应`用于电子商务等信息安全领域`。虽然,`SHA与MD5通过碰撞法都被破解了`, 但是SHA仍然是公认的安全加密算法,`较之MD5更为安全`。  java实现: ```Java public static final String KEY_SHA = "SHA"; /** * SHA加密 * * @param data * @return * @throws Exception */ public static byte[] encryptSHA(byte[] data) throws Exception { MessageDigest sha = MessageDigest.getInstance(KEY_SHA); sha.update(data); return sha.digest(); } } ``` #### HMAC HMAC(Hash Message Authentication Code,`散列消息鉴别码`,基于密钥的`Hash算法的认证协议`。消息鉴别码实现鉴别的原理是,`用公开函数和密钥产生一个固定长度的值作为认证标识`,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。`接收方利用与发送方共享的密钥进行鉴别认证`等。  java实现: ```java /** * MAC算法可选以下多种算法 * * * HmacMD5 * HmacSHA1 * HmacSHA256 * HmacSHA384 * HmacSHA512 * */ public static final String KEY_MAC = "HmacMD5"; /** * 初始化HMAC密钥 * * @return * @throws Exception */ public static String initMacKey() throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC); SecretKey secretKey = keyGenerator.generateKey(); return encryptBASE64(secretKey.getEncoded()); } /** * HMAC加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encryptHMAC(byte[] data, String key) throws Exception { SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); return mac.doFinal(data); } ``` 注意:`BASE64Encoder 和BASE64Decoder是非官方JDK实现类`。虽然可以在JDK里能找到并使用,但是在API里查不到。JRE 中 sun 和 com.sun 开头包的类都是未被文档化的,他们属于 java, javax 类库的基础,其中的实现大多数与底层平台有关,一般来说是`不推荐使用`的。 `BASE64的加密解密是双向的`,可以求反解。` MD5、SHA以及HMAC是单向加密`,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传输过程中是否被修改。其中HMAC算法有一个密钥,增强了数据传输过程中的安全性,强化了算法外的不可控因素。 `单向加密的用途主要是为了校验数据在传输过程中是否被修改`。 接下来我们介绍`对称加密算法`,最常用的莫过于`DES数据加密算法`。 #### DES DES-Data Encryption Standard,即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中 Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:`加密 或解密`。 `DES算法把64位的明文输入块变为64位的密文输出块`,它所使用的密钥也是64位。 Coder类为上面非对称加密算法的类。 ```java 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; /** * DES安全编码组件 * * * 支持 DES、DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR) * DES key size must be equal to 56 * DESede(TripleDES) key size must be equal to 112 or 168 * AES key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available * Blowfish key size must be multiple of 8, and can only range from 32 to 448 (inclusive) * RC2 key size must be between 40 and 1024 bits * RC4(ARCFOUR) key size must be between 40 and 1024 bits * 具体内容 需要关注 JDK Document http://.../docs/technotes/guides/security/SunProviders.html * * * @version 1.0 * @since 1.0 */ public abstract class DESCoder extends Coder { /** * ALGORITHM 算法 * 可替换为以下任意一种算法,同时key值的size相应改变。 * * * DES key size must be equal to 56 * DESede(TripleDES) key size must be equal to 112 or 168 * AES key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available * Blowfish key size must be multiple of 8, and can only range from 32 to 448 (inclusive) * RC2 key size must be between 40 and 1024 bits * RC4(ARCFOUR) key size must be between 40 and 1024 bits * * * 在Key toKey(byte[] key)方法中使用下述代码 * SecretKey secretKey = new SecretKeySpec(key, ALGORITHM); 替换 * * DESKeySpec dks = new DESKeySpec(key); * SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); * SecretKey secretKey = keyFactory.generateSecret(dks); * */ public static final String ALGORITHM = "DES"; /** * 转换密钥 * * @param key * @return * @throws Exception */ private static Key toKey(byte[] key) throws Exception { DESKeySpec dks = new DESKeySpec(key); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); SecretKey secretKey = keyFactory.generateSecret(dks); // 当使用其他对称加密算法时,如AES、Blowfish等算法时,用下述代码替换上述三行代码 // SecretKey secretKey = new SecretKeySpec(key, ALGORITHM); return secretKey; } /** * 解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decrypt(byte[] data, String key) throws Exception { Key k = toKey(decryptBASE64(key)); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, k); return cipher.doFinal(data); } /** * 加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encrypt(byte[] data, String key) throws Exception { Key k = toKey(decryptBASE64(key)); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, k); return cipher.doFinal(data); } /** * 生成密钥 * * @return * @throws Exception */ public static String initKey() throws Exception { return initKey(null); } /** * 生成密钥 * * @param seed * @return * @throws Exception */ public static String initKey(String seed) throws Exception { SecureRandom secureRandom = null; if (seed != null) { secureRandom = new SecureRandom(decryptBASE64(seed)); } else { secureRandom = new SecureRandom(); } KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM); kg.init(secureRandom); SecretKey secretKey = kg.generateKey(); return encryptBASE64(secretKey.getEncoded()); } } ``` 其实DES有很多同胞兄弟,如DESede(TripleDES)、AES、Blowfish、RC2、RC4(ARCFOUR)。这里就不过多阐述了,大同小异,`只要换掉ALGORITHM换成对应的值`,同时做一个代码替换**SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);**就可以了,此外就是`密钥长度不同了`。 ```java /** * DES key size must be equal to 56 * DESede(TripleDES) key size must be equal to 112 or 168 * AES key size must be equal to 128, 192 or 256,but 192 and 256 bits may not be available * Blowfish key size must be multiple of 8, and can only range from 32 to 448 (inclusive) * RC2 key size must be between 40 and 1024 bits * RC4(ARCFOUR) key size must be between 40 and 1024 bits **/ ``` 除了DES,我们还知道有DESede(TripleDES,就是3DES)、AES、Blowfish、RC2、RC4(ARCFOUR)等多种对称加密方式,其实现方式大同小异,这里介绍对称加密的另一个算法——PBE 。 #### PBE PBE——Password-based encryption(基于密码加密)。其`特点在于口令由用户自己掌管`,不借助任何物理媒体;采用随机数(这里我们叫做盐)杂凑多重加密等方法保证数据的安全性。是一种简便的加密方式。 ```java import java.security.Key; import java.util.Random; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; /** * PBE安全编码组件 */ public abstract class PBECoder extends Coder { /** * 支持以下任意一种算法 * * * PBEWithMD5AndDES * PBEWithMD5AndTripleDES * PBEWithSHA1AndDESede * PBEWithSHA1AndRC2_40 * */ public static final String ALGORITHM = "PBEWITHMD5andDES"; /** * 盐初始化 * * @return * @throws Exception */ public static byte[] initSalt() throws Exception { byte[] salt = new byte[8]; Random random = new Random(); random.nextBytes(salt); return salt; } /** * 转换密钥 * * @param password * @return * @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 * @throws Exception */ public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception { Key key = toKey(password); 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 * @throws Exception */ public static byte[] decrypt(byte[] data, String password, byte[] salt) throws Exception { Key key = toKey(password); PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, key, paramSpec); return cipher.doFinal(data); } } ``` #### RSA 这种算法1978年就出现了,它是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。算法的名字以发明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。 这种加密算法的特点主要是密钥的变化,上文我们看到DES只有一个密钥。相当于只有一把钥匙,如果这把钥匙丢了,数据也就不安全了。RSA同时有两把钥 匙,`公钥与私钥`。同时`支持数字签名。数字签名的意义在于,对传输过来的数据进行校验。确保数据在传输工程中不被修改`。 **流程分析:** 1. 甲方构建密钥对儿,将公钥公布给乙方,将私钥保留。 2. 甲方使用私钥加密数据,然后用私钥对加密后的数据签名,发送给乙方签名以及加密后的数据;乙方使用公钥、签名来验证待解密数据是否有效,如果有效使用公钥对数据解密。 3. 乙方使用公钥加密数据,向甲方发送经过加密后的数据;甲方获得加密数据,通过私钥解密。 按如上步骤给出序列图,如下:    ```java 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; import javax.crypto.Cipher; /** * RSA安全编码组件 * */ public abstract class RSACoder extends Coder { public static final String KEY_ALGORITHM = "RSA"; public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; private static final String PUBLIC_KEY = "RSAPublicKey"; private static final String PRIVATE_KEY = "RSAPrivateKey"; /** * 用私钥对信息生成数字签名 * * @param data 加密数据 * @param privateKey 私钥 * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { // 解密由base64编码的私钥 byte[] keyBytes = decryptBASE64(privateKey); // 构造PKCS8EncodedKeySpec对象 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取私钥匙对象 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); // 用私钥对信息生成数字签名 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(priKey); signature.update(data); return encryptBASE64(signature.sign()); } /** * 校验数字签名 * * @param data 加密数据 * @param publicKey 公钥 * @param sign 数字签名 * @return 校验成功返回true 失败返回false * @throws Exception */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { // 解密由base64编码的公钥 byte[] keyBytes = decryptBASE64(publicKey); // 构造X509EncodedKeySpec对象 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取公钥匙对象 PublicKey pubKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(pubKey); signature.update(data); // 验证签名是否正常 return signature.verify(decryptBASE64(sign)); } /** * 解密 * 用私钥解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = decryptBASE64(key); // 取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); 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 * @throws Exception */ public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = decryptBASE64(key); // 取得公钥 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key 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 * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception { // 对公钥解密 byte[] keyBytes = decryptBASE64(key); // 取得公钥 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); 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); } /** * 加密 * 用私钥加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = decryptBASE64(key); // 取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 对数据加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 取得私钥 * * @param keyMap * @return * @throws Exception */ public static String getPrivateKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded()); } /** * 取得公钥 * * @param keyMap * @return * @throws Exception */ public static String getPublicKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded()); } /** * 初始化密钥 * * @return * @throws Exception */ public static Map initKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator .getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); // 公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map keyMap = new HashMap(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } } ``` 简要总结一下,使用公钥加密、私钥解密,完成了乙方到甲方的一次数据传递,通过私钥加密、公钥解密,同时通过私钥签名、公钥验证签名,完成了一次甲方到乙方的数据传递与验证,两次数据传递完成一整套的数据交互! 类似数字签名,数字信封是这样描述的: **数字信封** 数字信封用加密技术来保证只有特定的收信人才能阅读信的内容。 流程: 信息发送方采用对称密钥来加密信息,然后再用接收方的公钥来加密此对称密钥(这部分称为数字信封),再将它和信息一起发送给接收方;接收方先用相应的私钥打开数字信封,得到对称密钥,然后使用对称密钥再解开信息。 #### DH Diffie- Hellman算法(D-H算法),密钥一致协议。是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用 户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说,就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私 钥)。以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥 (SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单 单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!该算法源于中国的同余定理——中国馀数定理。 `一种适基于密钥一致协议的加密算法`。 流程分析: 1.甲方构建密钥对儿,将公钥公布给乙方,将私钥保留;双方约定数据加密算法;乙方通过甲方公钥构建密钥对儿,将公钥公布给甲方,将私钥保留。 2.甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给乙方加密后的数据;乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。 3.乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥加密数据,发送给甲方加密后的数据;甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥,然后通过本地密钥对数据解密。    ```java import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PublicKey; 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; /** * DH安全编码组件 */ public abstract class DHCoder extends Coder { public static final String ALGORITHM = "DH"; /** * 默认密钥字节数 * * * DH * Default Keysize 1024 * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive). * */ private static final int KEY_SIZE = 1024; /** * DH加密下需要一种对称加密算法对数据加密,这里我们使用DES,也可以使用其他对称加密算法。 */ public static final String SECRET_ALGORITHM = "DES"; private static final String PUBLIC_KEY = "DHPublicKey"; private static final String PRIVATE_KEY = "DHPrivateKey"; /** * 初始化甲方密钥 * * @return * @throws Exception */ public static Map initKey() throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator .getInstance(ALGORITHM); keyPairGenerator.initialize(KEY_SIZE); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 甲方公钥 DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic(); // 甲方私钥 DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate(); Map keyMap = new HashMap(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 初始化乙方密钥 * * @param key 甲方公钥 * @return * @throws Exception */ public static Map initKey(String key) throws Exception { // 解析甲方公钥 byte[] keyBytes = decryptBASE64(key); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); // 由甲方公钥构建乙方密钥 DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams(); KeyPairGenerator keyPairGenerator = KeyPairGenerator .getInstance(keyFactory.getAlgorithm()); keyPairGenerator.initialize(dhParamSpec); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 乙方公钥 DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic(); // 乙方私钥 DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate(); Map keyMap = new HashMap(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } /** * 加密 * * @param data 待加密数据 * @param publicKey 甲方公钥 * @param privateKey 乙方私钥 * @return * @throws Exception */ public static byte[] encrypt(byte[] data, String publicKey, String privateKey) throws Exception { // 生成本地密钥 SecretKey secretKey = getSecretKey(publicKey, privateKey); // 数据加密 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data); } /** * 解密 * * @param data 待解密数据 * @param publicKey 乙方公钥 * @param privateKey 乙方私钥 * @return * @throws Exception */ public static byte[] decrypt(byte[] data, String publicKey, String privateKey) throws Exception { // 生成本地密钥 SecretKey secretKey = getSecretKey(publicKey, privateKey); // 数据解密 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(data); } /** * 构建密钥 * * @param publicKey 公钥 * @param privateKey 私钥 * @return * @throws Exception */ private static SecretKey getSecretKey(String publicKey, String privateKey) throws Exception { // 初始化公钥 byte[] pubKeyBytes = decryptBASE64(publicKey); KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes); PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); // 初始化私钥 byte[] priKeyBytes = decryptBASE64(privateKey); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes); Key priKey = keyFactory.generatePrivate(pkcs8KeySpec); KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory .getAlgorithm()); keyAgree.init(priKey); keyAgree.doPhase(pubKey, true); // 生成本地密钥 SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM); return secretKey; } /** * 取得私钥 * * @param keyMap * @return * @throws Exception */ public static String getPrivateKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded()); } /** * 取得公钥 * * @param keyMap * @return * @throws Exception */ public static String getPublicKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded()); } } ``` #### DSA DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。简单的说,这是一种更高级的验证方式,用作数字签名。`不单单只有公钥、私钥,还有数字签名`。私钥加密生成数字签名,公钥验证数据及签 名。如果数据和签名不匹配则认为验证失败!数字签名的作用就是校验数据在传输过程中不被修改。数字签名,是单向加密的升级! `DSA数字签名,非对称加密的另一种实现`。   ```java 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; /** * DSA安全编码组件 */ public abstract class DSACoder extends Coder { public static final String ALGORITHM = "DSA"; /** * 默认密钥字节数 * * * DSA * Default Keysize 1024 * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive). * */ private static final int KEY_SIZE = 1024; /** * 默认种子 */ private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3"; private static final String PUBLIC_KEY = "DSAPublicKey"; private static final String PRIVATE_KEY = "DSAPrivateKey"; /** * 用私钥对信息生成数字签名 * * @param data 加密数据 * @param privateKey 私钥 * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { // 解密由base64编码的私钥 byte[] keyBytes = decryptBASE64(privateKey); // 构造PKCS8EncodedKeySpec对象 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); // 取私钥匙对象 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); // 用私钥对信息生成数字签名 Signature signature = Signature.getInstance(keyFactory.getAlgorithm()); signature.initSign(priKey); signature.update(data); return encryptBASE64(signature.sign()); } /** * 校验数字签名 * * @param data 加密数据 * @param publicKey 公钥 * @param sign 数字签名 * @return 校验成功返回true 失败返回false * @throws Exception */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { // 解密由base64编码的公钥 byte[] keyBytes = decryptBASE64(publicKey); // 构造X509EncodedKeySpec对象 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); // 取公钥匙对象 PublicKey pubKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(keyFactory.getAlgorithm()); signature.initVerify(pubKey); signature.update(data); // 验证签名是否正常 return signature.verify(decryptBASE64(sign)); } /** * 生成密钥 * * @param seed 种子 * @return 密钥对象 * @throws Exception */ public static Map initKey(String seed) throws Exception { KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM); // 初始化随机产生器 SecureRandom secureRandom = new SecureRandom(); secureRandom.setSeed(seed.getBytes()); keygen.initialize(KEY_SIZE, secureRandom); KeyPair keys = keygen.genKeyPair(); DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic(); DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate(); Map map = new HashMap(2); map.put(PUBLIC_KEY, publicKey); map.put(PRIVATE_KEY, privateKey); return map; } /** * 默认生成密钥 * * @return 密钥对象 * @throws Exception */ public static Map initKey() throws Exception { return initKey(DEFAULT_SEED); } /** * 取得私钥 * * @param keyMap * @return * @throws Exception */ public static String getPrivateKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded()); } /** * 取得公钥 * * @param keyMap * @return * @throws Exception */ public static String getPublicKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded()); } } ``` #### ECC ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制。在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生。 当我开始整理《Java加密技术(二)》的时候,我就已经在开始研究ECC了,但是关于Java实现ECC算法的资料实在是太少了,无论是国内还是国外的 资料,无论是官方还是非官方的解释,最终只有一种答案——ECC算法在jdk1.5后加入支持,目前仅仅只能完成密钥的生成与解析。 如果想要获得ECC算法实现,需要调用硬件完成加密/解密(ECC算法相当耗费资源,如果单纯使用CPU进行加密/解密,效率低下),涉及到Java Card领域,PKCS#11。 其实,PKCS#11配置很简单,但缺乏硬件设备,无法尝试! ```java import java.math.BigInteger; import java.security.Key; import java.security.KeyFactory; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECFieldF2m; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; import java.security.spec.EllipticCurve; 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.NullCipher; import sun.security.ec.ECKeyFactory; import sun.security.ec.ECPrivateKeyImpl; import sun.security.ec.ECPublicKeyImpl; /** * ECC安全编码组件 */ public abstract class ECCCoder extends Coder { public static final String ALGORITHM = "EC"; private static final String PUBLIC_KEY = "ECCPublicKey"; private static final String PRIVATE_KEY = "ECCPrivateKey"; /** * 解密 * 用私钥解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decrypt(byte[] data, String key) throws Exception { // 对密钥解密 byte[] keyBytes = decryptBASE64(key); // 取得私钥 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = ECKeyFactory.INSTANCE; ECPrivateKey priKey = (ECPrivateKey) keyFactory .generatePrivate(pkcs8KeySpec); ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(), priKey.getParams()); // 对数据解密 // TODO Chipher不支持EC算法 未能实现 Cipher cipher = new NullCipher(); // Cipher.getInstance(ALGORITHM, keyFactory.getProvider()); cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams()); return cipher.doFinal(data); } /** * 加密 * 用公钥加密 * * @param data * @param privateKey * @return * @throws Exception */ public static byte[] encrypt(byte[] data, String privateKey) throws Exception { // 对公钥解密 byte[] keyBytes = decryptBASE64(privateKey); // 取得公钥 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = ECKeyFactory.INSTANCE; ECPublicKey pubKey = (ECPublicKey) keyFactory .generatePublic(x509KeySpec); ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(), pubKey.getParams()); // 对数据加密 // TODO Chipher不支持EC算法 未能实现 Cipher cipher = new NullCipher(); // Cipher.getInstance(ALGORITHM, keyFactory.getProvider()); cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams()); return cipher.doFinal(data); } /** * 取得私钥 * * @param keyMap * @return * @throws Exception */ public static String getPrivateKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded()); } /** * 取得公钥 * * @param keyMap * @return * @throws Exception */ public static String getPublicKey(Map keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded()); } /** * 初始化密钥 * * @return * @throws Exception */ public static Map initKey() throws Exception { BigInteger x1 = new BigInteger( "2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16); BigInteger x2 = new BigInteger( "289070fb05d38ff58321f2e800536d538ccdaa3d9", 16); ECPoint g = new ECPoint(x1, x2); // the order of generator BigInteger n = new BigInteger( "5846006549323611672814741753598448348329118574063", 10); // the cofactor int h = 2; int m = 163; int[] ks = {7, 6, 3}; ECFieldF2m ecField = new ECFieldF2m(m, ks); // y^2+xy=x^3+x^2+1 BigInteger a = new BigInteger("1", 2); BigInteger b = new BigInteger("1", 2); EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b); ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g, n, h); // 公钥 ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec); BigInteger s = new BigInteger( "1234006549323611672814741753598448348329118574063", 10); // 私钥 ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec); Map keyMap = new HashMap(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } ``` > 请注意上述代码中的**TODO**内容,再次提醒注意,Chipher不支持EC算法 ,以上代码仅供参考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。为了确保程序能够正常执行,我们使用了NullCipher类,验证程序。 #### Java证书体系的实现 在构建Java代码实现前,我们需要完成证书的制作。 1.生成keyStroe文件 在命令行下执行以下命令: ``` keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:\zlex.keystore ``` 其中 **-genkey**表示生成密钥 **-validity**指定证书有效期,这里是**36000**天 **-alias**指定别名,这里是**www.zlex.org** **-keyalg**指定算法,这里是**RSA** **-keystore**指定存储位置,这里是**d:\zlex.keystore** 在这里我使用的密码为 **123456** 控制台输出: ``` 输入keystore密码: 再次输入新密码: 您的名字与姓氏是什么? [Unknown]: www.zlex.org 您的组织单位名称是什么? [Unknown]: zlex 您的组织名称是什么? [Unknown]: zlex 您所在的城市或区域名称是什么? [Unknown]: BJ 您所在的州或省份名称是什么? [Unknown]: BJ 该单位的两字母国家代码是什么 [Unknown]: CN CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗? [否]: Y 输入的主密码 (如果和 keystore 密码相同,按回车): 再次输入新密码: ``` 这时,在D盘下会生成一个zlex.keystore的文件。 2.生成自签名证书 光有keyStore文件是不够的,还需要证书文件,证书才是直接提供给外界使用的公钥凭证。 导出证书: ``` keytool -export -keystore d:\zlex.keystore -alias www.zlex.org -file d:\zlex.cer -rfc ``` 其中 **-export**指定为导出操作 **-keystore**指定**keystore文件** **-alias**指定导出**keystore文件中的别名** **-file**指向**导出路径** **-rfc**以文本格式输出,也就是以**BASE64编码**输出 这里的密码是 **123456** 控制台输出: ``` 输入keystore密码: 保存在文件中的认证 ``` 当然,使用方是需要导入证书的! 可以通过自签名证书完成CAS单点登录系统的构建! ```java 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 java.util.Date; import javax.crypto.Cipher; /** * 证书组件 * * @author 梁栋 * @version 1.0 * @since 1.0 */ public abstract class CertificateCoder extends Coder { /** * Java密钥库(Java Key Store,JKS)KEY_STORE */ public static final String KEY_STORE = "JKS"; public static final String X509 = "X.509"; /** * 由KeyStore获得私钥 * * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ private static PrivateKey getPrivateKey(String keyStorePath, String alias, String password) throws Exception { KeyStore ks = getKeyStore(keyStorePath, password); PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray()); return key; } /** * 由Certificate获得公钥 * * @param certificatePath * @return * @throws Exception */ private static PublicKey getPublicKey(String certificatePath) throws Exception { Certificate certificate = getCertificate(certificatePath); PublicKey key = certificate.getPublicKey(); return key; } /** * 获得Certificate * * @param certificatePath * @return * @throws Exception */ private static Certificate getCertificate(String certificatePath) throws Exception { CertificateFactory certificateFactory = CertificateFactory .getInstance(X509); FileInputStream in = new FileInputStream(certificatePath); Certificate certificate = certificateFactory.generateCertificate(in); in.close(); return certificate; } /** * 获得Certificate * * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ private static Certificate getCertificate(String keyStorePath, String alias, String password) throws Exception { KeyStore ks = getKeyStore(keyStorePath, password); Certificate certificate = ks.getCertificate(alias); return certificate; } /** * 获得KeyStore * * @param keyStorePath * @param password * @return * @throws Exception */ private static KeyStore getKeyStore(String keyStorePath, String password) throws Exception { FileInputStream is = new FileInputStream(keyStorePath); KeyStore ks = KeyStore.getInstance(KEY_STORE); ks.load(is, password.toCharArray()); is.close(); return ks; } /** * 私钥加密 * * @param data * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) throws Exception { // 取得私钥 PrivateKey privateKey = getPrivateKey(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 * @throws Exception */ public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password) throws Exception { // 取得私钥 PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); // 对数据加密 Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 公钥加密 * * @param data * @param certificatePath * @return * @throws Exception */ public static byte[] encryptByPublicKey(byte[] data, String certificatePath) throws Exception { // 取得公钥 PublicKey publicKey = getPublicKey(certificatePath); // 对数据加密 Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 公钥解密 * * @param data * @param certificatePath * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] data, String certificatePath) throws Exception { // 取得公钥 PublicKey publicKey = getPublicKey(certificatePath); // 对数据加密 Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 验证Certificate * * @param certificatePath * @return */ public static boolean verifyCertificate(String certificatePath) { return verifyCertificate(new Date(), certificatePath); } /** * 验证Certificate是否过期或无效 * * @param date * @param certificatePath * @return */ public static boolean verifyCertificate(Date date, String certificatePath) { boolean status = true; try { // 取得证书 Certificate certificate = getCertificate(certificatePath); // 验证证书是否过期或无效 status = verifyCertificate(date, certificate); } catch (Exception e) { status = false; } return status; } /** * 验证证书是否过期或无效 * * @param date * @param certificate * @return */ private static boolean verifyCertificate(Date date, Certificate certificate) { boolean status = true; try { X509Certificate x509Certificate = (X509Certificate) certificate; x509Certificate.checkValidity(date); } catch (Exception e) { status = false; } return status; } /** * 签名 * * @param keyStorePath * @param alias * @param password * @return * @throws Exception */ public static String sign(byte[] sign, String keyStorePath, String alias, String password) throws Exception { // 获得证书 X509Certificate x509Certificate = (X509Certificate) getCertificate( keyStorePath, alias, password); // 获取私钥 KeyStore ks = getKeyStore(keyStorePath, password); // 取得私钥 PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password .toCharArray()); // 构建签名 Signature signature = Signature.getInstance(x509Certificate .getSigAlgName()); signature.initSign(privateKey); signature.update(sign); return encryptBASE64(signature.sign()); } /** * 验证签名 * * @param data * @param sign * @param certificatePath * @return * @throws Exception */ public static boolean verify(byte[] data, String sign, String certificatePath) throws Exception { // 获得证书 X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath); // 获得公钥 PublicKey publicKey = x509Certificate.getPublicKey(); // 构建签名 Signature signature = Signature.getInstance(x509Certificate .getSigAlgName()); signature.initVerify(publicKey); signature.update(data); return signature.verify(decryptBASE64(sign)); } /** * 验证Certificate * * @param keyStorePath * @param alias * @param password * @return */ public static boolean verifyCertificate(Date date, String keyStorePath, String alias, String password) { boolean status = true; try { Certificate certificate = getCertificate(keyStorePath, alias, password); status = verifyCertificate(date, certificate); } catch (Exception e) { status = false; } return status; } /** * 验证Certificate * * @param keyStorePath * @param alias * @param password * @return */ public static boolean verifyCertificate(String keyStorePath, String alias, String password) { return verifyCertificate(new Date(), keyStorePath, alias, password); } } ```
评论区