1. 对称加密与非对称加密
加密技术是最常见的安全保密手段,数据加密技术的关键在于加密/解密算法和密钥管理。数据加密的基本过程是对原来为明文的文件或数据按某种加密算法进行处理,使其成为不可读的一段代码,通常成为“密文”。“密文”只能在输入相应的密钥之后才能显示出原来的内容,通过这样的途径保证被加密的内容不被窃取。
对称加密的特点是文件或数据加密和解密使用相同的密钥,这种方法在密码学中成为对称加密算法。
非对称加密算法区别于对称加密算法,非对称加密算法需要两个密钥,公钥(Publickey)和私钥(PrivateKey)。公钥与私钥是一对,如果使用公钥对数据加密,需要对应的私钥才能解密;如果使用私钥对数据加密,需要对应的公钥才能解密。
2. 非对称加密技术——RSA算法
RSA算法是常用的非对称加密技术,名称的由来是提出人Ron Rivest、Adi Shamir、Leonard Adleman三个人的姓氏的首字母。一般来说,非对称加密技术有两种用法,一种是采用公钥加密,私钥解密;另一种就是本文所描述的私钥签名,公钥验签。
2.1 生成证书文件
首先采用RSA算法生成证书文件,这个证书文件中会包含公钥与私钥的信息。
Keytool是Java提供的一个证书管理工具,这里我们就用keytool来生成密钥证书。使用keytool生成证书文件有以下几个步骤
1.保证JDK的环境变量是可用的;
2.打开cmd窗口,并进入证书生成的目标文件夹(证书会在当前文件夹中生成);
3.执行代码段中的命令,命令的各个参数意义如下:
-alias:密钥的别名,这里我设置为mountainkey
-keyalg:使用的hash算法,这里我使用的是RSA算法
-keypass:密钥的访问密码,这里我设置为mountain
-keystore:密钥库的文件名,这里我设置为mountain.keystore,生成文件后会在mountain.keystore中保存证书内容
-storepass:密钥库的访问密码,这里我设置为mountainkeystore
keytool -genkeypair -alias mountainkey -keyalg RSA -keypass mountain -keystore mountain.keystore -storepass mountainkeystore
命令的输入过程如下图所示。
执行成功会在dos窗口所处的目录中生成证书文件,如下图所示。
2.2 获取公钥
生成的证书文件mountain.keystore中包含了公钥与私钥,现在从证书中获取公钥。这里需要用到openssl工具包,下载地址为http://slproweb.com/products/Win32OpenSSL.html
下载并安装成功后,将openssl的bin目录加入path环境变量,如下图所示。
现在,以cmd进入刚刚证书文件所在的目录,输入以下命令(证书文件为mountain.keystore),在提示输入秘钥库密码后输入storepass设置的内容,也就是秘钥库密码。
keytool -list -rfc --keystore mountain.keystore | openssl x509 -inform pem -pubkey
显示的内容如下,该内容包含了公钥的信息,也就是从BEGIN PUBLIC KEY到END PUBLIC KEY的内容,该字符串用于去校验通过私钥加密后的内容。
2.3 使用私钥签名
现在我们测试通过获取证书当中的私钥对数据进行加密,能够读取证书文件并获取私钥的方法有很多,这里我使用Spring Security所提供的方法来对进行此项操作。
/**
* 读取证书文件中的私钥,并使用私钥对数据加密,得到加密后的字符串
*/
@Test
public void testGenerateToken(){
String keystore = "mountain.keystore";
String keystorePassword = "mountainkeystore";
ClassPathResource classPathResource = new ClassPathResource(keystore);
String alias = "mountainkey";
String keyPassword = "mountain";
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,keystorePassword.toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, keyPassword.toCharArray());
RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
Map<String,String> contentMap = new HashMap<>();
contentMap.put("hello","hello world");
String bodyString = JSON.toJSONString(contentMap);
Jwt jwt = JwtHelper.encode(bodyString, new RsaSigner(aPrivate));
String encodedContent = jwt.getEncoded();
System.out.println(encodedContent);
}
控制台打印得到的私钥加密后的内容如下。
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6ImhlbGxvIHdvcmxkIn0.LNB7GpiTYl1G4MNYpFDPgay6funMLXeoGUo3TU30IfhIJiSU7zobJ-SpJap6ARbCJoRc5_JURb4ti-6vanf3cpIyOayk2H83gfBOSY8_wACbpfhlt_PGT0RYfStGC7OVwKFUAdEaO6hYYD466vI8d6d9uNb1RfNfGToYlosH05McYAyrM2XLcy0T7glZmuNw_JgMflRAjN09K13MlQZttn6W-lhSlv38CE1_CJ8SzNLHC6U7Dzd1FlcW9Xs-IvDalw1xzgzNRfbNpdvcZTnNhbdNa6_bSlSIfSM9IHngG3EVS_hwQkGOfBeV5gxQZrW4BB9niK8FwvoDB1cVjy4Ktw
2.4 使用公钥校验
将使用私钥签名后的数据进行校验,这里依然使用Spring Security提供的方法。
/**
* 使用公钥校验加密后的字符串
*/
@Test
public void testCheckTokenByPublicKey() {
// 公钥
String publickey = "-----BEGIN PUBLIC KEY-----" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbuddIbMU5FjqpJR4Ikn" +
"xktq1k/0C10XfOR2VU79qh4PXGSNn6Vt5BZgK8Ow4cA7SzAMoBUkxev/5I2Mx4p4" +
"gk+6ImQ+IsTi6tqXOQ7DHjpogfsX/VeXJ93Aeq8v9hOqtKYj5q1jy4skGRvbD+c8" +
"Z6knxLQb9I6HE39v3BZZL+WTYz6kx8BTZ0rPd7C5uOVqYo/FG+QzY+Ndv2u7gNcy" +
"V9sRnM+hI2w5e87LuG+V6GhekdKqtS0dsjKskpjX/L2ppykdi1hkCtS/ipZ5aaAj" +
"/SzVfWfQTxw4Yh+3QVc+KoSW61KlCZ+SSu7YrszAqlg93927/eWWLjYUFsCqP0jw" +
"5wIDAQAB" +
"-----END PUBLIC KEY-----";
// 私钥加密后的内容
String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6ImhlbGxvIHdvcmxkIn0.LNB7GpiTYl1G4MNYpFDPgay6funMLXeoGUo3TU30IfhIJiSU7zobJ-SpJap6ARbCJoRc5_JURb4ti-6vanf3cpIyOayk2H83gfBOSY8_wACbpfhlt_PGT0RYfStGC7OVwKFUAdEaO6hYYD466vI8d6d9uNb1RfNfGToYlosH05McYAyrM2XLcy0T7glZmuNw_JgMflRAjN09K13MlQZttn6W-lhSlv38CE1_CJ8SzNLHC6U7Dzd1FlcW9Xs-IvDalw1xzgzNRfbNpdvcZTnNhbdNa6_bSlSIfSM9IHngG3EVS_hwQkGOfBeV5gxQZrW4BB9niK8FwvoDB1cVjy4Ktw";
//校验jwt令牌
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
//拿到jwt令牌中自定义的内容
String claims = jwt.getClaims();
System.out.println(claims);
}
控制台打印的内容即是使用私钥签名前的内容。
{"hello":"hello world"}