- 初始化获得EJBCA实例
/**
* 初始化EjbcaWS,获得EJBCA服务端Webservice实例
* @return
*/
public static EjbcaWS init()
{
EjbcaWS ejbcaWS = null;
CryptoProviderTools.installBCProvider();
System.setProperty("javax.net.ssl.keyStore","C:/Users/mango/Desktop/superadmin.p12");
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
Provider tlsProvider = new TLSProvider();
Security.addProvider(tlsProvider);
Security.setProperty("ssl.TrustManagerFactory.algorithm", "AcceptAll");
System.setProperty("javax.net.ssl.keyStorePassword", "e9a7d6bb701cfba8b0f20de5b8e92d1cf9c1f4f0");
try
{
KeyManagerFactory.getInstance("NewSunX509");
}
catch (NoSuchAlgorithmException e1)
{
e1.printStackTrace();
}
Security.setProperty("ssl.KeyManagerFactory.algorithm", "NewSunX509");
QName qname = new QName("http://ws.protocol.core.ejbca.org/","EjbcaWSService");
EjbcaWSService service = null;
try {
service = new EjbcaWSService(new URL("https://ejbcatest:8443/ejbca/ejbcaws/ejbcaws?wsdl"), qname);
ejbcaWS = service.getEjbcaWSPort();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return ejbcaWS;
}
采用域名的方式访问EJBCA服务端,则需要在客户端所在服务器hosts文件中加入服务器域名和IP。
- 注册和更新终端实体
/**
*
* @Title: editUser
* @Description: 添加用户实例或更新用户实体
* @param @param ejbcaWS
* @param @param userName
* @param @param passWord
* @param @param caName
* @param @throws ApprovalException_Exception
* @param @throws AuthorizationDeniedException_Exception
* @param @throws CADoesntExistsException_Exception
* @param @throws EjbcaException_Exception
* @param @throws UserDoesntFullfillEndEntityProfile_Exception
* @param @throws WaitingForApprovalException_Exception
* @param @throws IllegalQueryException_Exception
* @param @throws EndEntityProfileNotFoundException_Exception 参数
* @return void 返回类型
* @throws
*/
public static void editUser(EjbcaWS ejbcaWS, String userName, String passWord, String caName, String certType) throws ApprovalException_Exception, AuthorizationDeniedException_Exception,
CADoesntExistsException_Exception, EjbcaException_Exception, UserDoesntFullfillEndEntityProfile_Exception,
WaitingForApprovalException_Exception, IllegalQueryException_Exception, EndEntityProfileNotFoundException_Exception {
// Test to add a user.
final UserDataVOWS user = new UserDataVOWS();
user.setUsername(userName);
user.setPassword(passWord);
user.setClearPwd(true);
user.setSubjectDN("CN=" + userName);
user.setCaName(caName);
user.setEmail(null);
user.setSubjectAltName(null);
user.setStatus(EndEntityConstants.STATUS_NEW);
user.setTokenType(certType);
user.setEndEntityProfileName("EMPTY");
user.setCertificateProfileName("ENDUSER");
String pattern = "yyyy-MM-dd HH:mm:ssZZ"; // ISO 8601标准时间格式
user.setStartTime(DateFormatUtils.format(new Date(),pattern));//证书有效起始日期
user.setEndTime(DateFormatUtils.format(DateUtils.addDays(new Date(), 100), pattern));//结束日期
ejbcaWS.editUser(user);
}
- 查找终端实体
-
/** * 检查用户实体是否存在 * @param ejbcaWS * @param username * @return * @throws Exception */ public static boolean isExist(EjbcaWS ejbcaWS, String username) throws Exception { UserMatch usermatch = new UserMatch(); usermatch.setMatchwith(UserMatch.MATCH_WITH_USERNAME); usermatch.setMatchtype(UserMatch.MATCH_TYPE_EQUALS); usermatch.setMatchvalue(username); try { List<UserDataVOWS> users = ejbcaWS.findUser(usermatch); if (users != null && users.size() > 0) { return true; } else { return false; } } catch (Exception e) { throw new Exception("检查用户 " + username + " 是否存在时出错:" + e.getMessage()); } }
- 吊销并删除终端实体
/** * * @Title: revokeUser * @Description: 吊销证书 * @param @param ejbcaWS * @param @param userName * @return void 返回类型 * @throws */ public static void revokeUser(EjbcaWS ejbcaWS, String userName) throws Exception { // Revoke and delete ejbcaWS.revokeUser(userName, RevokedCertInfo.REVOCATION_REASON_KEYCOMPROMISE, true); UserMatch usermatch = new UserMatch(); usermatch.setMatchwith(UserMatch.MATCH_WITH_USERNAME); usermatch.setMatchtype(UserMatch.MATCH_TYPE_EQUALS); usermatch.setMatchvalue(userName); List<UserDataVOWS> userdatas = ejbcaWS.findUser(usermatch); if (userdatas != null && userdatas.size() > 0) { System.out.println("revoke failed!"); } else { System.out.println("revoke successfully!"); } }
true表示是否删除用户。
- 获取证书
public static List<Certificate> findCert(EjbcaWS ejbcaWS, String certName) throws AuthorizationDeniedException_Exception, EjbcaException_Exception
{
//第一个入参为实体名还是证书名?, End entity
List<Certificate> foundcerts = ejbcaWS.findCerts(certName, true);
return foundcerts;
}
获取某个终端实体的所有证书,true表示只查找有效的证书。
public static List<Certificate> getLastCAChainCert(EjbcaWS ejbcaWS, String caName) throws AuthorizationDeniedException_Exception, EjbcaException_Exception, CADoesntExistsException_Exception
{
//入参为CA名
List<Certificate> foundcerts = ejbcaWS.getLastCAChain(caName);
return foundcerts;
}
根据证书16进制序列号及颁发者DN获取证书。
public static Certificate getCert(EjbcaWS ejbcaWS, String certSNinHex, String issuerDN) throws AuthorizationDeniedException_Exception, EjbcaException_Exception, CADoesntExistsException_Exception
{
//第一个入参为证书序列号,第二个参数为颁发者DN
Certificate foundcerts = ejbcaWS.getCertificate(certSNinHex, issuerDN);
return foundcerts;
}
- 申请证书
根据已注册终端实体申请包含公私钥及证书的pkcs12文件
/**
*
* @Title: createCert
* @Description: 根据用户实体名称密码申请pkcs12证书
* @param @param ejbcaWS
* @param @param username
* @param @param password
* @param @param path
* @param @throws Exception 参数
* @return void 返回类型
* @throws
*/
public static void createCert(EjbcaWS ejbcaWS,String username, String password, String path) throws Exception {
FileOutputStream fileOutputStream = null;
try {
// 创建证书文件
KeyStore ksenv = ejbcaWS.pkcs12Req(username, password, null, "2048", AlgorithmConstants.KEYALGORITHM_RSA);
java.security.KeyStore ks = KeyStoreHelper.getKeyStore(ksenv.getKeystoreData(), "PKCS12", password);
fileOutputStream = new FileOutputStream(path + File.separator + username + ".p12");
ks.store(fileOutputStream, password.toCharArray());
// 创建密码文件
File pwdFile = new File(path + File.separator + username + ".pwd");
pwdFile.createNewFile();
BufferedWriter out = new BufferedWriter(new FileWriter(pwdFile));
out.write(password);
out.flush();
out.close();
} catch (Exception e) {
throw new Exception("用户 " + username + " 证书创建失败:" + e.getMessage());
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
}
}
}
}
根据CSR请求申请pem格式证书
certificateResponse = ejbcaWS.certificateRequest(userDataVOWS, pkcs10AsBase64, CertificateHelper.CERT_REQ_TYPE_PKCS10, null, CertificateHelper.RESPONSETYPE_CERTIFICATE);
userDataVOWS:用户信息,用户名必填
pkcs10AsBase64: CSR请求
同一个用户可以创建多份证书,与web界面申请证书规则不同。
- 吊销证书
/**
* @param issuerDN 颁发者DN
* @param reason 吊销证书的原因
* @return List<Certificate> 查询到的证书列表
* @throws
* @Title: revokeUser
* @Description: 吊销证书
*/
public static void revokeCert(final String issuerDN, final String certificateSN, final int reason) throws Exception {
//第一个入参为证书颁发者DN,第二个参数为证书序列号
ejbcaWS.revokeCert(issuerDN, certificateSN, reason);
}
- 获取证书吊销列表
/**
*
* @Title: getCRL
* @Description: 获取某个CA的证书吊销列表
* @param @param ejbcaWS
* @param @param caName CA名称
* @param @param delta 是否为增量
* @param @return
* @param @throws CADoesntExistsException_Exception
* @param @throws EjbcaException_Exception
* @param @throws CRLException 参数
* @return int 返回类型
* @throws
*/
public static Set<? extends X509CRLEntry> getCRL(EjbcaWS ejbcaWS, final String caName, final boolean delta) throws CADoesntExistsException_Exception, EjbcaException_Exception, CRLException {
final byte[] crlBytes = ejbcaWS.getLatestCRL(caName, delta);
if(crlBytes == null)
{
return null;
}
final X509CRL crl = CertTools.getCRLfromByteArray(crlBytes);
final Set crls = crl.getRevokedCertificates();
final BigInteger crlNumber = CrlExtensions.getCrlNumber(crl);
System.out.println(crl.getIssuerDN());
System.out.println(crl.getThisUpdate());
System.out.println(crl.getNextUpdate());
System.out.println(crlNumber.intValue());
X509CRLEntry entry = crl.getRevokedCertificate(new BigInteger("8805361580365352127"));
if (entry != null) {
String time = new SimpleDateFormat("yyyyMMddHHmmss").format(entry.getRevocationDate());
System.out.println(entry.getSerialNumber());
System.out.println(entry.getRevocationReason());
System.out.println(entry.getCertificateIssuer());
}
return crls;
}
注意CA的CRL列表不能实时更新,有滞后性,一般一天更新一次。
- 证书验证
- 验证证书链
/**
*
* @Title: verifyEffect
* @Description: 验证证书链
* @param @param ejbcaWS
* @param @param foundcerts
* @param @return 参数
* @return boolean 返回类型
* @throws
*/
public static boolean verifyEffect(EjbcaWS ejbcaWS,List<Certificate> foundcerts){
boolean effectFlag = false;
try
{
System.out.println("foundcerts.size:"+foundcerts.size());
java.security.cert.Certificate cert =
(java.security.cert.Certificate) CertificateHelper.getCertificate(foundcerts.get(0).getCertificateData());
System.out.println("client SubjectDN:"+ CertTools.getSubjectDN(cert));
System.out.println("client IssuerDN:"+ CertTools.getIssuerDN(cert));
System.out.println("client SerialNumber:"+ CertTools.getSerialNumber(cert));
for(int i = 1 ;i < foundcerts.size(); i++)
{
java.security.cert.Certificate cert2 =
(java.security.cert.Certificate)CertificateHelper.getCertificate(foundcerts.get(i).getCertificateData());
System.out.println("service SubjectDN:"+ CertTools.getSubjectDN(cert2));
System.out.println("service IssuerDN:"+ CertTools.getIssuerDN(cert2));
System.out.println("service SerialNumber:"+ CertTools.getSerialNumber(cert2));
cert.verify(cert2.getPublicKey());
effectFlag = true;
}
}
catch (Exception e) {
System.out.println(e.getMessage());
}
return effectFlag;
}
- 验证证书有效性
/**
* <p>
* 验证证书是否过期或无效
* </p>
*
* @param date 日期
* @param certificate 证书
* @return
*/
public static boolean verifyCertificate(Date date,java.security.cert.Certificate certificate) {
boolean isValid = true;
try {
X509Certificate x509Certificate = (X509Certificate)certificate;
x509Certificate.checkValidity(date);
System.out.println("cert valid");
} catch (Exception e) {
System.out.println(e.getMessage());
isValid = false;
}
return isValid;
}
- 验证签名
/**
* <p>
* 验证签名
* </p>
*
* @param data 已加密数据
* @param sign 数据签名[BASE64]
* @param certificatePath 证书存储路径
* @return
* @throws Exception
*/
public static boolean verifySign(byte[] data, String sign, java.security.cert.Certificate certificate)
throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) certificate;
// 获得公钥
PublicKey publicKey = x509Certificate.getPublicKey();
// 构建签名
Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
signature.initVerify(publicKey);
signature.update(data);
// return signature.verify((sign));
return true;
}
- 遗留问题
1.EJBCA提供了JAVA SDK去调用其Webservice接口,初始化时获取了EJBCA的EjbcaWSService实例,并需要设置证书。如果使用手机客户端直接访问EJBCA的话APP是否能获得该实例,如何配置证书等。EJBCA是否提供了手机APP集成时需要使用的SDK?
2.获取Ejbca实例客户端需要配置超级管理员证书,该证书拥有删除用户,修改用户,注册CA等一系列敏感权限,如果手机直接集成的话会存在危险。目前看可以在管理员界面对该证书的权限进行配置。
3.部署环境时需要修改ejbca-setup.sh脚本中的httpsserver_hostname为部署所在服务器的主机名称,这样在生成EJBCA HTTPS服务端证书时证书Domain Name与服务器域名相同,客户端才可以通过域名正常访问。如果通过IP访问则还需要修改ejbca-setup.sh脚本。
后面我会把操作的代码打包放到资源里面,大家可以下载