网上资料关于JAVA操作硬件USBKEY的例子比较少,本篇使用主要实现2个功能:
1 java实现向USBKEY导入证书
2 java调用USBKEY证书签名
对USBKEY陌生的同学请参考:https://blog.csdn.net/liujoi/article/details/106150546
本篇需要使用:bcpkix-jdk15on-160.jar、bcprov-ext-jdk15on-160.jar,完整源码见附件。
package org.liuy.pkcs11;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import sun.misc.BASE64Encoder;
import sun.security.pkcs11.SunPKCS11;
import sun.security.pkcs11.wrapper.CK_ATTRIBUTE;
import sun.security.pkcs11.wrapper.CK_INFO;
import sun.security.pkcs11.wrapper.CK_SESSION_INFO;
import sun.security.pkcs11.wrapper.CK_SLOT_INFO;
import sun.security.pkcs11.wrapper.CK_TOKEN_INFO;
import sun.security.pkcs11.wrapper.PKCS11;
import sun.security.pkcs11.wrapper.PKCS11Constants;
import sun.security.pkcs11.wrapper.PKCS11Exception;
/**
* 使用PKCS11 对硬件USBKEY:下发证书、签名、打印硬件基本信息等功能
*
* @author liuy
*
*/
public class Pkcs11Util {
protected PKCS11 myPKCS11Module_;
protected long token_ = -1L;
/**
* 初始化库文件,必须首先加载
* @param libPath pkcs11库的路径:windows和linux都可以
* @throws IOException
* @throws PKCS11Exception
*/
public Pkcs11Util(String libPath) throws IOException, PKCS11Exception
{
myPKCS11Module_ = PKCS11.getInstance(libPath, "C_GetFunctionList", null, false);
}
/**
* 获取Cryptoki的通用信息
* @throws PKCS11Exception
*/
public CK_INFO getInfo() throws PKCS11Exception
{
CK_INFO moduleInfo = myPKCS11Module_.C_GetInfo();
return moduleInfo;
}
/**
* 获取 槽信息
* @return
* @throws PKCS11Exception
*/
public List getSlotInfo() throws PKCS11Exception
{
List list= new ArrayList();
long[] slotIDs = myPKCS11Module_.C_GetSlotList(false);
for (int i=0; i < slotIDs.length; i++) {
CK_SLOT_INFO slotInfo = myPKCS11Module_.C_GetSlotInfo(slotIDs[i]);
list.add(slotInfo);
}
return list;
}
/**
* 获取硬件信息
* @return
* @throws PKCS11Exception
*/
public List getTokenInfo() throws PKCS11Exception
{
List list= new ArrayList();
long[] tokenIDs = myPKCS11Module_.C_GetSlotList(true);
for (int i=0; i < tokenIDs.length; i++) {
CK_TOKEN_INFO tokenInfo = myPKCS11Module_.C_GetTokenInfo(tokenIDs[i]);
list.add(tokenInfo);
if (token_ == -1L) {
token_ = tokenIDs[i];
}
}
return list;
}
/**
* PKCS11 写入证书
* @param cert
* @param label
* @param id
* @return
* @throws PKCS11Exception
*
* @throws CertificateEncodingException
*/
public void uploadCertificate(X509Certificate cert, String label, String id) throws PKCS11Exception, CertificateEncodingException {
long session_ = myPKCS11Module_.C_OpenSession(token_,PKCS11Constants.CKF_RW_SESSION | PKCS11Constants.CKF_SERIAL_SESSION, null, null);
CK_SESSION_INFO sessionInfo = myPKCS11Module_.C_GetSessionInfo(session_);
System.out.println(sessionInfo);
CK_ATTRIBUTE[] certificate = new CK_ATTRIBUTE[9];
// certificate[0] = new CK_ATTRIBUTE(PKCS11Constants.CKA_CLASS, PKCS11Constants.CKO_CERTIFICATE);
// certificate[1] = new CK_ATTRIBUTE(PKCS11Constants.CKA_TOKEN, true);
// certificate[2] = new CK_ATTRIBUTE(PKCS11Constants.CKA_PRIVATE, false);
// certificate[3] = new CK_ATTRIBUTE(PKCS11Constants.CKA_LABEL, label.toCharArray());
// certificate[4] = new CK_ATTRIBUTE(PKCS11Constants.CKA_SUBJECT, cert.getSubjectX500Principal().getEncoded());
// certificate[5] = new CK_ATTRIBUTE(PKCS11Constants.CKA_ID, StringUtil.hexStringToByte(id));
// certificate[6] = new CK_ATTRIBUTE(PKCS11Constants.CKA_ISSUER, cert.getIssuerX500Principal().getEncoded());
// certificate[7] = new CK_ATTRIBUTE(PKCS11Constants.CKA_SERIAL_NUMBER, cert.getSerialNumber().toByteArray());
// certificate[8] = new CK_ATTRIBUTE(PKCS11Constants.CKA_VALUE, cert.getEncoded());
// myPKCS11Module_.C_CreateObject(session_, certificate);
// myPKCS11Module_.C_CloseSession(token_);
}
/**
* 导入证书
* @param cfgPath 配置文件路径
* @param pin USBKEY密码
* @throws InvalidKeyException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws IllegalStateException
* @throws IOException
*/
public void importCert(String cfgPath,String pin) throws InvalidKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, IllegalStateException, IOException
{
Provider p = new SunPKCS11(cfgPath);
importCert(p,pin);
}
/***
*
* 导入证书,可以动态导入证书
* @param cfgPath 配置文件路径
* @param pin USBKEY密码
* @throws InvalidKeyException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws IllegalStateException
* @throws IOException
*/
public void importCert(ByteArrayInputStream confStream,String pin) throws InvalidKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, IllegalStateException, IOException
{
Provider p = new SunPKCS11(confStream);
importCert(p,pin);
}
/**
* 使用sunPKCS11实现导入
* @param cfgPath 配置文件路径
* @param pin USBKEY密码
* @throws KeyStoreException
* @throws IOException
* @throws CertificateException
* @throws NoSuchAlgorithmException
* @throws IllegalStateException
* @throws SignatureException
* @throws NoSuchProviderException
* @throws InvalidKeyException
*/
private void importCert(Provider p,String pin) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InvalidKeyException, NoSuchProviderException, SignatureException, IllegalStateException
{
if (-1 == Security.addProvider(p)) {
throw new RuntimeException("could not add security provider");
}
//pin U盾的密码
char[] upin = pin.toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS11", p);
ks.load(null, upin);
//产品密钥,一般使用PKCS10创建
SecureRandom sr = new SecureRandom();
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", p);
keyGen.initialize(2048, sr);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey pk = keyPair.getPrivate();
X509Certificate[] chain = generateV3Certificate(keyPair);
//alias 条目可以根据项目创建,这里防止不重复简单的获取时间
String alias=String.valueOf(System.currentTimeMillis());
ks.setKeyEntry(alias, pk,null, chain);
ks.store(null);
System.out.println("OK");
}
/**
* USBKEY签名
* @param cfgPath 配置文件路径
* @param data 签名数据
* @param pin UBKEY密码
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws IOException
* @throws UnrecoverableKeyException
* @throws InvalidKeyException
* @throws SignatureException
*/
public void sign(String cfgPath,String data,String pin) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException, InvalidKeyException, SignatureException
{
//CSP
// BouncyCastleProvider providerBC = new BouncyCastleProvider();
// Security.addProvider(providerBC);
// SunMSCAPI providerMSCAPI = new SunMSCAPI();
// Security.addProvider(providerMSCAPI);
// KeyStore ks = KeyStore.getInstance("Windows-MY","SunMSCAPI");
// ks.load(null, null);
//P11
SunPKCS11 providerMSCAPI = new SunPKCS11(cfgPath);
Provider a = providerMSCAPI;
Security.addProvider(a);
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, pin.toCharArray());
Enumeration aliases = ks.aliases();
while (aliases.hasMoreElements())
{
String alias = aliases.nextElement();
System.out.println("alias:" + alias+":");
X509Certificate x509 = (X509Certificate) ks.getCertificate(alias);
PrivateKey pk=(PrivateKey)ks.getKey(alias, null);
byte[] pkBy=pk.getEncoded();
// 构建签名
Signature signature = Signature.getInstance(x509.getSigAlgName());
signature.initSign(pk);
signature.update(data.getBytes());
byte[] sign=signature.sign();
System.out.println(Base64.toBase64String(sign));
}
}
/**
* 生产证书
* @param pair
* @return
* @throws InvalidKeyException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws CertificateEncodingException
* @throws IllegalStateException
* @throws NoSuchAlgorithmException
*/
public X509Certificate[] generateV3Certificate(KeyPair pair) throws InvalidKeyException, NoSuchProviderException, SignatureException, CertificateEncodingException, IllegalStateException, NoSuchAlgorithmException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setIssuerDN(new X500Principal("CN=Test Certificate"));
certGen.setNotBefore(new Date(System.currentTimeMillis() - 10000));
certGen.setNotAfter(new Date(System.currentTimeMillis() + 10000));
certGen.setSubjectDN(new X500Principal("CN=Test Certificate"));
certGen.setPublicKey(pair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSA");
certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
certGen.addExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth));
certGen.addExtension(X509Extensions.SubjectAlternativeName, false, new GeneralNames(new GeneralName(GeneralName.rfc822Name, "[email protected]")));
X509Certificate[] chain = new X509Certificate[1];
chain[0] = certGen.generate(pair.getPrivate());
return chain;
}
public static void main(String[] args) throws Exception
{
String realPath = Pkcs11Util.class.getClassLoader().getResource("").getFile();
java.io.File file = new java.io.File(realPath);
realPath = file.getCanonicalPath();
String libPath=realPath+"/ShuttleCsp11_3003_15.dll";
Pkcs11Util p11=new Pkcs11Util(libPath);
CK_INFO ck_info=p11.getInfo();
System.out.println("接口版本号:"+ck_info.cryptokiVersion);
System.out.println("接口提供厂家:"+String.valueOf(ck_info.manufacturerID));
System.out.println();
List slotList=p11.getSlotInfo();
for(int i=0; i < slotList.size(); i++)
{
CK_SLOT_INFO slot_info=slotList.get(i);
System.out.println("设备描述:"+String.valueOf(slot_info.slotDescription));
System.out.println("设备厂家:"+String.valueOf(slot_info.manufacturerID));
System.out.println("硬件标识:"+slot_info.flags);
System.out.println("硬件编号:"+slot_info.hardwareVersion);
System.out.println("固件版本:"+slot_info.hardwareVersion);
System.out.println();
}
List tokenList=p11.getTokenInfo();
for(int i=0; i < tokenList.size(); i++)
{
CK_TOKEN_INFO token_info=tokenList.get(i);
System.out.println("应用标记:"+String.valueOf(token_info.label));
System.out.println("设备厂家:"+String.valueOf(token_info.manufacturerID));
System.out.println("设备类型:"+String.valueOf(token_info.model));
System.out.println("设备序列号:"+String.valueOf(token_info.serialNumber));
System.out.println("状态标识:"+token_info.flags);
System.out.println("最大会话数:"+token_info.ulMaxSessionCount);
System.out.println("当前会话数:"+token_info.ulSessionCount);
System.out.println("PIN最大长度:"+token_info.ulMaxPinLen);
System.out.println("PIN最小长度:"+token_info.ulMinPinLen);
System.out.println("PIN最小长度:"+token_info.ulMinPinLen);
System.out.println("公有区域总空间:"+token_info.ulTotalPublicMemory);
System.out.println("公有区域剩余空间:"+token_info.ulFreePublicMemory);
System.out.println("私有区域总空间:"+token_info.ulTotalPrivateMemory);
System.out.println("私有区域剩余空间:"+token_info.ulFreePrivateMemory);
System.out.println("硬件版本:"+token_info.hardwareVersion);
System.out.println();
}
//测试KEY用的是:飞天诚信epass3003的标准产品
//淘宝地址:https://detail.tmall.com/item.htm?id=43722580737
StringBuffer setP11Conf=new StringBuffer();
setP11Conf.append("name=epass3003 \n");
//如果是linux,对应加载so文件
setP11Conf.append("library="+libPath+" \n");
//电脑上插了多个的时候需要重新设置
setP11Conf.append("slot=1 \n");
setP11Conf.append("attributes(generate, *, *) = { CKA_TOKEN = true } \n");
setP11Conf.append("attributes(generate, CKO_CERTIFICATE, *) = { CKA_PRIVATE = false } \n");
setP11Conf.append("attributes(generate, CKO_PUBLIC_KEY, *) = { CKA_PRIVATE = false } \n");
byte[] pkcs11ConfigBytes = setP11Conf.toString().getBytes();
ByteArrayInputStream confStream = new ByteArrayInputStream(pkcs11ConfigBytes);
// p11.importCert(confStream, "123456");
String cfgPath=realPath+"/epass3003.cfg";
p11.sign(cfgPath, "123", "123456");
}
}
实际项目中,u盾生产PKCS10请求给CA公司,再由CA公司颁发证书导入;应用的时候可以先记录alias生产P10之后,获取证书在根据alias导入