1.
2.
通过java代码实现如下:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* DSA安全编码组件
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public abstract class DSACoder extends Coder {
public static final String ALGORITHM = "DSA";
/**
* 默认密钥字节数
*
*
* DSA
* Default Keysize 1024
* Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
*
*/
private static final int KEY_SIZE = 1024;
/**
* 默认种子
*/
private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";
private static final String PUBLIC_KEY = "DSAPublicKey";
private static final String PRIVATE_KEY = "DSAPrivateKey";
/**
* 用私钥对信息生成数字签名
*
* @param data
* 加密数据
* @param privateKey
* 私钥
*
* @return
* @throws Exception
*/
public static String sign(byte[] data, String privateKey) throws Exception {
// 解密由base64编码的私钥
byte[] keyBytes = decryptBASE64(privateKey);
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 取私钥匙对象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 用私钥对信息生成数字签名
Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
signature.initSign(priKey);
signature.update(data);
return encryptBASE64(signature.sign());
}
/**
* 校验数字签名
*
* @param data
* 加密数据
* @param publicKey
* 公钥
* @param sign
* 数字签名
*
* @return 校验成功返回true 失败返回false
* @throws Exception
*
*/
public static boolean verify(byte[] data, String publicKey, String sign)
throws Exception {
// 解密由base64编码的公钥
byte[] keyBytes = decryptBASE64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 取公钥匙对象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
signature.initVerify(pubKey);
signature.update(data);
// 验证签名是否正常
return signature.verify(decryptBASE64(sign));
}
/**
* 生成密钥
*
* @param seed
* 种子
* @return 密钥对象
* @throws Exception
*/
public static Map initKey(String seed) throws Exception {
KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
// 初始化随机产生器
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(seed.getBytes());
keygen.initialize(KEY_SIZE, secureRandom);
KeyPair keys = keygen.genKeyPair();
DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
Map map = new HashMap(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 默认生成密钥
*
* @return 密钥对象
* @throws Exception
*/
public static Map initKey() throws Exception {
return initKey(DEFAULT_SEED);
}
/**
* 取得私钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPrivateKey(Map keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPublicKey(Map keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encryptBASE64(key.getEncoded());
}
}
再给出一个测试类:
import static org.junit.Assert.*;
import java.util.Map;
import org.junit.Test;
/**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class DSACoderTest {
@Test
public void test() throws Exception {
String inputStr = "abc";
byte[] data = inputStr.getBytes();
// 构建密钥
Map keyMap = DSACoder.initKey();
// 获得密钥
String publicKey = DSACoder.getPublicKey(keyMap);
String privateKey = DSACoder.getPrivateKey(keyMap);
System.err.println("公钥:/r" + publicKey);
System.err.println("私钥:/r" + privateKey);
// 产生签名
String sign = DSACoder.sign(data, privateKey);
System.err.println("签名:/r" + sign);
// 验证签名
boolean status = DSACoder.verify(data, publicKey, sign);
System.err.println("状态:/r" + status);
assertTrue(status);
}
}
控制台输出:
公钥:
MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZp
RV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fn
xqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuE
C/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJ
FnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImo
g9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAIu4RUlcQLp49PI0MrbssOY+3uySVnp0TULSv
5T4VaHoKzsLHgGTrwOvsGA+V3yCNl2WDu3D84bSLF7liTWgOj+SMOEaPk4VyRTlLXZWGPsf1Mfd9
21XAbMeVyKDSHHVGbMjBScajf3bXooYQMlyoHiOt/WrCo+mv7efstMM0PGo=
私钥:
MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2
USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4
O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmC
ouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCB
gLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhR
kImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFwIVAIegLUtmm2oQKQJTOiLugHTSjl/q
签名:
MC0CFQCMg0J/uZmF8GuRpr3TNq48w60nDwIUJCyYNah+HtbU6NcQfy8Ac6LeLQs=
状态:
true
(七)
ECC
ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制。在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生。
当我开始整理《Java加密技术(二)》的时候,我就已经在开始研究ECC了,但是关于Java实现ECC算法的资料实在是太少了,无论是国内还是国外的 资料,无论是官方还是非官方的解释,最终只有一种答案——ECC算法在jdk1.5后加入支持,目前仅仅只能完成密钥的生成与解析。
尽管如此,我照旧提供相应的Java实现代码,以供大家参考。
通过java代码实现如下:
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldF2m;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.EllipticCurve;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.NullCipher;
import sun.security.ec.ECKeyFactory;
import sun.security.ec.ECPrivateKeyImpl;
import sun.security.ec.ECPublicKeyImpl;
/**
* ECC安全编码组件
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public abstract class ECCCoder extends Coder {
public static final String ALGORITHM = "EC";
private static final String PUBLIC_KEY = "ECCPublicKey";
private static final String PRIVATE_KEY = "ECCPrivateKey";
/**
* 解密
* 用私钥解密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String key) throws Exception {
// 对密钥解密
byte[] keyBytes = decryptBASE64(key);
// 取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = ECKeyFactory.INSTANCE;
ECPrivateKey priKey = (ECPrivateKey) keyFactory
.generatePrivate(pkcs8KeySpec);
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(),
priKey.getParams());
// 对数据解密
// TODO Chipher不支持EC算法 未能实现
Cipher cipher = new NullCipher();
// Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
return cipher.doFinal(data);
}
/**
* 加密
* 用公钥加密
*
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String privateKey)
throws Exception {
// 对公钥解密
byte[] keyBytes = decryptBASE64(privateKey);
// 取得公钥
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = ECKeyFactory.INSTANCE;
ECPublicKey pubKey = (ECPublicKey) keyFactory
.generatePublic(x509KeySpec);
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(),
pubKey.getParams());
// 对数据加密
// TODO Chipher不支持EC算法 未能实现
Cipher cipher = new NullCipher();
// Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());
return cipher.doFinal(data);
}
/**
* 取得私钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPrivateKey(Map keyMap)
throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* 取得公钥
*
* @param keyMap
* @return
* @throws Exception
*/
public static String getPublicKey(Map keyMap)
throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return encryptBASE64(key.getEncoded());
}
/**
* 初始化密钥
*
* @return
* @throws Exception
*/
public static Map initKey() throws Exception {
BigInteger x1 = new BigInteger(
"2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16);
BigInteger x2 = new BigInteger(
"289070fb05d38ff58321f2e800536d538ccdaa3d9", 16);
ECPoint g = new ECPoint(x1, x2);
// the order of generator
BigInteger n = new BigInteger(
"5846006549323611672814741753598448348329118574063", 10);
// the cofactor
int h = 2;
int m = 163;
int[] ks = { 7, 6, 3 };
ECFieldF2m ecField = new ECFieldF2m(m, ks);
// y^2+xy=x^3+x^2+1
BigInteger a = new BigInteger("1", 2);
BigInteger b = new BigInteger("1", 2);
EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);
ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g,
n, h);
// 公钥
ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);
BigInteger s = new BigInteger(
"1234006549323611672814741753598448348329118574063", 10);
// 私钥
ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);
Map keyMap = new HashMap(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
}
请 注意上述代码中的TODO内容,再次提醒注意,Chipher不支持EC算法 ,以上代码仅供参考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持 EC算法。为了确保程序能够正常执行,我们使用了NullCipher类,验证程序。
照旧提供一个测试类:
import static org.junit.Assert.*;
import java.math.BigInteger;
import java.security.spec.ECFieldF2m;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.EllipticCurve;
import java.util.Map;
import org.junit.Test;
/**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class ECCCoderTest {
@Test
public void test() throws Exception {
String inputStr = "abc";
byte[] data = inputStr.getBytes();
Map keyMap = ECCCoder.initKey();
String publicKey = ECCCoder.getPublicKey(keyMap);
String privateKey = ECCCoder.getPrivateKey(keyMap);
System.err.println("公钥: /n" + publicKey);
System.err.println("私钥: /n" + privateKey);
byte[] encodedData = ECCCoder.encrypt(data, publicKey);
byte[] decodedData = ECCCoder.decrypt(encodedData, privateKey);
String outputStr = new String(decodedData);
System.err.println("加密前: " + inputStr + "/n/r" + "解密后: " + outputStr);
assertEquals(inputStr, outputStr);
}
}
控制台输出:
公钥:
MEAwEAYHKoZIzj0CAQYFK4EEAAEDLAAEAv4TwFN7vBGsqgfXk95ObV5clO7oAokHD7BdOP9YMh8u
gAU21TjM2qPZ
私钥:
MDICAQAwEAYHKoZIzj0CAQYFK4EEAAEEGzAZAgEBBBTYJsR3BN7TFw7JHcAHFkwNmfil7w==
加密前: abc
解密后: abc
(八)
本篇的主要内容为Java证书体系的实现。
请大家在阅读本篇内容时先阅读 Java加密技术(四),预先了解RSA加密算法。
在构建Java代码实现前,我们需要完成证书的制作。
1.生成keyStroe文件
在命令行下执行以下命令:
keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:/zlex.keystore
其中
-genkey表示生成密钥
-validity指定证书有效期,这里是36000天
-alias指定别名,这里是www.zlex.org
-keyalg指定算法,这里是RSA
-keystore指定存储位置,这里是d:/zlex.keystore
在这里我使用的密码为 123456
控制台输出:
Console代码
输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
[Unknown]: www.zlex.org
您的组织单位名称是什么?
[Unknown]: zlex
您的组织名称是什么?
[Unknown]: zlex
您所在的城市或区域名称是什么?
[Unknown]: BJ
您所在的州或省份名称是什么?
[Unknown]: BJ
该单位的两字母国家代码是什么
[Unknown]: CN
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
[否]: Y
输入的主密码
(如果和 keystore 密码相同,按回车):
再次输入新密码:
这时,在D盘下会生成一个zlex.keystore的文件。
2.生成自签名证书
光有keyStore文件是不够的,还需要证书文件,证书才是直接提供给外界使用的公钥凭证。
导出证书:
Shell代码
keytool -export -keystore d:/zlex.keystore -alias www.zlex.org -file d:/zlex.cer -rfc
其中
-export指定为导出操作
-keystore指定keystore文件
-alias指定导出keystore文件中的别名
-file指向导出路径
-rfc以文本格式输出,也就是以BASE64编码输出
这里的密码是 123456
控制台输出:
Console代码
输入keystore密码:
保存在文件中的认证
当然,使用方是需要导入证书的!
可以通过自签名证书完成CAS单点登录系统的构建!
Ok,准备工作完成,开始Java实现!
通过java代码实现如下:Coder类见 Java加密技术(一)
Java代码
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.crypto.Cipher;
/**
* 证书组件
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public abstract class CertificateCoder extends Coder {
/**
* Java密钥库(Java Key Store,JKS)KEY_STORE
*/
public static final String KEY_STORE = "JKS";
public static final String X509 = "X.509";
/**
* 由KeyStore获得私钥
*
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(String keyStorePath, String alias,
String password) throws Exception {
KeyStore ks = getKeyStore(keyStorePath, password);
PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
return key;
}
/**
* 由Certificate获得公钥
*
* @param certificatePath
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(String certificatePath)
throws Exception {
Certificate certificate = getCertificate(certificatePath);
PublicKey key = certificate.getPublicKey();
return key;
}
/**
* 获得Certificate
*
* @param certificatePath
* @return
* @throws Exception
*/
private static Certificate getCertificate(String certificatePath)
throws Exception {
CertificateFactory certificateFactory = CertificateFactory
.getInstance(X509);
FileInputStream in = new FileInputStream(certificatePath);
Certificate certificate = certificateFactory.generateCertificate(in);
in.close();
return certificate;
}
/**
* 获得Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
private static Certificate getCertificate(String keyStorePath,
String alias, String password) throws Exception {
KeyStore ks = getKeyStore(keyStorePath, password);
Certificate certificate = ks.getCertificate(alias);
return certificate;
}
/**
* 获得KeyStore
*
* @param keyStorePath
* @param password
* @return
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password)
throws Exception {
FileInputStream is = new FileInputStream(keyStorePath);
KeyStore ks = KeyStore.getInstance(KEY_STORE);
ks.load(is, password.toCharArray());
is.close();
return ks;
}
/**
* 私钥加密
*
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
// 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
// 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
*
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKey(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKey(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 验证Certificate
*
* @param certificatePath
* @return
*/
public static boolean verifyCertificate(String certificatePath) {
return verifyCertificate(new Date(), certificatePath);
}
/**
* 验证Certificate是否过期或无效
*
* @param date
* @param certificatePath
* @return
*/
public static boolean verifyCertificate(Date date, String certificatePath) {
boolean status = true;
try {
// 取得证书
Certificate certificate = getCertificate(certificatePath);
// 验证证书是否过期或无效
status = verifyCertificate(date, certificate);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* 验证证书是否过期或无效
*
* @param date
* @param certificate
* @return
*/
private static boolean verifyCertificate(Date date, Certificate certificate) {
boolean status = true;
try {
X509Certificate x509Certificate = (X509Certificate) certificate;
x509Certificate.checkValidity(date);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* 签名
*
* @param keyStorePath
* @param alias
* @param password
*
* @return
* @throws Exception
*/
public static String sign(byte[] sign, String keyStorePath, String alias,
String password) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(
keyStorePath, alias, password);
// 获取私钥
KeyStore ks = getKeyStore(keyStorePath, password);
// 取得私钥
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
.toCharArray());
// 构建签名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
signature.initSign(privateKey);
signature.update(sign);
return encryptBASE64(signature.sign());
}
/**
* 验证签名
*
* @param data
* @param sign
* @param certificatePath
* @return
* @throws Exception
*/
public static boolean verify(byte[] data, String sign,
String certificatePath) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
// 获得公钥
PublicKey publicKey = x509Certificate.getPublicKey();
// 构建签名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(decryptBASE64(sign));
}
/**
* 验证Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
*/
public static boolean verifyCertificate(Date date, String keyStorePath,
String alias, String password) {
boolean status = true;
try {
Certificate certificate = getCertificate(keyStorePath, alias,
password);
status = verifyCertificate(date, certificate);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* 验证Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
*/
public static boolean verifyCertificate(String keyStorePath, String alias,
String password) {
return verifyCertificate(new Date(), keyStorePath, alias, password);
}
}
再给出一个测试类:
import static org.junit.Assert.*;
import org.junit.Test;
/**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class CertificateCoderTest {
private String password = "123456";
private String alias = "www.zlex.org";
private String certificatePath = "d:/zlex.cer";
private String keyStorePath = "d:/zlex.keystore";
@Test
public void test() throws Exception {
System.err.println("公钥加密——私钥解密");
String inputStr = "Ceritifcate";
byte[] data = inputStr.getBytes();
byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
certificatePath);
byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
keyStorePath, alias, password);
String outputStr = new String(decrypt);
System.err.println("加密前: " + inputStr + "/n/r" + "解密后: " + outputStr);
// 验证数据一致
assertArrayEquals(data, decrypt);
// 验证证书有效
assertTrue(CertificateCoder.verifyCertificate(certificatePath));
}
@Test
public void testSign() throws Exception {
System.err.println("私钥加密——公钥解密");
String inputStr = "sign";
byte[] data = inputStr.getBytes();
byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password);
byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
certificatePath);
String outputStr = new String(decodedData);
System.err.println("加密前: " + inputStr + "/n/r" + "解密后: " + outputStr);
assertEquals(inputStr, outputStr);
System.err.println("私钥签名——公钥验证签名");
// 产生签名
String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
password);
System.err.println("签名:/r" + sign);
// 验证签名
boolean status = CertificateCoder.verify(encodedData, sign,
certificatePath);
System.err.println("状态:/r" + status);
assertTrue(status);
}
}
控制台输出:
Console代码
公钥加密——私钥解密
加密前: Ceritificate
解密后: Ceritificate
私钥加密——公钥解密
加密前: sign
解密后: sign
私钥签名——公钥验证签名
签名:
pqBn5m6PJlfOjH0A6U2o2mUmBsfgyEY1NWCbiyA/I5Gc3gaVNVIdj/zkGNZRqTjhf3+J9a9z9EI7
6F2eWYd7punHx5oh6hfNgcKbVb52EfItl4QEN+djbXiPynn07+Lbg1NOjULnpEd6ZhLP1YwrEAuM
OfvX0e7/wplxLbySaKQ=
状态:
true
由此完成了证书验证体系!
同样,我们可以对代码做签名——代码签名!
通过工具JarSigner可以完成代码签名。
这里我们对tools.jar做代码签名,命令如下:
Shell代码
jarsigner -storetype jks -keystore zlex.keystore -verbose tools.jar www.zlex.org
控制台输出:
Console代码
输入密钥库的口令短语:
正在更新: META-INF/WWW_ZLEX.SF
正在更新: META-INF/WWW_ZLEX.RSA
正在签名: org/zlex/security/Security.class
正在签名: org/zlex/tool/Main$1.class
正在签名: org/zlex/tool/Main$2.class
正在签名: org/zlex/tool/Main.class
警告:
签名者证书将在六个月内过期。
此时,我们可以对签名后的jar做验证!
验证tools.jar,命令如下:
Shell代码
jarsigner -verify -verbose -certs tools.jar
控制台输出:
Console代码
402 Sat Jun 20 16:25:14 CST 2009 META-INF/MANIFEST.MF
532 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.SF
889 Sat Jun 20 16:25:14 CST 2009 META-INF/WWW_ZLEX.RSA
sm 590 Wed Dec 10 13:03:42 CST 2008 org/zlex/security/Security.class
X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
[证书将在 09-9-18 下午3:27 到期]
sm 705 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$1.class
X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
[证书将在 09-9-18 下午3:27 到期]
sm 779 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main$2.class
X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
[证书将在 09-9-18 下午3:27 到期]
sm 12672 Tue Dec 16 18:00:56 CST 2008 org/zlex/tool/Main.class
X.509, CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
[证书将在 09-9-18 下午3:27 到期]
s = 已验证签名
m = 在清单中列出条目
k = 在密钥库中至少找到了一个证书
i = 在身份作用域内至少找到了一个证书
jar 已验证。
警告:
此 jar 包含签名者证书将在六个月内过期的条目。
代码签名认证的用途主要是对发布的软件做验证,支持 Sun Java .jar (Java Applet) 文件(J2SE)和 J2ME MIDlet Suite 文件。
通过浏览器直接访问https://www.zlex.org/examples/你也会获得上述内容。也就是说应用甲方作为服务器构建tomcat服 务,乙方可以通过上述方式访问甲方受保护的SSL应用,并且不需要考虑具体的加密解密问题。甲乙双方可以经过相应配置,通过双方的tomcat配置有效的 SSL服务,简化上述代码实现,完全通过证书配置完成SSL双向认证!
(十)
在Java 加密技术(九)中,我们使用自签名证书完成了认证。接下来,我们使用第三方CA签名机构完成证书签名。
这里我们使用thawte提供的测试用21天免费ca证书。
1.要在该网站上注明你的域名,这里使用www.zlex.org作为测试用域名(请勿使用该域名作为你的域名地址,该域名受法律保护!请使用其他非注册域名!)。
2.如果域名有效,你会收到邮件要求你访问https://www.thawte.com/cgi/server/try.exe获得ca证书。
3.复述密钥库的创建。
Shell代码
keytool -genkey -validity 36000 -alias www.zlex.org -keyalg RSA -keystore d:/zlex.keystore
在这里我使用的密码为 123456
控制台输出:
Console代码
输入keystore密码:
再次输入新密码:
您的名字与姓氏是什么?
[Unknown]: www.zlex.org
您的组织单位名称是什么?
[Unknown]: zlex
您的组织名称是什么?
[Unknown]: zlex
您所在的城市或区域名称是什么?
[Unknown]: BJ
您所在的州或省份名称是什么?
[Unknown]: BJ
该单位的两字母国家代码是什么
[Unknown]: CN
CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN 正确吗?
[否]: Y
输入的主密码
(如果和 keystore 密码相同,按回车):
再次输入新密码:
4.通过如下命令,从zlex.keystore中导出CA证书申请。
Shell代码
keytool -certreq -alias www.zlex.org -file d:/zlex.csr -keystore d:/zlex.keystore -v
你会获得zlex.csr文件,可以用记事本打开,内容如下格式:
Text代码
-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBnDCCAQUCAQAwXDELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAkJKMQswCQYDVQQHEwJCSjENMAsG
A1UEChMEemxleDENMAsGA1UECxMEemxleDEVMBMGA1UEAxMMd3d3LnpsZXgub3JnMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCR6DXU9Mp+mCKO7cv9JPsj0n1Ec/GpM09qvhpgX3FNad/ZWSDc
vU77YXZSoF9hQp3w1LC+eeKgd2MlVpXTvbVwBNVd2HiQPp37ic6BUUjSaX8LHtCl7l0BIEye9qQ2
j8G0kak7e8ZA0s7nb3Ymq/K8BV7v0MQIdhIc1bifK9ZDewIDAQABoAAwDQYJKoZIhvcNAQEFBQAD
gYEAMA1r2fbZPtNx37U9TRwadCH2TZZecwKJS/hskNm6ryPKIAp9APWwAyj8WJHRBz5SpZM4zmYO
oMCI8BcnY2A4JP+R7/SwXTdH/xcg7NVghd9A2SCgqMpF7KMfc5dE3iygdiPu+UhY200Dvpjx8gmJ
1UbH3+nqMUyCrZgURFslOUY=
-----END NEW CERTIFICATE REQUEST-----
5.将上述文件内容拷贝到https://www.thawte.com/cgi/server/try.exe中,点击next,获得回应内容,这里是p7b格式。
内容如下:Text代码
-----BEGIN PKCS7-----
MIIF3AYJKoZIhvcNAQcCoIIFzTCCBckCAQExADALBgkqhkiG9w0BBwGgggWxMIID
EDCCAnmgAwIBAgIQA/mx/pKoaB+KGX2hveFU9zANBgkqhkiG9w0BAQUFADCBhzEL
MAkGA1UEBhMCWkExIjAgBgNVBAgTGUZPUiBURVNUSU5HIFBVUlBPU0VTIE9OTFkx
HTAbBgNVBAoTFFRoYXd0ZSBDZXJ0aWZpY2F0aW9uMRcwFQYDVQQLEw5URVNUIFRF
U1QgVEVTVDEcMBoGA1UEAxMTVGhhd3RlIFRlc3QgQ0EgUm9vdDAeFw0wOTA1Mjgw
MDIxMzlaFw0wOTA2MTgwMDIxMzlaMFwxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJC
SjELMAkGA1UEBxMCQkoxDTALBgNVBAoTBHpsZXgxDTALBgNVBAsTBHpsZXgxFTAT
BgNVBAMTDHd3dy56bGV4Lm9yZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
keg11PTKfpgiju3L/ST7I9J9RHPxqTNPar4aYF9xTWnf2Vkg3L1O+2F2UqBfYUKd
8NSwvnnioHdjJVaV0721cATVXdh4kD6d+4nOgVFI0ml/Cx7Qpe5dASBMnvakNo/B
tJGpO3vGQNLO5292JqvyvAVe79DECHYSHNW4nyvWQ3sCAwEAAaOBpjCBozAMBgNV
HRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBABgNVHR8E
OTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNl
cnZlckNBLmNybDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9v
Y3NwLnRoYXd0ZS5jb20wDQYJKoZIhvcNAQEFBQADgYEATPuxZbtJJSPmXvfrr1yz
xqM06IwTZ6UU0lZRG7I0WufMjNMKdpn8hklUhE17mxAhGSpewLVVeLR7uzBLFkuC
X7wMXxhoYdJZtNai72izU6Rd1oknao7diahvRxPK4IuQ7y2oZ511/4T4vgY6iRAj
q4q76HhPJrVRL/sduaiu+gYwggKZMIICAqADAgECAgEAMA0GCSqGSIb3DQEBBAUA
MIGHMQswCQYDVQQGEwJaQTEiMCAGA1UECBMZRk9SIFRFU1RJTkcgUFVSUE9TRVMg
T05MWTEdMBsGA1UEChMUVGhhd3RlIENlcnRpZmljYXRpb24xFzAVBgNVBAsTDlRF
U1QgVEVTVCBURVNUMRwwGgYDVQQDExNUaGF3dGUgVGVzdCBDQSBSb290MB4XDTk2
MDgwMTAwMDAwMFoXDTIwMTIzMTIxNTk1OVowgYcxCzAJBgNVBAYTAlpBMSIwIAYD
VQQIExlGT1IgVEVTVElORyBQVVJQT1NFUyBPTkxZMR0wGwYDVQQKExRUaGF3dGUg
Q2VydGlmaWNhdGlvbjEXMBUGA1UECxMOVEVTVCBURVNUIFRFU1QxHDAaBgNVBAMT
E1RoYXd0ZSBUZXN0IENBIFJvb3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
ALV9kG+Os6x/DOhm+tKUQfzVMWGhE95sFmEtkMMTX2Zi4n6i6BvzoReJ5njzt1LF
cqu4EUk9Ji20egKKfmqRzmQFLP7+1niSdfJEUE7cKY40QoI99270PTrLjJeaMcCl
+AYl+kD+RL5BtuKKU3PurYcsCsre6aTvjMcqpTJOGeSPAgMBAAGjEzarmA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAgozj7BkD9O8si2V0v+EZ/t7E
fz/LC8y6mD7IBUziHy5/53ymGAGLtyhXHvX+UIE6UWbHro3IqVkrmY5uC93Z2Wew
A/6edK3KFUcUikrLeewM7gmqsiASEKx2mKRKlu12jXyNS5tXrPWRDvUKtFC1uL9a
12rFAQS2BkIk7aU+ghYxAA==
-----END PKCS7-----
将其存储为zlex.p7b
6.将由CA签发的证书导入密钥库。
Shell代码
keytool -import -trustcacerts -alias www.zlex.org -file d:/zlex.p7b -keystore d:/zlex.keystore -v
在这里我使用的密码为 123456
控制台输出:
Console代码
输入keystore密码:
回复中的最高级认证:
所有者:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
TESTING PURPOSES ONLY, C=ZA
签发人:CN=Thawte Test CA Root, OU=TEST TEST TEST, O=Thawte Certification, ST=FOR
TESTING PURPOSES ONLY, C=ZA
序列号:0
有效期: Thu Aug 01 08:00:00 CST 1996 至Fri Jan 01 05:59:59 CST 2021
证书指纹:
MD5:5E:E0:0E:1D:17:B7:CA:A5:7D:36:D6:02:DF:4D:26:A4
SHA1:39:C6:9D:27:AF:DC:EB:47:D6:33:36:6A:B2:05:F1:47:A9:B4:DA:EA
签名算法名称:MD5withRSA
版本: 3
扩展:
#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
... 是不可信的。 还是要安装回复? [否]: Y
认证回复已安装在 keystore中
[正在存储 d:/zlex.keystore]
7.域名定位
将域名www.zlex.org定位到本机上。打开C:/Windows/System32/drivers/etc/hosts文件,将 www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1 www.zlex.org.现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。
8.配置server.xml
Xml代码
keystoreFile="conf/zlex.keystore"
keystorePass="123456"
truststoreFile="conf/zlex.keystore"
truststorePass="123456"
SSLEnabled="true"
URIEncoding="UTF-8"
clientAuth="false"
maxThreads="150"
port="443"
protocol="HTTP/1.1"
scheme="https"
secure="true"
sslProtocol="TLS" />
将文件zlex.keystore拷贝到tomcat的conf目录下,重新启动tomcat.访问https://www.zlex.org/,我们发现联网有些迟钝。大约5秒钟后,网页正常显示,同时有如下图所示:
浏览器验证了该CA机构的有效性。
调整测试类:
Java代码
import static org.junit.Assert.*;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import org.junit.Test;
/**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class CertificateCoderTest {
private String password = "123456";
private String alias = "www.zlex.org";
private String certificatePath = "d:/zlex.cer";
private String keyStorePath = "d:/zlex.keystore";
@Test
public void test() throws Exception {
System.err.println("公钥加密——私钥解密");
String inputStr = "Ceritifcate";
byte[] data = inputStr.getBytes();
byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
certificatePath);
byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
keyStorePath, alias, password);
String outputStr = new String(decrypt);
System.err.println("加密前: " + inputStr + "/n/r" + "解密后: " + outputStr);
// 验证数据一致
assertArrayEquals(data, decrypt);
// 验证证书有效
assertTrue(CertificateCoder.verifyCertificate(certificatePath));
}
@Test
public void testSign() throws Exception {
System.err.println("私钥加密——公钥解密");
String inputStr = "sign";
byte[] data = inputStr.getBytes();
byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password);
byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
certificatePath);
String outputStr = new String(decodedData);
System.err.println("加密前: " + inputStr + "/n/r" + "解密后: " + outputStr);
assertEquals(inputStr, outputStr);
System.err.println("私钥签名——公钥验证签名");
// 产生签名
String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
password);
System.err.println("签名:/r" + sign);
// 验证签名
boolean status = CertificateCoder.verify(encodedData, sign,
certificatePath);
System.err.println("状态:/r" + status);
assertTrue(status);
}
@Test
public void testHttps() throws Exception {
URL url = new URL("https://www.zlex.org/examples/");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
CertificateCoder.configSSLSocketFactory(conn, password, keyStorePath,
keyStorePath);
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
DataInputStream dis = new DataInputStream(is);
byte[] data = new byte[length];
dis.readFully(data);
dis.close();
conn.disconnect();
System.err.println(new String(data));
}
}