Java加解密技术

说在前面

Java加解密技术是在Java中对密码学的一种实现,但密码学本身是有很多方面的内容。

什么是加解密技术

以下是维基上密码学的定义:

Cryptography or cryptology is the practice and study of techniques for secure communication in the presence of third parties called adversaries. More generally, cryptography is about constructing and analyzing protocols that prevent third parties or the public from reading private messages; various aspects in information security such as data confidentiality, data integrity, authentication, and non-repudiation are central to modern cryptography. Modern cryptography exists at the intersection of the disciplines of mathematics, computer science, electrical engineering, communication science, and physics. Applications of cryptography include electronic commerce, chip-based payment cards, digital currencies, computer passwords, and military communications.

密码学是针对信息不被第三方获得的安全传输实践和研究。通常,密码学是关于构造和分析协议,以防止第三方或公众读取私有消息;在信息安全的各个方面,如数据机密性、数据完整性、身份验证和不可否认性,是现代密码学的核心。现代密码学存在于数学、计算机科学、电子工程、通信科学。。。

以上讲了三点:

信息
传输
安全

简单一句话:密码学就是安全地把信息传给对方(不相关的第三方无法获取你传出去的真实内容)

密码学有一套丰富的参考模型,里面包含了八大安全机制:

  1. 加密机制
  2. 数字签名机制
  3. 访问控制机制
  4. 数据完整性机制
  5. 认证机制
  6. 业务流填充机身
  7. 路由控制机制
  8. 公证机制

这里面只讨论加密钥相关的内容(你加密完,我收到后解密成原来的内容),针对密码学中的其他内容,后续文章再做讨论。

加解密的分类

按密码体制

这是一般常说的一种分法,主要是对称密码体制非对称密码体制

  1. 对称:加密和解密所用的密钥相同
  2. 非对称:加密和密钥所用的不同,密钥分别叫做公钥、私钥。公钥向可以公开,私钥是保密的。

按明文的处理方法

主要分为分组密码流密码

  1. 分组密码:加密的时候把明文分成固定长度的几块,用同一个密钥和算法,将不同的分组块用这同一个密钥和算法进行加密处理。
  2. 流密码:加密的时候按位或字节加密明文。

Java对密码学的支持

主要三方面: 1. java API,2. JavaEE容器, 3. Java工具

Java API

Java中有很多接口和实现类上的支持,如

  1. MessageDigest类,做信息的一个摘要,一般有MD5和SHA
  2. Mac和Cipher两个类, Mac类,构建HMAC加密算法,Cipher可以构建DES、AES、RSA等加密算法
  3. Signature类,用于做数字签名和验证
  4. Certificate类,主要操作证书。

JavaEE容器

比如Tomcat,只需要简单做一些基本的配置,就可以实现HTTPS的应用。

Java工具

keytool命令(跟javac等一起在JDK中)可以实现密钥、证书的管理,如生成、导出等。

Java中几种常见加解密的实现

一些说明

理论上,密钥的长度是范围比较广的,但由于美国出口的限制(JCE),一些长度JDK中默认不支持的,如果需要,可以通过替换JRE中JCE(local_policy.jar, US_export_policy.jar 两个文件, 可在Oracle官网下载) 来支持其他长度。
当然,本地JDK/JRE中的java.security文件中最好也关注一下。

算法 理论长度 实际支持长度 说明
DES 值域:{56, 128} 56 JCE限制
AES 值域:{128, 192, 256} 128 JCE限制
RSA 区间: [512, 16384]

密钥的生成

主要包括对称加,非对称和椭圆签名算法。
以下用到的两个第三方类为:
org.apache.commons.codec.binary.Base64
org.bouncycastle.jce.provider.BouncyCastleProvider

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

