密码的常用术语:
1.密码体制:由明文空间、密文空间、密钥空间、加密算法和解密算法5部分组成。
2.密码协议:也称为安全协议,是指以密码学为基础的消息交换的通信协议,目的是在网络环境中提供安全的服务。
3.柯克霍夫原则:数据的安全基于密钥而不是算法的保密。即系统的安全取决于密钥,对密钥保密,对算法公开。——现代密码学设计的基本原则。
密码的分类:
按照时间可以分为古典密码和现代密码。
按照加密算法是否公开可以分为受限制的算法和基于密钥的算法(之所以把算法公开主要是防止加密算法的发明人利用它做一些不为人知的事)。
按照密码体制的不同可以分为对称密码、非对称密码。
按照明文的处理方法,可以分为:
- 分组密码:加密时将明文分成固定长度的组,用同一密钥和算法对每一块加密,输出也是固定长度的密文。多用于网络加密。
- 流密码:也称为序列密码。加密时每次加密一位或者一个字节明文。
散列函数(又称为hash函数),可以用来验证数据的完整性。它的特点是:长度不受限制、hash值容易计算、散列的运算是不可逆的。
与散列函数相关的算法有:消息摘要算法(MD5等)、安全散列算法(SHA)、消息认证码算法(MAC)。
数字签名:主要针对以数字的形式存储的消息进行的处理。
OSI与TCP/IP安全体系
业务流填充机制:在数据传输的过程中填入一些额外的信息混淆真实的数据。
Java安全组成、包、及第三方拓展
打开jdk安装目录下的jrelibsecurityjava.security文件:
我们要使用第三地方的加解密的提供者可以在上面增加provider和它的引用。参看文档的上面一部分我们可以使用调用方法的形式,参见文档:
JDK本身提供了3个包:
java.security:消息摘要
javax.crypto:安全消息摘要,消息认证(鉴别)码
java.net.ssl:安全套接字(常用的类:HttpsURLConnection、SSLContext)
第三方java扩展:
Bouncy Castle:支持两种方案:①配置;②调用
Commons Codec:Apache,Base64、二进制、十六进制、字符集编码;URL编码/解码
Base64加密
base64算法是基于64个字符的一种替换算法。base64加密的产生式电子邮件的“历史问题”——邮件只能传输ASCII码。base64加密的应用场景:email、密钥、证书文件。该算法可以由3种方式实现:JDK、Bouncy Castle、Commons Codec。
实现如下:
1 package base64; 2 3 import java.io.IOException; 4 5 import org.apache.commons.codec.binary.Base64; 6 7 import sun.misc.BASE64Decoder; 8 import sun.misc.BASE64Encoder; 9 10 /** 11 * 该类是Base64算法的实现 12 */ 13 14 public class JavaBase64 { 15 16 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 17 18 public static void main(String[] args) throws IOException { 19 System.out.println("原始字符串: " + src); 20 System.out.println(); 21 jdkBase64(); 22 System.out.println(); 23 commonsCodecBase64(); 24 System.out.println(); 25 bouncyCastleBase64(); 26 } 27 28 /** JDK实现Base64编码 */ 29 public static void jdkBase64() throws IOException{ 30 //如果这里没有出现sun的base64可以从Build Path中删除然后添加 31 BASE64Encoder encoder = new BASE64Encoder(); 32 String encode = encoder.encode(src.getBytes());//编码 33 34 BASE64Decoder decoder = new BASE64Decoder(); 35 String decode = new String(decoder.decodeBuffer(encode));//解码 36 37 System.out.println("JDK实现的base64编码: " + encode); 38 System.out.println("JDK实现的base64解码: " + decode); 39 } 40 41 /** Commons Codec实现base64编码 */ 42 public static void commonsCodecBase64() { 43 byte[] encode = Base64.encodeBase64(src.getBytes()); 44 byte[] decode = Base64.decodeBase64(encode); 45 46 System.out.println("Commons Codec实现base64编码: " + new String(encode)); 47 System.out.println("Commons Codec实现base64解码: " + new String(decode)); 48 } 49 50 /**Bouncy Castle实现base64编码 */ 51 public static void bouncyCastleBase64() { 52 byte[] encode = org.bouncycastle.util.encoders.Base64.encode(src.getBytes()); 53 byte[] decode = org.bouncycastle.util.encoders.Base64.decode(encode); 54 55 System.out.println("Bouncy Castle实现base64编码: "+new String(encode)); 56 System.out.println("Bouncy Castle实现base64解码: "+new String(decode)); 57 } 58 }
运行结果(base64比较经典的就是一等号结尾):
消息摘要算法加密
消息摘要算法主要分为3类:MD(Message Digest)、SHA(Secure Hash Algorithm)、MAC(Message Authentication Code),以上3类算法的主要作用是验证数据的完整性——是数字签名的核心算法。
消息摘要算法——MD
MD算法家族有3类MD2、MD4、MD5,MD家族生成的都是128位的信息摘要。
算法 | 摘要长度 | 实现方 |
MD2 | 128 | JDK |
MD4 | 128 | Bouncy Castle |
MD5 | 128 | JDK |
信息摘要算法由于使用的是一种单向函数,所以理论上是不可破解的(山东大学的王晓云教授已经破解了MD5和SHA,所以消息摘要是可以伪造的,只不过难度比较大)。所有MD算法进行摘要的结果都是128为位(32位16进制的数字,因为1位16进制数代表4位二进制数)。
以下是Java中实现的各种MD加密:
1 package md; 2 3 import java.security.MessageDigest; 4 import java.security.NoSuchAlgorithmException; 5 import java.security.Security; 6 7 import org.apache.commons.codec.binary.Hex; 8 import org.apache.commons.codec.digest.DigestUtils; 9 import org.bouncycastle.crypto.Digest; 10 import org.bouncycastle.crypto.digests.MD2Digest; 11 import org.bouncycastle.crypto.digests.MD5Digest; 12 import org.bouncycastle.jce.provider.BouncyCastleProvider; 13 14 public class JavaMD { 15 16 private static String src = "object-oriented"; // 需要加密的原始字符串 17 18 public static void main(String[] args) throws NoSuchAlgorithmException { 19 System.out.println("原始字符串:" + src + " "); 20 21 jdkMD5(); 22 bouncyCastleMD5(); 23 commonsCodecMD5(); 24 System.out.println(); 25 26 jdkMD2(); 27 bouncyCastleMD2(); 28 commonsCodecMD2(); 29 System.out.println(); 30 31 bouncyCastleMD4(); 32 } 33 34 /** jdk实现MD5加密 */ 35 public static void jdkMD5() throws NoSuchAlgorithmException { 36 37 MessageDigest md = MessageDigest.getInstance("MD5"); 38 byte[] md5Bytes = md.digest(src.getBytes()); 39 System.out.println("JDK MD5:" + Hex.encodeHexString(md5Bytes));//利用第三方包将byte数组转化为16进制字符串 40 } 41 42 /** jdk实现MD2加密 */ 43 public static void jdkMD2() throws NoSuchAlgorithmException { 44 45 MessageDigest md = MessageDigest.getInstance("md2"); 46 byte[] md5Bytes = md.digest(src.getBytes()); 47 System.out.println("JDK MD2:" + Hex.encodeHexString(md5Bytes)); 48 } 49 50 /** Bouncy Castle实现MD4加密 */ 51 public static void bouncyCastleMD4() throws NoSuchAlgorithmException{ 52 /*通过这种方式给JDK动态添加一个provider,就可以通过这种方式获得JDK本身不支持的MD4了*/ 53 Security.addProvider(new BouncyCastleProvider()); 54 55 MessageDigest md = MessageDigest.getInstance("md4"); 56 57 byte[] md4Bytes = md.digest(src.getBytes()); 58 System.out.println("bc MD4: " + org.bouncycastle.util.encoders.Hex.toHexString(md4Bytes)); 59 } 60 61 /** Bouncy Castle实现MD5加密 */ 62 public static void bouncyCastleMD5(){ 63 Digest digest = new MD5Digest(); 64 digest.update(src.getBytes(), 0, src.getBytes().length); 65 byte[]md5Bytes = new byte[digest.getDigestSize()]; 66 digest.doFinal(md5Bytes, 0); 67 System.out.println("bc MD5: " + org.bouncycastle.util.encoders.Hex.toHexString(md5Bytes)); 68 } 69 70 /** Bouncy Castle实现MD2加密 */ 71 public static void bouncyCastleMD2(){ 72 Digest digest = new MD2Digest(); 73 digest.update(src.getBytes(), 0, src.getBytes().length); 74 byte[]md2Bytes = new byte[digest.getDigestSize()]; 75 digest.doFinal(md2Bytes, 0); 76 System.out.println("bc MD2: " + org.bouncycastle.util.encoders.Hex.toHexString(md2Bytes)); 77 78 } 79 80 /** Commons Codec 实现MD5加密*/ 81 public static void commonsCodecMD5() { 82 System.out.println("cc MD5: " + DigestUtils.md5Hex(src.getBytes())); 83 } 84 85 /** Commons Codec 实现MD2加密*/ 86 public static void commonsCodecMD2() { 87 System.out.println("cc MD2: " + DigestUtils.md2Hex(src.getBytes())); 88 } 89 }
以上程序的运行结果:
JDK本身提供了MD2和MD5的实现,apache的Commons Codec在JDK的基础上进行了改良,使用Commons Codec提供接口进行MD2和MD5加密将会简单很多。JDK本省并没有提供MD4算法的实现,但是我们可以通过动态添加provider的方式让jdk支持MD4,见以下代码:
1 /* 通过这种方式给JDK动态添加一个provider,就可以通过这种方式获得JDK本身不支持的MD4了 */ 2 Security.addProvider(new BouncyCastleProvider()); 3 4 MessageDigest md = MessageDigest.getInstance("md4"); 5 6 byte[] md4Bytes = md.digest(src.getBytes()); 7 System.out.println("bc MD4: " + org.bouncycastle.util.encoders.Hex.toHexString(md4Bytes));
Bouncy Castle提供了MD2、MD4和MD5的实现,对消息摘要算法的支持比较完善,但是API还是没有Apache的Commons Codec友善。因此,如果我们要进行MD2和MD4实现,最好选用Commons Codec。
MD算法的应用
注册时:
应用程序服务器将用户提交的密码进行MD5即:数据库中存放的用户名是明文,而密码是密文(16进制字符串)摘要算法,得到32位的16进制字符串(密文)。把用户名(明文)和密码(密文)进行信息持久化存储到数据库中,返回注册结果。
登录时:
应用程序服务器同样对密码进行MD5摘要,然后将用户提交的用户名和密码的摘要信息和数据库中存储的信息进行比对,返回登录结果。
消息摘要算法——SHA
安全散列算法,固定长度的摘要信息。被认为是MD5的继承者。是一个系列,包括SHA-1、SHA-2(SHA-224、SHA-256、SHA-384、SHA-512),也就是除了SHA-1,其他的4种都被称为是SHA-2。每种算法的摘要长度和实现方如下:
SHA算法的实现和MD算法的实现大同小异,也是JDK提供了默认的几种实现,apache的Commons Codec在JDK的基础上进行了优化,使其更好用,而Bouncy Castle是JDK的拓展,提供了JDK和Commons Codec没有的SHA-224的实现。
1 package sha; 2 3 import java.security.MessageDigest; 4 import java.security.NoSuchAlgorithmException; 5 6 import org.apache.commons.codec.binary.Hex; 7 import org.apache.commons.codec.digest.DigestUtils; 8 import org.bouncycastle.crypto.Digest; 9 import org.bouncycastle.crypto.digests.SHA1Digest; 10 import org.bouncycastle.crypto.digests.SHA224Digest; 11 import org.bouncycastle.crypto.digests.SHA256Digest; 12 13 public class JavaSHA { 14 15 private static String src = "object-oriente"; // 需要加密的原始字符串 16 17 public static void main(String[] args) throws NoSuchAlgorithmException { 18 19 System.out.println("原始字符串:" + src); 20 21 jdkSHA1(); 22 bouncyCastleSHA1(); 23 commonsCodecSAH1(); 24 System.out.println(); 25 26 bouncyCastleSHA224(); 27 System.out.println(); 28 jdkSHA256(); 29 30 bouncyCastleSHA256(); 31 } 32 33 /** JDK实现sha-1 */ 34 public static void jdkSHA1() throws NoSuchAlgorithmException { 35 MessageDigest md = MessageDigest.getInstance("sha");// sha1算法传入参数为sha 36 byte[] sha1Bytes = md.digest(src.getBytes()); 37 System.out.println("JDK SHA-1: " + Hex.encodeHexString(sha1Bytes)); 38 } 39 40 /** JDK实现sha-256 */ 41 public static void jdkSHA256() throws NoSuchAlgorithmException{ 42 MessageDigest md = MessageDigest.getInstance("sha-256"); 43 md.update(src.getBytes()); 44 System.out.println("JDK SHA-256: " + org.bouncycastle.util.encoders.Hex.toHexString(md.digest())); 45 } 46 47 /** Bouncy Castle实现sha-1 */ 48 public static void bouncyCastleSHA1(){ 49 50 Digest digest = new SHA1Digest(); 51 digest.update(src.getBytes(), 0, src.getBytes().length); 52 byte[]sha1Bytes = new byte[digest.getDigestSize()]; 53 digest.doFinal(sha1Bytes, 0); 54 System.out.println("bc SHA-1: " + org.bouncycastle.util.encoders.Hex.toHexString(sha1Bytes)); 55 } 56 57 /** Bouncy Castle实现sha-224 */ 58 public static void bouncyCastleSHA224(){ 59 60 Digest digest = new SHA224Digest(); 61 digest.update(src.getBytes(), 0, src.getBytes().length); 62 byte[]sha224Bytes = new byte[digest.getDigestSize()]; 63 digest.doFinal(sha224Bytes, 0); 64 System.out.println("bc SHA-224: " + org.bouncycastle.util.encoders.Hex.toHexString(sha224Bytes)); 65 } 66 67 /** Bouncy Castle实现sha-256 */ 68 public static void bouncyCastleSHA256() { 69 Digest digest = new SHA256Digest(); 70 digest.update(src.getBytes(), 0, src.getBytes().length); 71 byte[] sha256Bytes = new byte[digest.getDigestSize()]; 72 digest.doFinal(sha256Bytes, 0); 73 System.out.println("bc SHA-256: " + org.bouncycastle.util.encoders.Hex.toHexString(sha256Bytes)); 74 } 75 76 /** Commons Codec实现sha-1 */ 77 public static void commonsCodecSAH1(){ 78 79 System.out.println("cc SHA-1: " + DigestUtils.sha1Hex(src.getBytes())); 80 81 /* 采用下面的方式更加方便 */ 82 // System.out.println("cc SHA-1: " + DigestUtils.sha1Hex(src)); 83 84 } 85 }
运行结果:
SHA算法的应用
在浏览器的证书管理器中证书:WEB证书一般采用SHA算法。
消息摘要算法是为了防止消息在传输过程中的篡改。
我们在很多网站上都可以用QQ账号一键登录,通常腾讯会给每一个接入方一个key,可能会约定一个消息传送的格式(例如:http://**?msg=12Hsad74mj&×tamp=1309488734),其中msg=摘要信息+key+时间戳。
消息摘要算法——MAC
MAC(Message Authentication Code),兼容了MD和SHA的特性,并且在它们的基础上加入了密钥。因此MAC也称为HMAC(keyed-Hash Message Authentication Code)含有密钥的散列函数算法。
- MD系列:HmacMD2、HmacMD4、HmacMD5
- SHA系列:HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384、HmacSHA512
例如:常用的Linux客户端SecurityCRT。MAC的算法的提供者如下:
Commons Codec中并没有提供Hmac算法的实现。
下面的程序以JDK本身和Bouncy Castle实现了HmacMD5,如果需要实现其他的加密只需要改变相关的参数即可:
1 package hmac; 2 3 import java.security.InvalidKeyException; 4 import java.security.NoSuchAlgorithmException; 5 6 import javax.crypto.KeyGenerator; 7 import javax.crypto.Mac; 8 import javax.crypto.SecretKey; 9 import javax.crypto.spec.SecretKeySpec; 10 11 import org.apache.commons.codec.DecoderException; 12 import org.apache.commons.codec.binary.Hex; 13 import org.bouncycastle.crypto.digests.MD5Digest; 14 import org.bouncycastle.crypto.macs.HMac; 15 import org.bouncycastle.crypto.params.KeyParameter; 16 17 18 public class JavaHmac { 19 20 private static String src = "object-oriente"; // 需要加密的原始字符串 21 22 public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, DecoderException { 23 24 System.out.println("原始字符串:" + src + " "); 25 jdkHmacMD5(); 26 bouncyCastleHmacMD5(); 27 } 28 29 public static void jdkHmacMD5() throws NoSuchAlgorithmException, InvalidKeyException, DecoderException{ 30 31 //1.得到密钥 32 KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5"); 33 SecretKey secretKey = keyGenerator.generateKey();//生成密钥 34 // byte[] key = secretKey.getEncoded();//获得密钥 35 byte[] key = Hex.decodeHex("aabbccddee".toCharArray()); 36 37 //2.还原密钥 38 SecretKey restoreSecretKey = new SecretKeySpec(key, "hmacMD5"); 39 40 //3.信息摘要 41 Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());//实例化mac 42 mac.init(restoreSecretKey);//初始化mac 43 byte[] hmacMD5Bytes = mac.doFinal(src.getBytes());//执行摘要 44 45 System.out.println("jdkHmacMD5: " + Hex.encodeHexString(hmacMD5Bytes)); 46 } 47 48 public static void bouncyCastleHmacMD5() { 49 50 HMac hmac = new HMac(new MD5Digest()); 51 //生成密钥的时候以aabbccddee为基准 52 hmac.init(new KeyParameter(org.bouncycastle.util.encoders.Hex.decode("aabbccddee"))); 53 hmac.update(src.getBytes(), 0, src.getBytes().length); 54 55 // 执行摘要 56 byte[]hmacMDdBytes = new byte[hmac.getMacSize()]; 57 hmac.doFinal(hmacMDdBytes, 0); 58 59 System.out.println("bcHmacMD5: "+org.bouncycastle.util.encoders.Hex.toHexString(hmacMDdBytes)); 60 } 61 62 }
运行结果:
HMAC算法的应用:
对称加密算法
加密密钥和解密密钥相同,加密运算和解密运算互为逆运算。是一种初等的加密算法。主要的算法有DES(3DES)、AES、PBE、IDEA。
DES
数据加密标准(Data Encryption Standard),IBM提交的算法。
1 package des; 2 3 import java.security.Security; 4 5 import javax.crypto.Cipher; 6 import javax.crypto.KeyGenerator; 7 import javax.crypto.SecretKey; 8 import javax.crypto.SecretKeyFactory; 9 import javax.crypto.spec.DESKeySpec; 10 11 import org.apache.commons.codec.binary.Hex; 12 import org.bouncycastle.jce.provider.BouncyCastleProvider; 13 14 public class JavaDES { 15 16 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 17 18 public static void main(String[] args) throws Exception { 19 System.out.println("原始字符串: " + src); 20 jdkDES(); 21 bouncyCastleDES(); 22 } 23 24 /** jdk实现DES加密 */ 25 public static void jdkDES() throws Exception{ 26 27 //1.生成key 28 KeyGenerator keyGenerator = KeyGenerator.getInstance("des");//密钥生成器 29 keyGenerator.init(56);//指定密钥长度为56位 30 SecretKey secretKey = keyGenerator.generateKey();//用密钥生成器生成密钥 31 byte[] byteKeys = secretKey.getEncoded();//得到密钥的byte数组 32 33 //2.key转换 34 DESKeySpec desKeySpec = new DESKeySpec(byteKeys); 35 SecretKeyFactory factory = SecretKeyFactory.getInstance("des");//秘密密钥工厂 36 SecretKey convertSecretKey = factory.generateSecret(desKeySpec); 37 38 //3.加密 39 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");//见上图的工作模式和填充模式 40 cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);//加密模式 41 byte[]result = cipher.doFinal(src.getBytes()); 42 43 System.out.println("jdk DES加密: " + Hex.encodeHexString(result)); 44 45 //4.解密 46 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);//解密模式 47 result = cipher.doFinal(result); 48 System.out.println("jdk DES解密: " + new String(result)); 49 } 50 51 /** Bouncy Castle实现DES加密 */ 52 public static void bouncyCastleDES() throws Exception{ 53 54 Security.addProvider(new BouncyCastleProvider());//增加provider 55 56 //1.生成key 57 KeyGenerator keyGenerator = KeyGenerator.getInstance("des","BC");//密钥生成器,指定为bouncyCastle默认是sun的JCE 58 keyGenerator.init(56);//指定key长度为56位 59 keyGenerator.getProvider(); 60 SecretKey secretKey = keyGenerator.generateKey();//用密钥生成器生成密钥 61 byte[] byteKeys = secretKey.getEncoded();//得到密钥的byte数组 62 63 //2.key转换 64 DESKeySpec desKeySpec = new DESKeySpec(byteKeys); 65 SecretKeyFactory factory = SecretKeyFactory.getInstance("des");//密钥工厂 66 SecretKey convertSecretKey = factory.generateSecret(desKeySpec); 67 68 //3.加密 69 Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); 70 cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);//加密模式 71 byte[]result = cipher.doFinal(src.getBytes()); 72 73 System.out.println("bc DES加密: " + Hex.encodeHexString(result)); 74 75 //4.解密 76 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);//解密模式 77 result = cipher.doFinal(result); 78 System.out.println("bc DES解密: " + new String(result)); 79 } 80 }
运行结果:
在使用bc进行DES加解密的时候除了需要使用Security.addProvider()方法增加一个BouncyCastle,还需要指定密钥生成器的提供者为BC,如上面的代码的57行,否则会默认使用sun的JCE。应用场景:
3重DES
3DES的好处是密钥长度增加。迭代次数增加。
实现的方式通常由JDK和BC两种,实现的方式和DES的实现方式大同小异:
1 package des; 2 3 import java.security.SecureRandom; 4 import java.security.Security; 5 6 import javax.crypto.Cipher; 7 import javax.crypto.KeyGenerator; 8 import javax.crypto.SecretKey; 9 import javax.crypto.SecretKeyFactory; 10 import javax.crypto.spec.DESedeKeySpec; 11 12 import org.apache.commons.codec.binary.Hex; 13 import org.bouncycastle.jce.provider.BouncyCastleProvider; 14 15 public class Java3DES { 16 17 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 18 19 public static void main(String[] args) throws Exception { 20 System.out.println("原始字符串:" + src + " "); 21 jdk3DES(); 22 bouncyCastle3DES(); 23 } 24 25 /** jdk实现3DES */ 26 public static void jdk3DES() throws Exception{ 27 //1.生成key 28 KeyGenerator keyGenerator = KeyGenerator.getInstance("desede");//密钥生成器 29 // keyGenerator.init(new SecureRandom()); //可以用它生成一个默认长度的key 30 keyGenerator.init(168);//指定密钥长度为112位 31 SecretKey secretKey = keyGenerator.generateKey();//用密钥生成器生成密钥 32 byte[] byteKeys = secretKey.getEncoded();//得到密钥的byte数组 33 34 //2.key转换 35 DESedeKeySpec deSedeKeySpec = new DESedeKeySpec(byteKeys); 36 SecretKeyFactory factory = SecretKeyFactory.getInstance("desede");//秘密密钥工厂 37 SecretKey convertSecretKey = factory.generateSecret(deSedeKeySpec); 38 39 //3.加密 40 Cipher cipher = Cipher.getInstance("desede/ECB/PKCS5Padding"); 41 cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);//加密模式 42 byte[]result = cipher.doFinal(src.getBytes()); 43 44 System.out.println("jdk DES3加密: " + Hex.encodeHexString(result)); 45 46 //4.解密 47 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);//解密模式 48 result = cipher.doFinal(result); 49 System.out.println("jdk DES3解密: " + new String(result) + " "); 50 } 51 52 /** bc实现3DES */ 53 public static void bouncyCastle3DES() throws Exception{ 54 55 Security.addProvider(new BouncyCastleProvider()); 56 57 //1.生成key 58 KeyGenerator keyGenerator = KeyGenerator.getInstance("desede","BC");//密钥生成器[指定provider] 59 keyGenerator.init(new SecureRandom());//指定密钥的长度为默认 60 SecretKey secretKey = keyGenerator.generateKey();//用密钥生成器生成密钥 61 byte[] byteKeys = secretKey.getEncoded();//得到密钥的byte数组 62 63 //2.key转换 64 DESedeKeySpec deSedeKeySpec = new DESedeKeySpec(byteKeys); 65 SecretKeyFactory factory = SecretKeyFactory.getInstance("desede");//秘密密钥工厂 66 SecretKey convertSecretKey = factory.generateSecret(deSedeKeySpec); 67 68 //3.加密 69 Cipher cipher = Cipher.getInstance("desede/ECB/PKCS5Padding"); 70 cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);//加密模式 71 byte[]result = cipher.doFinal(src.getBytes()); 72 73 System.out.println("bc DES3加密: " + Hex.encodeHexString(result)); 74 75 //4.解密 76 cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);//解密模式 77 result = cipher.doFinal(result); 78 System.out.println("bc DES3解密: " + new String(result) + " "); 79 80 } 81 }
运行结果:
AES
AES产生的原因是3重DES的效率比较低而DES的安全性较低。AES是目前使用最多的对称加密算法,AES还有一个优势是至今尚未被破解。AES通常用于移动通信系统的加密以及基于SSH协议的软件(SSH Client、SecurityCRT)的加密。密钥长度以及实现方如下:
无政策限制权限文件是指:因为某些国家的进口管制限制,java发布的运行环境包中的一些加解密有一定的限制(因为美国政府的一些原因)。一般的JDK的jre的security包中的local_policy.jar和US_export_policy.jar都是没有内容的,所以只能使用128位加密。下面是JDK和BC实现的AES加密(步骤和DES类似):
1 package aes; 2 3 import java.security.Key; 4 import java.security.Security; 5 6 import javax.crypto.Cipher; 7 import javax.crypto.KeyGenerator; 8 import javax.crypto.SecretKey; 9 import javax.crypto.spec.SecretKeySpec; 10 11 import org.apache.commons.codec.binary.Base64; 12 import org.bouncycastle.jce.provider.BouncyCastleProvider; 13 14 public class JavaAES { 15 16 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 17 18 public static void main(String[] args) throws Exception { 19 System.out.println("初始字符串:" + src + " "); 20 jdkAES(); 21 bouncyCastleAES(); 22 } 23 24 public static void jdkAES() throws Exception{ 25 26 //1.生成key 27 KeyGenerator keyGenerator = KeyGenerator.getInstance("aes"); 28 keyGenerator.init(128);//初始化key的长度,只能是128, 29 SecretKey secretKey = keyGenerator.generateKey();//生成key 30 byte[] keyBytes = secretKey.getEncoded();//得到key的字节数组 31 32 //2.key的转换 33 Key key = new SecretKeySpec(keyBytes, "aes"); 34 35 //3.加密 36 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//加解密方式+工作模式+填充方式 37 cipher.init(Cipher.ENCRYPT_MODE, key);//以加密模式初始化 38 byte[] result = cipher.doFinal(src.getBytes()); 39 System.out.println("JDK AES加密:" + Base64.encodeBase64String(result)); 40 41 //4.解密 42 cipher.init(Cipher.DECRYPT_MODE, key); 43 result = cipher.doFinal(result); 44 System.out.println("JDK AES解密:" + new String(result)); 45 } 46 47 public static void bouncyCastleAES() throws Exception{ 48 49 Security.addProvider(new BouncyCastleProvider()); 50 KeyGenerator keyGenerator = KeyGenerator.getInstance("aes", "BC"); 51 keyGenerator.init(128);//这里只能指定128,不能使用keyGenerator.init(new SecureRandom()) 52 SecretKey secretKey = keyGenerator.generateKey(); 53 byte[] keyBytes = secretKey.getEncoded(); 54 55 Key key = new SecretKeySpec(keyBytes, "aes"); 56 57 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5padding"); 58 cipher.init(Cipher.ENCRYPT_MODE, key); 59 byte[] result = cipher.doFinal(src.getBytes()); 60 System.out.println("bc AES加密:" + Base64.encodeBase64String(result)); 61 62 cipher.init(Cipher.DECRYPT_MODE, key); 63 result = cipher.doFinal(result); 64 System.out.println("bc AES解密:" + new String(result)); 65 } 66 }
运行结果:
PBE
PBE算法结合了消息摘要算法和对称加密算法的优点,是一种特殊的对称加密算法。Password Based Encryption,基于口令的加密。因为口令是比较好记的,就容易通过穷举、猜测的方式获得口令——针对这种情况,我们采用的方式是加盐(Salt),通过加入一些额外的内容(通常是随机字符)去扰乱。实现的方式有2种:JDK和BC。
JDK和BC实现的PBEWithMD5AndDES的算法如下:
1 package pbe; 2 3 import java.security.Key; 4 import java.security.SecureRandom; 5 import java.security.Security; 6 7 import javax.crypto.Cipher; 8 import javax.crypto.SecretKeyFactory; 9 import javax.crypto.spec.PBEKeySpec; 10 import javax.crypto.spec.PBEParameterSpec; 11 12 import org.apache.commons.codec.binary.Base64; 13 import org.bouncycastle.jce.provider.BouncyCastleProvider; 14 15 public class JavaPBE { 16 17 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 18 19 public static void main(String[] args) throws Exception { 20 System.out.println("原始字符串:" + src); 21 jdkPBE(); 22 bouncyCastlePBE(); 23 } 24 25 public static void jdkPBE() throws Exception{ 26 27 //1.初始化盐 28 SecureRandom secureRandom = new SecureRandom();//强加密随机数生成器 29 byte[] salt= secureRandom.generateSeed(8);//产生盐必须是8位 30 31 //2.口令与密钥 32 String password = "root"; 33 PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());//密钥转换的对象 34 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");//实例化密钥转换工厂 35 Key key = factory.generateSecret(pbeKeySpec);//由工厂产生key 36 37 //3.加密 38 PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 100);//PBE输入参数的材料,盐,迭代100次 39 Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES"); 40 cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); 41 byte[] result = cipher.doFinal(src.getBytes()); 42 System.out.println("jdk PBE加密:" + Base64.encodeBase64String(result)); 43 44 //4.解密 45 cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); 46 result = cipher.doFinal(result); 47 System.out.println("jdk PBE解密:" + new String(result)); 48 } 49 50 public static void bouncyCastlePBE() throws Exception{ 51 52 Security.addProvider(new BouncyCastleProvider());//添加到provider 53 54 SecureRandom secureRandom = new SecureRandom(); 55 byte[] salt = secureRandom.generateSeed(8); 56 57 String password = "admin"; 58 PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); 59 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES","BC");//指定provider 60 Key key = factory.generateSecret(pbeKeySpec); 61 62 PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 50); 63 Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES"); 64 cipher.init(Cipher.ENCRYPT_MODE, key,parameterSpec); 65 byte[]result = cipher.doFinal(src.getBytes()); 66 67 System.out.println("bc PBE加密:" + Base64.encodeBase64String(result)); 68 69 cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); 70 result = cipher.doFinal(result); 71 System.out.println("bc PBE解密:" + new String(result)); 72 } 73 }
运行结果:
应用场景:
非对称加密算法
加密密钥分为公钥和私钥。可以使用公钥加密私钥解密,也可以使用私钥加密公钥解密。非对称加密算法主要有:DH(Diffie-Hellman)密钥交换算法、RSA(基于因子分解)、Elgamal(基于离散对数)、ECC(Elliptical Curve Cryptography,椭圆曲线加密)。
DH(密钥交换)算法
如何安全地传送密钥是对称加密算法的症结所在。密钥交换算法是通过构建本地密钥来解决对称加密算法中的密钥传递的问题的。
实现该算法的步骤和所需要的类如下:
1.初始化发送方密钥
-KeyPairGenerator
-KeyPair(密钥载体,密钥对,包括公约和私钥)
-PublicKey
2.初始化接收方密钥
-KeyFactory(可以生成公钥和私钥)
-X509EncodedKeySpec(根据ASN.1标准进行密钥编码)
-DHPublicKey
-DHParameterSpec
-KeyPairGenerator
-PrivateKey
3.密钥构建
-KeyAgreement(提供密钥一致性或密钥交换协议的功能)
-SecretKey(生成一个分组的秘密密钥)
-KeyFactory
-X509EncodedKeySpec
-PublicKey
4.加解密
-Cipher(JCE框架的核心)
具体实现:
1 package dh; 2 3 import java.security.KeyFactory; 4 import java.security.KeyPair; 5 import java.security.KeyPairGenerator; 6 import java.security.PrivateKey; 7 import java.security.PublicKey; 8 import java.security.spec.X509EncodedKeySpec; 9 import java.util.Objects; 10 11 import javax.crypto.Cipher; 12 import javax.crypto.KeyAgreement; 13 import javax.crypto.SecretKey; 14 import javax.crypto.interfaces.DHPublicKey; 15 import javax.crypto.spec.DHParameterSpec; 16 17 import org.apache.commons.codec.binary.Base64; 18 19 public class JavaDH { 20 21 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 22 23 public static void main(String[] args) throws Exception { 24 System.out.println("初始字符串:" + src); 25 jdkDH(); 26 } 27 28 /** JDK实现密钥交换算法 */ 29 public static void jdkDH() throws Exception{ 30 31 //1.初始化发送方密钥 32 KeyPairGenerator senderKeyPairGenerator = KeyPairGenerator.getInstance("dh"); 33 senderKeyPairGenerator.initialize(512);//密钥长度512 34 KeyPair senderKeyPair = senderKeyPairGenerator.generateKeyPair(); 35 byte[] senderPublicKeyEnc = senderKeyPair.getPublic().getEncoded();//发送方公钥(需要把这个发送给) 36 37 //2.初始化接收方密钥 38 KeyFactory receiverkeyFactory = KeyFactory.getInstance("dh"); 39 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(senderPublicKeyEnc); 40 PublicKey receiverPublicKey = receiverkeyFactory.generatePublic(x509EncodedKeySpec); 41 DHParameterSpec dhParameterSpec = ((DHPublicKey)receiverPublicKey).getParams(); 42 KeyPairGenerator receiverKeyPairGenerator = KeyPairGenerator.getInstance("dh"); 43 receiverKeyPairGenerator.initialize(dhParameterSpec); 44 KeyPair receiverKeyPair = receiverKeyPairGenerator.generateKeyPair(); 45 PrivateKey receiverPrivateKey = receiverKeyPair.getPrivate(); 46 47 //3.密钥构建 48 KeyAgreement receiverkeyAgreement = KeyAgreement.getInstance("dh"); 49 receiverkeyAgreement.init(receiverPrivateKey); 50 receiverkeyAgreement.doPhase(receiverPublicKey, true); 51 SecretKey receiverDesKey = receiverkeyAgreement.generateSecret("des"); 52 byte[] receiverPublicKeyEnc = receiverKeyPair.getPublic().getEncoded(); 53 54 KeyFactory senderKeyFactory = KeyFactory.getInstance("dh"); 55 x509EncodedKeySpec = new X509EncodedKeySpec(receiverPublicKeyEnc); 56 PublicKey senderPublicKey = senderKeyFactory.generatePublic(x509EncodedKeySpec); 57 KeyAgreement senderKeyAgreement = KeyAgreement.getInstance("dh"); 58 senderKeyAgreement.init(senderKeyPair.getPrivate()); 59 senderKeyAgreement.doPhase(senderPublicKey, true); 60 SecretKey senderDesKey = senderKeyAgreement.generateSecret("des"); 61 if (Objects.equals(senderDesKey,receiverDesKey)) { 62 System.out.println("双方密钥相同"); 63 } 64 65 //4.加密 66 Cipher cipher = Cipher.getInstance("des"); 67 cipher.init(Cipher.ENCRYPT_MODE, senderDesKey); 68 byte[] result = cipher.doFinal(src.getBytes()); 69 System.out.println("密钥交换算法加密:" + Base64.encodeBase64String(result)); 70 71 //5.解密 72 cipher.init(cipher.DECRYPT_MODE, receiverDesKey); 73 result = cipher.doFinal(result); 74 System.out.println("密钥交换算法解密:" + new String(result)); 75 } 76 }
运行结果:
非对称加密算法——RSA
RSA是唯一被广泛接受并实现的通用算法。
在RSA算法中公钥的长度远远小于私钥的长度。以下是其java实现:
1 package rsa; 2 3 import java.security.KeyFactory; 4 import java.security.KeyPair; 5 import java.security.KeyPairGenerator; 6 import java.security.PrivateKey; 7 import java.security.PublicKey; 8 import java.security.interfaces.RSAPrivateKey; 9 import java.security.interfaces.RSAPublicKey; 10 import java.security.spec.PKCS8EncodedKeySpec; 11 import java.security.spec.X509EncodedKeySpec; 12 13 import javax.crypto.Cipher; 14 15 import org.apache.commons.codec.binary.Base64; 16 17 18 public class JavaRSA { 19 20 private static String src = "面向对象编程,object-oriented!@#*5"; // 需要加密的原始字符串 21 22 public static void main(String[] args) throws Exception { 23 24 System.out.println("初始字符串:" + src); 25 jdkRSA(); 26 27 } 28 29 public static void jdkRSA() throws Exception{ 30 //1.初始化密钥 31 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 32 keyPairGenerator.initialize(512);//密钥长度为64的整数倍,最大是65536 33 KeyPair keyPair = keyPairGenerator.generateKeyPair(); 34 RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); 35 RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); 36 System.out.println("RSA公钥:" + Base64.encodeBase64String(rsaPublicKey.getEncoded())); 37 System.out.println("RSA私钥:" + Base64.encodeBase64String(rsaPrivateKey.getEncoded())); 38 39 //2.1私钥加密,公钥解密【加密】 40 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); 41 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 42 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); 43 Cipher cipher = Cipher.getInstance("RSA"); 44 cipher.init(Cipher.ENCRYPT_MODE, privateKey); 45 byte[] result = cipher.doFinal(src.getBytes()); 46 System.out.println("JDK RSA私钥加密:" + Base64.encodeBase64String(result)); 47 48 //2.2私钥加密,公钥解密【解密】 49 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); 50 keyFactory = KeyFactory.getInstance("RSA"); 51 PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); 52 cipher.init(Cipher.DECRYPT_MODE, publicKey); 53 result = cipher.doFinal(result); 54 System.out.println("JDK RSA公钥解密:" + new String(result)); 55 56 //3.1公钥加密,私钥解密【加密】 57 x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); 58 keyFactory = KeyFactory.getInstance("RSA"); 59 publicKey = keyFactory.generatePublic(x509EncodedKeySpec); 60 cipher.init(Cipher.ENCRYPT_MODE, publicKey); 61 result = cipher.doFinal(src.getBytes()); 62 System.out.println("JDK RSA公钥加密:" + Base64.encodeBase64String(result)); 63 64 //3.2公约加密,私钥解密【解密】 65 pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); 66 keyFactory = KeyFactory.getInstance("RSA"); 67 privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); 68 cipher.init(Cipher.DECRYPT_MODE, privateKey); 69 result = cipher.doFinal(result); 70 System.out.println("JDK RSA私钥解密:" + new String(result)); 71 } 72 73 }
RSA有两种模式公钥加密私钥解密和私钥加密公钥解密两种模式,其序列图如下:
Elgamal算法
和RSA不同的是它只提供公钥加密,它依靠BouncyCastle实现。
java实现如下:
1 package elgamal; 2 3 import java.security.AlgorithmParameterGenerator; 4 import java.security.AlgorithmParameters; 5 import java.security.KeyFactory; 6 import java.security.KeyPair; 7 import java.security.KeyPairGenerator; 8 import java.security.PrivateKey; 9 import java.security.PublicKey; 10 import java.security.SecureRandom; 11 import java.security.Security; 12 import java.security.spec.PKCS8EncodedKeySpec; 13 import java.security.spec.X509EncodedKeySpec; 14 15 import javax.crypto.Cipher; 16 import javax.crypto.spec.DHParameterSpec; 17 18 import org.apache.commons.codec.binary.Base64; 19 import org.bouncycastle.jce.provider.BouncyCastleProvider; 20 /** 21 * 非对称加密算法——ELGamal算法 22 * 对于:“Illegal key size or default parameters”异常,是因为美国的出口限制,Sun通过权限文件(local_policy.jar、US_export_policy.jar)做了相应限制。 23 * Java 7 无政策限制文件:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html, 24 * 下载后得到UnlimitedJCEPolicyJDK7.zip,解压替换%JAVA_HOME%/jre/lib/security的两个文件即可 25 * @author gaopengfei 26 * 27 */ 28 public class JavaELGamal { 29 30 private static String src = "object-oriented!@#*5"; // 需要加密的原始字符串 31 32 public static void main(String[] args) throws Exception { 33 System.out.println("初始字符串:" + src); 34 bouncyCastleELGamal(); 35 } 36 37 /** 38 * Bouncy Castle实现ELGamal,这种算法和RSA算法的区别是只能公钥加密,私钥解密 39 * */ 40 private static void bouncyCastleELGamal() throws Exception{ 41 42 Security.addProvider(new BouncyCastleProvider());//加入对Bouncy Castle的支持 43 44 //1.初始化发送方密钥 45 AlgorithmParameterGenerator algorithmParameterGenerator = AlgorithmParameterGenerator.getInstance("ELGamal"); 46 algorithmParameterGenerator.init(256);//初始化参数生成器 47 AlgorithmParameters algorithmParameters = algorithmParameterGenerator.generateParameters();//生成算法参数 48 DHParameterSpec dhParameterSpec = (DHParameterSpec)algorithmParameters.getParameterSpec(DHParameterSpec.class);//构建参数材料 49 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ELGamal");//实例化密钥对生成器 50 //初始化密钥对生成器 51 keyPairGenerator.initialize(dhParameterSpec, new SecureRandom()); 52 KeyPair keyPair = keyPairGenerator.generateKeyPair(); 53 //公钥和私钥 54 PublicKey elGamalPublicKey = keyPair.getPublic(); 55 PrivateKey elGamalPrivateKey = keyPair.getPrivate(); 56 System.out.println("ELGamal公钥:" + Base64.encodeBase64String(elGamalPublicKey.getEncoded())); 57 System.out.println("ELGamal私钥:" + Base64.encodeBase64String(elGamalPrivateKey.getEncoded())); 58 59 //2.加密【公钥加密】 60 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(elGamalPublicKey.getEncoded()); 61 KeyFactory keyFactory = KeyFactory.getInstance("ELGamal"); 62 elGamalPublicKey = (PublicKey) keyFactory.generatePublic(x509EncodedKeySpec); 63 Cipher cipher = Cipher.getInstance("ELGamal","BC"); 64 cipher.init(Cipher.ENCRYPT_MODE, elGamalPublicKey); 65 byte[] result = cipher.doFinal(src.getBytes()); 66 System.out.println("ELGamal公钥加密:" + Base64.encodeBase64String(result)); 67 68 //3.解密【私钥解密】 69 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(elGamalPrivateKey.getEncoded()); 70 keyFactory = KeyFactory.getInstance("ELGamal"); 71 elGamalPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); 72 cipher.init(Cipher.DECRYPT_MODE, elGamalPrivateKey); 73 result = cipher.doFinal(result); 74 System.out.println("ELGamal私钥解密:" + new String(result)); 75 76 // //加密【私钥加密】 77 // pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(elGamalPrivateKey.getEncoded()); 78 // keyFactory = KeyFactory.getInstance("ELGamal"); 79 // elGamalPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); 80 // cipher.init(Cipher.ENCRYPT_MODE, elGamalPrivateKey); 81 // result = cipher.doFinal(src.getBytes()); 82 // System.out.println("ELGamal私钥加密:" + Base64.encodeBase64String(result)); 83 // 84 // //解密【公钥解密】 85 // x509EncodedKeySpec = new X509EncodedKeySpec(elGamalPublicKey.getEncoded()); 86 // keyFactory = KeyFactory.getInstance("ELGamal"); 87 // elGamalPublicKey = keyFactory.generatePublic(x509EncodedKeySpec); 88 // cipher.init(Cipher.DECRYPT_MODE, elGamalPublicKey); 89 // result = cipher.doFinal(result); 90 // System.out.println("ELGamal解密:" + new String(result)); 91 } 92 93 }
数字签名
数字签名是带有密钥(公钥、私钥)的消息摘要算法。主要作用是验证数据的完整性、认证数据来源、抗否认。在数字签名的实现中我们使用私钥签名、公钥验证。常用的数字签名算法包括RSA、DSA、ECDSA。
RSA
该算法是数字签名的经典算法。主要包括MD和SHA两类。
Java实现RSA数字签名如下:
1 package rsa2; 2 3 import java.security.KeyFactory; 4 import java.security.KeyPair; 5 import java.security.KeyPairGenerator; 6 import java.security.PrivateKey; 7 import java.security.PublicKey; 8 import java.security.Signature; 9 import java.security.interfaces.RSAPrivateKey; 10 import java.security.interfaces.RSAPublicKey; 11 import java.security.spec.PKCS8EncodedKeySpec; 12 import java.security.spec.X509EncodedKeySpec; 13 14 import org.apache.commons.codec.binary.Hex; 15 16 public class JavaRSA { 17 18 private static String src = "What can I do for you?"; 19 20 public static void main(String[] args) throws Exception { 21 jdkRSA(); 22 } 23 24 public static void jdkRSA() throws Exception{ 25 26 //1.初始化密钥 27 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 28 keyPairGenerator.initialize(512); 29 KeyPair keyPair = keyPairGenerator.generateKeyPair(); 30 RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); 31 RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); 32 33 //2.执行数字签名【私钥签名】 34 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); 35 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 36 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); 37 Signature signature = Signature.getInstance("MD5WithRSA"); 38 signature.initSign(privateKey); 39 signature.update(src.getBytes()); 40 byte[] result = signature.sign(); 41 System.out.println("JDK RSA签名:" + Hex.encodeHexString(result)); 42 43 //3.验证签名【公钥验证】 44 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); 45 keyFactory = KeyFactory.getInstance("RSA"); 46 PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); 47 signature = Signature.getInstance("MD5WithRSA"); 48 signature.initVerify(publicKey); 49 signature.update(src.getBytes()); 50 boolean bool = signature.verify(result); 51 System.out.println("数字签名是否有效?" + bool); 52 } 53 }
运行结果:
DSA算法
DSS(Digital Signature Standard),数字签名标准,通过这个标准逐步形成了DSA(Digital Signature Algorithm),数字签名算算法。DSA仅仅包括数字签名,不能进行加解密。实现方式如下:
该算法到的实现和RSA数字签名的实现大同小异(只是在细节参数方面存在很少的差别)。见如下:
程序运行结果:
ECDSA
微软的Office、Windows操作系统的验证就是ECDSA算法——椭圆曲线数字签名算法(Elliptic Curve Digital Signature Algorithm),在2000年的时候称为了ANSI和IEEE的标准。特点是:速度快、签名短、强度高。在JDK1.7update4之后提供了对ECDSA的支持。该签名的算法也和RSA的数字签名算法也是大同小异。
运行结果:
最后附上一系列Java加密的项目地址(包括Base64、对称加密、非对称加密、消息摘要和数字签名)。本文所需的jar文件在项目的lib目录中,jdk无政策访问限制文件在项目的ext目录下。