RSA密钥及签名实践

这篇文章用Java代码演示

  • RSA密钥的生成与保存
  • RSA公钥加密,私钥解密
  • RSA私钥加密,公钥解密
  • 用RSA的私钥对报文进行签名
  • 用RSA的公钥对签名的报文进行验证

这些内容有助于理解密钥体系。

实践

RSA密钥的生成与保存

生成密钥对比较简单,只需要java.security.KeyPairGenerator类,初始化后,进行获取。

比如下面代码,生成了2048长的密钥。

1
2
3
4
5
6
7
KeyPairGenerator keyPairGenerate= KeyPairGenerator.getInstance("RSA");
int keysize = 2048;
keyPairGenerate.initialize(keysize);
KeyPair keypair = keyPairGenerate.generateKeyPair();
PublicKey publicKey = keypair.getPublic();
PrivateKey privateKey = keypair.getPrivate();
System.out.println("公钥格式: " + publicKey.getFormat() + ", 私钥格式: " + privateKey.getFormat());

默认的,公钥格式为X.509,私钥的格式为PKCS#8

保存到文件

1
2
org.apache.commons.io.FileUtils.writeByteArrayToFile(new File("publickey"), publicKey.getEncoded());
org.apache.commons.io.FileUtils.writeByteArrayToFile(new File("privatekey"), privateKey.getEncoded());

从文件中读取出来

1
2
3
4
5
6
7
8
9
KeyFactory kf = KeyFactory.getInstance("RSA");

byte[] byspuk = org.apache.commons.io.FileUtils.readFileToByteArray(new File("publickey"));
X509EncodedKeySpec encodedPublicKey = new X509EncodedKeySpec(byspuk);
PublicKey publickey = kf.generatePublic(encodedPublicKey);

byte[] bysprk = org.apache.commons.io.FileUtils.readFileToByteArray(new File("privatekey"));
PKCS8EncodedKeySpec encodedPrivateKey = new PKCS8EncodedKeySpec(bysprk);
PrivateKey privatekey = kf.generatePrivate(encodedPrivateKey);

RSA公钥加密,私钥解密,RSA私钥加密,公钥解密

这里用到了BC的相关类,如

  • org.bouncycastle.crypto.AsymmetricBlockCipher
  • org.bouncycastle.crypto.engines.RSAEngine
  • org.bouncycastle.crypto.params.AsymmetricKeyParameter
  • org.bouncycastle.crypto.util.PrivateKeyFactory
  • org.bouncycastle.crypto.util.PublicKeyFactory

需要加入依赖

1
2
3
4
5
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.48</version>
</dependency>

主要步骤:

  • 拿到公钥/私钥,转化为AsymmetricKeyParameter对像
  • 初始化RSAEngine对象
  • 通过processBlock()方法进行分块加密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* 获取默认私钥
*/
private static PrivateKey getPrivateKey() throws Exception
{
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey prvK = kf
.generatePrivate(new PKCS8EncodedKeySpec(FileUtils.readFileToByteArray(new File("prv.xx"))));
return prvK;
}

/**
* 私钥加密
*/
public static byte [] rsaPrivateKeyEncrypt(byte [] inputBytes) throws Exception
{
return rsaPrivateKeyCrypt(inputBytes,true);
}
/**
* 私钥解密
*/
public static byte [] rsaPrivateKeyDecrypt(byte [] inputBytes) throws Exception
{
return rsaPrivateKeyCrypt(inputBytes,false);
}
/**
* 公钥加密
*/
public static byte [] rsaPublicKeyDecrypt(byte [] inputBytes) throws Exception
{
return rsaPublicKeyCrypt(inputBytes, false);
}
/**
* 公钥解密
*/
public static byte [] rsaPublicKeyEncrypt(byte [] inputBytes) throws Exception
{
return rsaPublicKeyCrypt(inputBytes, true);
}
/**
* 公钥加/解密
*/
private static byte [] rsaPublicKeyCrypt(byte [] inputBytes ,boolean forEncryption) throws Exception
{
return rsaCrypt(inputBytes,forEncryption,true);
}
/**
* 私钥加/解密
*/
private static byte [] rsaPrivateKeyCrypt(byte [] inputBytes ,boolean forEncryption) throws Exception
{
return rsaCrypt(inputBytes,forEncryption,false);
}
/**
* 加/解密核心处理
*/
private static byte [] rsaCrypt(byte [] inputBytes,boolean forEncryption,boolean isPublicKey) throws Exception
{
final int MAX_BLOCK = 128;
AsymmetricKeyParameter param =null;
if(isPublicKey)
{
param = PublicKeyFactory.createKey(getPublicKey().getEncoded());
}
else
{
param = PrivateKeyFactory.createKey(getPrivateKey().getEncoded());
}
AsymmetricBlockCipher eng = new RSAEngine();
eng.init(forEncryption, param);
return blockLoop(inputBytes,eng,MAX_BLOCK );
}

