- 初始化獲得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腳本。
後面我會把操作的代碼打包放到資源裏面,大家可以下載