一、前言... 1
二、如何选择加密模式... 2
2.1 加密模式的分类... 2
2.2 ECB(Electronic Code Book)/电码本模式... 2
2.3 CBC(Cipher Block Chaining). 3
2.4CFB(Cipher feedback)... 4
2.5总结... 5
三、填充模式... 5
3.1 Cipher类参数分析... 6
四、编码格式(Bse64、Hex)... 7
五、Java算法库中几个重要的类分析... 9
5.1 密钥生成器KeyPairGenerator方法分析... 9
5.2KeyFactory 简介... 9
六、附录(完整的加密签名代码)... 10
6.1 RSA加密算法... 10
6.2 签名方案... 18
一、前言
一个成功的密码算法无非要考虑这几个方面:1)加密模式2)填充模式3)编码模式。这不仅是实现加密算法和签名方案的关键,同时也是将加密、签名方案运用到异构平台的关键。本文就这3个方面来介绍java平台下的bouncycastal库的密码算法用法。
二、如何选择加密模式
2.1 加密模式的分类
加密一般称为对称加密和非对称加密,对称加密有分为分组加密和序列密码。分组密码,也叫块密码,一次加密明文中的一个块,是将一定的为长分组,明文组经过加密运算得到密文组,密文组经过解密运算还原成明文。序列密码,也叫流加密,一次加密明文中的一个位,是指利用少量的密钥(制乱元素)通过某种复杂的运算(密码算法)产生大量的伪随机位流,用于对明文位流的加密,然后运用同样的密码算法和密钥,加密相同的伪随机流进行解密,还原成明文。以下介绍四种分组加密算法的模式:
2.2 ECB(Electronic Code Book)/电码本模式
图1 明文进行加密
基于ECB模式的明文加密算法,如图1所示,密文消息进入加密系统进行加密,先将明文消息进行分组,分组的长度由密钥的长度决定。如在RSA加密算法中,一般使用1024位(128byte)的密钥,所以将明文分为128byte一组进行加密。但是对于分组中不足128byte的情况,需要规定填充方式(规则)。最后,再将分组加密后的密文进行组装。
图2 密文解密
组合后的密文,进入解密系统进行解密。解密过程也要按照加密过程中的分组规则进行解密,同时加密的顺序也同样需要遵循加密时的组装规则。最为重要的一点时,加密规则中的如果最后一组不足分组长度,那么在解密时也需要将填充的消息分离出来。因此在解密时,需要记录原明文消息的长度(或最后一组明文消息的长度)、填充方式、组合方式等,才能将明文消息正确的解密。
基于ECB模式的简单、有利于并行计算、误差不会被传送,通常使用ECB模式进行加解密。但由于ECB模式不能隐藏明文的模式(repetitions in message may show in cipher text/在密文中出现明文消息的重复);可能对明文进行主动攻击,加密消息块相互独立成为被攻击的弱点/weakness due to encrypted message blocks being independent。
2.3 CBC(Cipher Block Chaining)
图 3CBC模式加密明文过程
这个词在分组密码中经常会用到,它是指一个明文分组在被加密之前要与前一个的密文分组进行异或运算。当加密算法用于此模式的时候除密钥外,还需协商一个初始化向量(IV),这个IV没有实际意义,只是在第一次计算的时候需要用到而已。所以代码中需要初始化这个IV向量,采用这种模式安全性会有所提高。
因为CBC模式不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准,并且每个密文块依赖于所有的信息块。再者明文消息中一个改变会影响所有密文块,所需注意的是发送方和接收方都需要知道初始化向量,同时加密过程是串行的,无法被并行化(在解密时,从两个邻接的密文块中即可得到一个平文块。因此,解密过程可以被并行化,运用这种方式可避免误差传递。
图4 CBC模式解密过程
2.4CFB(Cipher feedback)
密文反馈(CFB,Cipher feedback)模式类似于CBC,可以将块密码变为自同步的流密码;工作过程亦非常相似,CFB的解密过程几乎就是颠倒的CBC的加密过程:需要使用一个与块的大小相同的移位寄存器,并用IV将寄存器初始化。然后,将寄存器内容使用块密码加密,然后将结果的最高x位与平文的x进行异或,以产生密文的x位。下一步将生成的x位密文移入寄存器中,并对下面的x位平文重复这一过程。解密过程与加密过程相似,以IV开始,对寄存器加密,将结果的高x与密文异或,产生x位平文,再将密文的下面x位移入寄存器。
与CBC相似,平文的改变会影响接下来所有的密文,因此加密过程不能并行化;而同样的,与CBC类似,解密过程是可以并行化的。
图5 CFB模式加密过程
图6 CFB模式解密过程
2.5总结
以上4种加密模式只是加密的方式不同,具体的加密器是相同的。需要注意的是在加密算法中,加密和解密需要使用同一种加密模式。
2.6加密模式的代码示例
public static String encryptDES(String encryptString, String encryptKey) throws Exception { SecretKeySpec key = new SecretKeySpec(getKey(encryptKey), "DES"); Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedData = cipher.doFinal(encryptString.getBytes()); return ConvertUtil.bytesToHexString(encryptedData); }
三、填充模式
填充模式,即数据空白补充的方式。本文在ECB加密算法中已指出填充模式的应用场景。实际上,大多数密码算法都是块密码算法,需要将明文消息切成固定大小的块,一块一块地进行加密。例如DES就需要将消息切割成一个个64位的块。如果消息长度不是64的整数倍,最后一个消息块就不够64位,这时就要对最后一个消息块进行填充。填充本质上是简易的,主要的问题在于存在多种可行的填充方式,如果加密时以某种方式填充,解密时就需要解析这种填充方式,并去除填充内容,才能得到正确的明文消息。另外,在不同的平台环境下加解密的互通,也同样需要异构平台去解析不同的填充方式,总而言之,双方只有遵循相同的填充和去填充规则,才能实现有效的解密。
接下来结合 Java 平台下密码扩展服务 SunJCE 提供的加密类函数 Cipher,对加密算法中使用到的明文填充方式进行阐述。主要介绍加密时明文数据的常用填充规则,并比较数据填充前后的区别,深入分析 SunJCE 支持的填充方式与常用填充规则的对应性,并对 RSA 算法的加解密互通进行了测试。加密数据填充方式的研究,为 Java 平台与其他平台之间加解密参数的约定提供了依据。
3.1 Cipher类参数分析
使用 Java编程实现时,SunJCE 提供的加密类 Cipher包含了多种公开的对称和非对称加密算法。为了创建Cipher对象,需要调用 Cipher类的 getInstance方法,并传递一个 transformation 参数。其中在获取 cipher 实例的时候使用的 API为:static Cipher getInstance(String transformation, String provid⁃er)其中 provider的名称是可选参数,默认为 SunJCE 提供 者 。 transformation 的 格 式 为 :“algorithm/mode/pad⁃ding”。表 1列出了部分 transformation 的参数。
对原文进行填充,主要基于以下原因:首先,考虑安全性。由于对原始数据进行了填充,使原文能够“伪装”在填充后的数据中,使得攻击者很难找到真正的原文位置。其次,由于块加密算法要求原文数据长度为分组长度的整数倍,如果加密原文不满足这个条件,则需要在加密前填充原文数据长度为分组长度的整数倍。另外,填充也为发送方与接收方提供了一种标准的形式以约束加密原文的大小。只有加解密双方知道填充方式,才可知道如何准确移去填充。
四、编码格式(Bse64、Hex)
Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。
Hex(16进制编码),在一些情况下,我们需要将公私钥由pem格式转换成十六进制来提供给第三方进行使用,如:U盾签名方案的实现、M1卡的加密等。
4.1实现原理(Bse64、Hex)
Base64实际上是对二进制码做分组转换操作,步骤如下:
Step1:每3个8位二进制码位一组,转换为4个6位二进制码为一组(不足6位时地位补0)。3个8位二进制码和4个6位二进制码长度都是24位。
Step2:对获得的4个6位二进制码补位,每个6位二进制码添加两位高位0,组成4个8位二进制码。
Step3:将获得的4个8位二进制码转换为4个十进制码。
Step4:将获得的十进制码转换为Base64字符表中对应的字符。字符串“A”,进行Base64编码,如下所示:
字符 A
ASCII码 65
二进制码 01000001
4个6位二进制码 010000 010000
4个8位二进制码 00010000 00010000
十进制码 16 16
字符表映射码 Q Q = =
字符串“A”经过Base64编码后得到字符串“QQ==”。
结果出现了两个等号。很显然,当原文的二进制码长度不足24位,最终转换为十进制时也不足4项,这时就需要用等号补位。
将Base64编码后的字符串最多会有2个等号,这时因为:
余数 = 原文字节数 MOD 3。
字符串“密”,对其使用UTF-8编码等到Byte数组{-27,-81,-122},
字符 密
UTF-8编码 -27 -81 -122
二进制码 11100101 10101111 10000110
4个6位二进制码 111001 011010 111110 000110
4个8位二进制码 00111001 00011010 00111110 00000110
十进制码 57 26 62 6
字符表映射码 5 a + G
字符串“密”经过Base64编码后得到字符串“5a+G”。
对照表:
索引 |
对应字符 |
索引 |
对应字符 |
索引 |
对应字符 |
索引 |
对应字符 |
0 |
A |
17 |
R |
34 |
i |
51 |
z |
1 |
B |
18 |
S |
35 |
j |
52 |
0 |
2 |
C |
19 |
T |
36 |
k |
53 |
1 |
3 |
D |
20 |
U |
37 |
l |
54 |
2 |
4 |
E |
21 |
V |
38 |
m |
55 |
3 |
5 |
F |
22 |
W |
39 |
n |
56 |
4 |
6 |
G |
23 |
X |
40 |
o |
57 |
5 |
7 |
H |
24 |
Y |
41 |
p |
58 |
6 |
8 |
I |
25 |
Z |
42 |
q |
59 |
7 |
9 |
J |
26 |
a |
43 |
r |
60 |
8 |
10 |
K |
27 |
b |
44 |
s |
61 |
9 |
11 |
L |
28 |
c |
45 |
t |
62 |
+ |
12 |
M |
29 |
d |
46 |
u |
63 |
/ |
13 |
N |
30 |
e |
47 |
v |
|
|
14 |
O |
31 |
f |
48 |
w |
|
|
15 |
P |
32 |
g |
49 |
x |
|
|
16 |
Q |
33 |
h |
50 |
y |
|
|
Hex:2个二进制数转化成一个16进制数,如F转为二进制为:11
五、Java算法库中几个重要的类分析
5.1 密钥生成器KeyPairGenerator方法分析
使用 Java编程实现时,java库中提供的加密类 KeyPairGenerator包含了多种公开的对称和非对称加密算法。为了创建KeyPairGenerator对象,需要调用 KeyPairGenerator类的 getInstance方法,并传递一个密码算法名称作为参数。
表1 transformation的参数
方法名称 |
加密模式 |
参数意义 |
getInstance() |
RSADSAAESDH |
密码算法签名算法的名称 |
Initialize() |
12825610242048(为8的倍数) |
密钥的长度,决定分组加密时组的长度 |
genKeyPair() |
无参 |
生成公钥、私钥 |
代码示例如下:
public static void gernatekey() { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); keyGen.initialize(1024); KeyPair keypair = keyGen.genKeyPair(); PrivateKey privateKey = keypair.getPrivate(); PublicKey publicKey = keypair.getPublic(); byte[] pubkey=publicKey.getEncoded(); }
---------------------------------------------------------------------------------------------------------------------------
RSA算法的密钥由公钥和私钥组成,公钥用于加密,私钥用于解密。顾名思义,公钥就是可以进行公开的密钥,一般可以公开给你的合作伙伴;私钥就是私有的,也就是只有你知道的,你的合作伙伴通过你提供的公钥进行加密的内容只有你能进行解密,这样也就只有你知道他发的是什么内容。用于加密的公钥和私钥是配对的。这样的一对密钥在Java中由java.security.KeyPairGenerator来产生。以下是一个生成密钥对的示例,该示例中还将生成的密钥对分别保存到了文件中。
private static final String ALGORITHM = "RSA"; private static final String PRIVATE_KEY_PATH = "D:\rsa_private.isa"; private static final String PUBLIC_KEY_PATH = "D:\rsa_public.isa";
5.2KeyFactory 简介
该类位于java.security包下,声明:public class KeyFactory extends Object密钥工厂用于将密钥(Key 类型的不透明加密密钥)转换成密钥规范(底层密钥材料的透明表示),反之3亦然。密钥工厂是双向的。也就是说,它们允许根据给定的密钥规范(密钥材料)构建不透明的密钥对象,也允许获取以恰当格式表示的密钥对象的底层密钥材料。
对于同一个密钥可以存在多个兼容的密钥规范。例如,可以使用 RSAPublicKeySpec 或 X509EncodedKeySpec 指定 RSA 公钥。密钥工厂可用于兼容密钥规范之间的转换。 以下是一个如何使用密钥工厂根据其编码实例化 RSA 公钥的示例。假定 Alice 收到了 Bob 的数字签名。Bob 也向她发送其公钥(以编码的格式)来验证他的签名。然后 Alice 执行以下操作:
//3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("MD5withRSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean bool = signature.verify(res); System.out.println("验证:"+bool);
六、附录(完整的加密签名代码)
6.1 RSA加密算法
import java.math.BigInteger; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; 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; import org.apache.commons.lang.ArrayUtils; /** * RSA加密与解密,采用Base64编码 * * @author East271536394 * @version 2012-6-25 上午9:18:57 */ public class RSAProvider { /** * KEY_ALGORITHM */ public static final String KEY_ALGORITHM = "RSA"; /** * 加密Key的长度等于1024或者2048 */ public static int KEYSIZE = 1024; /** * 解密时必须按照此分组解密 */ public static int decodeLen = KEYSIZE / 8; /** * 加密时小于117即可 */ public static int encodeLen = 100; /** * 公钥 */ private static final String PUBLIC_KEY = "publicKey"; /** * 私钥 */ private static final String PRIVATE_KEY = "privateKey"; /** * MODULES */ private static final String MODULES = "RSAModules"; /** * * 生成KeyPair */ public static Map<String, Object> generateKeyPair() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(KEYSIZE); KeyPair keyPair = keyPairGen.generateKeyPair(); // 公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); BigInteger modules = privateKey.getModulus(); Map<String, Object> keys = new HashMap<String, Object>(3); keys.put(PUBLIC_KEY, publicKey); keys.put(PRIVATE_KEY, privateKey); keys.put(MODULES, modules); return keys; } /** * * 加密private key * @param data * @param key */ public static byte[] encryptPrivateKey(String data, String key) throws Exception { byte[] bytes = data.getBytes("UTF-8"); byte[] encode = new byte[] {}; for (int i = 0; i < bytes.length; i += encodeLen) { byte[] subarray = ArrayUtils.subarray(bytes, i, i + encodeLen); byte[] doFinal = encryptByPrivateKey(subarray, key); encode = ArrayUtils.addAll(encode, doFinal); } return encode; } /** * 解密 public key * 方法说明。 * @param encode * @param key */ public static String decryptPublicKey(byte[] encode, String key) throws Exception { byte [] buffers = new byte[]{}; for (int i = 0; i < encode.length; i += decodeLen) { byte[] subarray = ArrayUtils.subarray(encode, i, i + decodeLen); byte[] doFinal = decryptByPublicKey(subarray, key); buffers = ArrayUtils.addAll(buffers, doFinal); } return new String(buffers, "UTF-8"); } /** * * 加密public key * @param data * @param key */ public static byte[] encryptPublicKey(String data, String key) throws Exception { byte[] bytes = data.getBytes("UTF-8"); byte[] encode = new byte[] {}; for (int i = 0; i < bytes.length; i += encodeLen) { byte[] subarray = ArrayUtils.subarray(bytes, i, i + encodeLen); byte[] doFinal = encryptByPublicKey(subarray, key); encode = ArrayUtils.addAll(encode, doFinal); } return encode; } /** * 解密 private key * 方法说明。 * @param encode * @param key */ public static String decryptPrivateKey(byte[] encode, String key) throws Exception { byte [] buffers = new byte[]{}; for (int i = 0; i < encode.length; i += decodeLen) { byte[] subarray = ArrayUtils.subarray(encode, i, i + decodeLen); byte[] doFinal = decryptByPrivateKey(subarray, key); buffers = ArrayUtils.addAll(buffers, doFinal); } return new String(buffers, "UTF-8"); } /** * 用私钥解密 * @param data * @param keyBytes */ private static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception { // 对密钥解密 取得私钥 byte[] keyBytes = Base64.base64ToByte(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 keyBytes */ private static byte[] decryptByPublicKey(byte[] data, String key) throws Exception { // 对密钥解密 取得公钥 byte[] keyBytes = Base64.base64ToByte(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 keyBytes */ private static byte[] encryptByPublicKey(byte[] data, String key) throws Exception { // 对公钥解密 // 取得公钥 byte[] keyBytes = Base64.base64ToByte(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 keyBytes */ private static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception { // 对密钥解密 // 取得私钥 byte[] keyBytes = Base64.base64ToByte(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); } /** * * * @return * @author East271536394 */ public static byte[] getModulesBytes(Map<String, Object> keys) { BigInteger big = (BigInteger) keys.get(MODULES); return big.toByteArray(); } /** * * 取得私钥 */ public static String getPrivateKeyBytes(Map<String, Object> keys) throws Exception { Key key = (Key) keys.get(PRIVATE_KEY); return Base64.byteToBase64(key.getEncoded()); } /** * 取得公钥 * */ public static String getPublicKeyBytes(Map<String, Object> keys) throws Exception { Key key = (Key) keys.get(PUBLIC_KEY); return Base64.byteToBase64(key.getEncoded()); } }<pre name="code" class="java"></pre> <pre></pre> <p></p> <pre name="code" class="java">import java.io.IOException; import java.io.UnsupportedEncodingException; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class Base64 { /** * 转为base64 * @param s 原始字符串 * @return String base64 */ public static String getBASE64(String s) { if (s == null) return null; try { return (new BASE64Encoder()).encode(s.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } /** * 从base64还原 * @param s base64串 * @return String 原始串 */ public static String getFromBASE64(String s) { if (s == null) return null; BASE64Decoder decoder = new BASE64Decoder(); try { byte[] b = decoder.decodeBuffer(s); return new String(b, "UTF-8"); } catch (Exception e) { return null; } } /** * 从byte转到base64 * @param bs 原始串 * @return String base64串 */ public static String byteToBase64(byte[] bs) { BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(bs); } /** * 从base64转到byte * @param base64 base64串 * @return byte 原始串 */ public static byte[] base64ToByte(String base64) { BASE64Decoder decoder = new BASE64Decoder(); try { return decoder.decodeBuffer(base64); } catch (IOException e) { e.printStackTrace(); return null; } } }
6.2 签名方案
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.spec.*; import java.security.spec.ECField; import java.security.spec.ECFieldF2m; import java.security.spec.ECFieldFp; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECPoint; import java.security.spec.EllipticCurve; import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.interfaces.DSAPrivateKey; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.math.*; import java.util.*; import java.util.HashMap; public class sigDemo{ private static String src = "spring is coming"; public static void sigByRsa(){ try { //1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); String N= byte2hex(rsaPublicKey.getModulus().toByteArray()); String e= byte2hex(rsaPublicKey.getPublicExponent().toByteArray()); String d=byte2hex(rsaPrivateKey.getPrivateExponent().toByteArray()); System.out.println("模数:"+N); System.out.println("公钥:"+e); System.out.println("私钥:"+d); //2.执行签名 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA"); signature.initSign(privateKey); signature.update(src.getBytes()); byte[] res = signature.sign(); System.out.println("签名:"+byte2hex(res)); //3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded()); keyFactory.getInstance("RSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("MD5withRSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean bool = signature.verify(res); System.out.println("验证:"+bool); } catch (Exception e) { e.printStackTrace(); } } public static void sigByDsa(){ try { //1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); keyPairGenerator.initialize(512); KeyPair keyPair = keyPairGenerator.generateKeyPair(); DSAPublicKey dsaPublicKey = (DSAPublicKey)keyPair.getPublic(); DSAPrivateKey dsaPrivateKey = (DSAPrivateKey)keyPair.getPrivate(); //2.执行签名 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(dsaPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("DSA"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("SHA1withDSA"); signature.initSign(privateKey); signature.update(src.getBytes()); byte[] res = signature.sign(); System.out.println("签名:"+byte2hex(res)); //3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(dsaPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("DSA"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("SHA1withDSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean bool = signature.verify(res); System.out.println("验证:"+bool); } catch (Exception e) { e.printStackTrace(); } } public static void sigByECdsa(){ try { //1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); keyPairGenerator.initialize(256); KeyPair keyPair = keyPairGenerator.generateKeyPair(); ECPublicKey ecPublicKey = (ECPublicKey)keyPair.getPublic(); ECPrivateKey ecPrivateKey = (ECPrivateKey)keyPair.getPrivate(); ECPoint y = ecPublicKey.getW(); String y1=byte2hex(y.getAffineX().toByteArray()); String y2=byte2hex(y.getAffineY().toByteArray()); ECPoint g = ecPublicKey.getParams().getGenerator(); String order=byte2hex(ecPublicKey.getParams().getOrder().toByteArray()); String x=byte2hex(ecPrivateKey.getS().toByteArray()); System.out.println("私钥:"+x); System.out.println("公钥:"+y1+" "+y2); System.out.println("阶:"+order); //2.执行签名 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(ecPrivateKey.getEncoded()); KeyFactory keyFactory = KeyFactory.getInstance("EC"); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); Signature signature = Signature.getInstance("SHA1withECDSA"); signature.initSign(privateKey); signature.update(src.getBytes()); byte[] res = signature.sign(); System.out.println("签名:"+byte2hex(res)); //3.验证签名 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(ecPublicKey.getEncoded()); keyFactory = KeyFactory.getInstance("EC"); PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); signature = Signature.getInstance("SHA1withECDSA"); signature.initVerify(publicKey); signature.update(src.getBytes()); boolean bool = signature.verify(res); System.out.println("验证:"+bool); } catch (Exception e) { e.printStackTrace(); } } public static boolean verifyECPoint(EllipticCurve ec, ECPoint point){ BigInteger a = ec.getA(); BigInteger b = ec.getB(); ECFieldFp field = (ECFieldFp) ec.getField(); BigInteger p = field.getP(); BigInteger x = point.getAffineX(); BigInteger y = point.getAffineY(); BigInteger right = x.pow(3).add(a.multiply(x)).add(b).mod(p); BigInteger left = y.pow(2).mod(p); if(left.compareTo(right)!=0) return false; return true; } public static String byte2hex(byte[] b) { String hs = ""; String stmp = ""; for (int n = 0; n < b.length; n++) { stmp = Integer.toHexString(b[n] & 0xFF); if (stmp.length() == 1) hs = hs + "0" + stmp; else hs = hs + stmp; } return hs.toUpperCase(); } public static void main(String args[]) { sigByRsa(); sigByDsa(); sigByECdsa(); } }