/**
* 分块处理
*/
private static byte [] blockLoop(byte [] inputArray,AsymmetricBlockCipher engine,int maxBlockSize) throws Exception
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
int arrayLen = inputArray.length;
int offset = 0;
byte [] tempCache =null;
int looptime = 0;

while((arrayLen - offset )>0)
{
int prcoessLen = (arrayLen - offset > maxBlockSize ) ? maxBlockSize : (arrayLen - offset);
tempCache = engine.processBlock(inputArray, offset, prcoessLen);
out.write(tempCache, 0, tempCache.length);
looptime++;
offset= looptime * maxBlockSize;
}
byte[] outArray = out.toByteArray();
out.close();
return outArray;
}

测试代码

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String testText = "A quick movement of the enemy will jeopardize six gunboats.";
byte[] bys = rsaPrivateKeyDecrypt(rsaPublicKeyEncrypt(testText.getBytes()));
System.out.println(new String(bys));
byte[] byst = rsaPublicKeyDecrypt(rsaPrivateKeyEncrypt(testText.getBytes()));
System.out.println(new String(byst));

}

相关辅助函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 获取默认公钥
*/
private static PublicKey getPublicKey() throws Exception
{
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = kf
.generatePublic(new X509EncodedKeySpec(FileUtils.readFileToByteArray(new File("pub.xx"))));
return pubKey;
}

/**
* 获取默认私钥
*/
private static PrivateKey getPrivateKey() throws Exception
{
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey prvK = kf
.generatePrivate(new PKCS8EncodedKeySpec(FileUtils.readFileToByteArray(new File("prv.xx"))));
return prvK;

密码相关的处理很多都是相似的。

用RSA的私钥对报文进行签名

对报文的签名主要是通过java.security.Signature这个类来进行的。
主要步骤:

  • 获取算法实例
  • 初始化私钥
  • 更新需要签名的数据
  • 生成摘要
1
2
3
4
5
6
7
8
9
10
public static String signText(String text,String privateKeyFile) throws Exception
{
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privatekey = kf.generatePrivate(new PKCS8EncodedKeySpec(FileUtils.readFileToByteArray(new File(privateKeyFile))));
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(privatekey);
instance.update(text.getBytes("UTF-8"));
byte[] singres = instance.sign();
return org.apache.commons.codec.binary.Base64.encodeBase64String(singres);
}

用RSA的公钥对签名的报文进行验证

验签与是通过java.security.Signature这个类来的。

主要步骤:

  • 获取算法实例
  • 初始化公钥
  • 更新所需要验证的源报文
  • 传入摘要来验证是否成功
1
2
3
4
5
6
7
8
9
public static boolean varifySign(String srcText,String signatureBase64,String publicKeyFilet) throws Exception
{
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publickey = kf.generatePublic(new X509EncodedKeySpec(FileUtils.readFileToByteArray(new File(publicKeyFilet))));
Signature verifySign = Signature.getInstance("SHA1withRSA");
verifySign.initVerify(publickey);
verifySign.update(srcText.getBytes("UTF-8"));
return verifySign.verify(Base64.decodeBase64(signatureBase64));
}

更多