/**
* 生成对称加密密钥
* algorithm支持 DES, AES两个
*/
public static String symmetricKeyGenerator(String algorithm, int keySize)
throws Exception {

Provider provider = new BouncyCastleProvider();
KeyGenerator generator = KeyGenerator.getInstance(algorithm);
generator.init(keySize);
SecretKey secretKey = generator.generateKey();
return Base64.encodeBase64String(secretKey.getEncoded());
}
/**
* 生成非对称加密密钥
* algorithm支持 RSA
*/
public static Map<String,String> asymmetricKeyPairGenerator(String algorithm, int keySize)
throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
keyPairGenerator.initialize(keySize);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Map<String, String> keys = new HashMap();
keys.put("privateKey", Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
keys.put("publicKey", Base64.encodeBase64String(keyPair.getPublic().getEncoded());
return keys;
}



/**
* 生成椭圆验签密钥
* algorithm支持 ECDSA
*/
public static Map<String,String> ecdsaKeyPairGenerator(String algorithm, int keySize)
throws Exception {
Provider provider = new BouncyCastleProvider();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm, provider);
keyPairGenerator.initialize(keySize);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Map<String, String> keys = new HashMap();
keys.put("privateKey", Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
keys.put("publicKey", Base64.encodeBase64String(keyPair.getPublic().getEncoded());
return keys;
}
}

加密与解密

DES加密与解密

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
public String encrypt(String data, String key)
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException{

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(key);
Key k = new SecretKeySpec(keyBytes, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, k);
return new BASE64Encoder().encode(cipher.doFinal(data.getBytes()));
}

public String decrypt(String data, String key)
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException{

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(key);
Key k = new SecretKeySpec(keyBytes, "DES");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, k);
return new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(data)));
}

AES加密与解密

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
public String encrypt(String data, String key)
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException{

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(key);
Key k = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, k);
return new BASE64Encoder().encode(cipher.doFinal(data.getBytes()));
}

public String decrypt(String data, String key)
throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException{

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(key);
Key k = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, k);
return new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(data)));
}

RSA加密与解密

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
public String encrypt(String body, String keyString) 
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeySpecException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException{

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(keyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA");

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
Key key = keyFactory.generatePrivate(keySpec);
cipher.init(Cipher.ENCRYPT_MODE, key);
return new BASE64Encoder().encode(cipher.doFinal(body.getBytes()));
}

public String decrypt(String body, String keyString)
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeySpecException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException {

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(keyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA");

Key key;

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
key = keyFactory.generatePublic(keySpec);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(body)));
}

加签与验签

RSA加签与验签

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
public String sign(String body, String keyString) 
throws NoSuchAlgorithmException,
InvalidKeySpecException,
InvalidKeyException,
SignatureException,
IOException {

byte[] keyBytes;
keyBytes = new BASE64Decoder().decodeBuffer(keyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

Signature signature = Signature.getInstance("MD5withRSA");

signature.initSign(privateKey);
signature.update(body.getBytes());
return Base64.encodeBase64String(signature.sign());
}

public boolean verify(String key, String data, String sign)
throws NoSuchAlgorithmException,
InvalidKeySpecException,
InvalidKeyException,
SignatureException {

X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(key));

Provider provider = new BouncyCastleProvider();

KeyFactory keyFactory = KeyFactory.getInstance("RSA", provider);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
Signature signature = Signature.getInstance("MD5withRSA", provider);

signature.initVerify(publicKey);
signature.update(data.getBytes());
return signature.verify(Base64.decodeBase64(sign));
}

ECDSA加签与验签

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
public String sign(String key, String data) 
throws NoSuchAlgorithmException,
InvalidKeySpecException,
InvalidKeyException,
SignatureException {

PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(key));

Provider provider = new BouncyCastleProvider();

KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", provider);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance("ECDSA", provider);

signature.initSign(privateKey);
signature.update(data.getBytes());
return Base64.encodeBase64String(signature.sign());
}

public boolean verify(String key, String data, String sign)
throws NoSuchAlgorithmException,
InvalidKeySpecException,
InvalidKeyException,
SignatureException {

X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(key));

Provider provider = new BouncyCastleProvider();

KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", provider);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
Signature signature = Signature.getInstance("ECDSA", provider);

signature.initVerify(publicKey);
signature.update(data.getBytes());
return signature.verify(Base64.decodeBase64(sign));
}

以上代码都在我的github里面 wangtingbang/cryptography