You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

X509Utils.java 42KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. /*
  2. * Copyright 2012 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.utils;
  17. import java.io.File;
  18. import java.io.FileInputStream;
  19. import java.io.FileOutputStream;
  20. import java.io.FileWriter;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.lang.reflect.Field;
  24. import java.math.BigInteger;
  25. import java.security.InvalidKeyException;
  26. import java.security.KeyPair;
  27. import java.security.KeyPairGenerator;
  28. import java.security.KeyStore;
  29. import java.security.NoSuchAlgorithmException;
  30. import java.security.PrivateKey;
  31. import java.security.SecureRandom;
  32. import java.security.Security;
  33. import java.security.SignatureException;
  34. import java.security.cert.CertPathBuilder;
  35. import java.security.cert.CertPathBuilderException;
  36. import java.security.cert.CertStore;
  37. import java.security.cert.Certificate;
  38. import java.security.cert.CertificateEncodingException;
  39. import java.security.cert.CertificateFactory;
  40. import java.security.cert.CollectionCertStoreParameters;
  41. import java.security.cert.PKIXBuilderParameters;
  42. import java.security.cert.PKIXCertPathBuilderResult;
  43. import java.security.cert.TrustAnchor;
  44. import java.security.cert.X509CRL;
  45. import java.security.cert.X509CertSelector;
  46. import java.security.cert.X509Certificate;
  47. import java.text.MessageFormat;
  48. import java.text.SimpleDateFormat;
  49. import java.util.ArrayList;
  50. import java.util.Arrays;
  51. import java.util.Calendar;
  52. import java.util.Date;
  53. import java.util.HashMap;
  54. import java.util.HashSet;
  55. import java.util.List;
  56. import java.util.Map;
  57. import java.util.Set;
  58. import java.util.TimeZone;
  59. import java.util.zip.ZipEntry;
  60. import java.util.zip.ZipOutputStream;
  61. import javax.crypto.Cipher;
  62. import javax.naming.ldap.LdapName;
  63. import org.bouncycastle.asn1.ASN1ObjectIdentifier;
  64. import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
  65. import org.bouncycastle.asn1.x500.X500Name;
  66. import org.bouncycastle.asn1.x500.X500NameBuilder;
  67. import org.bouncycastle.asn1.x500.style.BCStyle;
  68. import org.bouncycastle.asn1.x509.BasicConstraints;
  69. import org.bouncycastle.asn1.x509.GeneralName;
  70. import org.bouncycastle.asn1.x509.GeneralNames;
  71. import org.bouncycastle.asn1.x509.KeyUsage;
  72. import org.bouncycastle.asn1.x509.Extension;
  73. import org.bouncycastle.cert.X509CRLHolder;
  74. import org.bouncycastle.cert.X509v2CRLBuilder;
  75. import org.bouncycastle.cert.X509v3CertificateBuilder;
  76. import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
  77. import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
  78. import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
  79. import org.bouncycastle.jce.PrincipalUtil;
  80. import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
  81. import org.bouncycastle.openssl.PEMEncryptor;
  82. import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
  83. import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
  84. import org.bouncycastle.operator.ContentSigner;
  85. import org.bouncycastle.operator.OperatorCreationException;
  86. import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
  87. import org.slf4j.Logger;
  88. import org.slf4j.LoggerFactory;
  89. import com.gitblit.Constants;
  90. /**
  91. * Utility class to generate X509 certificates, keystores, and truststores.
  92. *
  93. * @author James Moger
  94. *
  95. */
  96. public class X509Utils {
  97. public static final String SERVER_KEY_STORE = "serverKeyStore.jks";
  98. public static final String SERVER_TRUST_STORE = "serverTrustStore.jks";
  99. public static final String CERTS = "certs";
  100. public static final String CA_KEY_STORE = "certs/caKeyStore.p12";
  101. public static final String CA_REVOCATION_LIST = "certs/caRevocationList.crl";
  102. public static final String CA_CONFIG = "certs/authority.conf";
  103. public static final String CA_CN = "Gitblit Certificate Authority";
  104. public static final String CA_ALIAS = CA_CN;
  105. private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
  106. private static final int KEY_LENGTH = 2048;
  107. private static final String KEY_ALGORITHM = "RSA";
  108. private static final String SIGNING_ALGORITHM = "SHA512withRSA";
  109. public static final boolean unlimitedStrength;
  110. private static final Logger logger = LoggerFactory.getLogger(X509Utils.class);
  111. static {
  112. Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
  113. // check for JCE Unlimited Strength
  114. int maxKeyLen = 0;
  115. try {
  116. maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
  117. } catch (NoSuchAlgorithmException e) {
  118. }
  119. unlimitedStrength = maxKeyLen > 128;
  120. if (unlimitedStrength) {
  121. logger.info("Using JCE Unlimited Strength Jurisdiction Policy files");
  122. } else {
  123. logger.info("Using JCE Standard Encryption Policy files, encryption key lengths will be limited");
  124. }
  125. }
  126. public static enum RevocationReason {
  127. // https://en.wikipedia.org/wiki/Revocation_list
  128. unspecified, keyCompromise, caCompromise, affiliationChanged, superseded,
  129. cessationOfOperation, certificateHold, unused, removeFromCRL, privilegeWithdrawn,
  130. ACompromise;
  131. public static RevocationReason [] reasons = {
  132. unspecified, keyCompromise, caCompromise,
  133. affiliationChanged, superseded, cessationOfOperation,
  134. privilegeWithdrawn };
  135. @Override
  136. public String toString() {
  137. return name() + " (" + ordinal() + ")";
  138. }
  139. }
  140. public interface X509Log {
  141. void log(String message);
  142. }
  143. public static class X509Metadata {
  144. // map for distinguished name OIDs
  145. public final Map<String, String> oids;
  146. // CN in distingiushed name
  147. public final String commonName;
  148. // password for store
  149. public final String password;
  150. // password hint for README in bundle
  151. public String passwordHint;
  152. // E or EMAILADDRESS in distinguished name
  153. public String emailAddress;
  154. // start date of generated certificate
  155. public Date notBefore;
  156. // expiraiton date of generated certificate
  157. public Date notAfter;
  158. // hostname of server for which certificate is generated
  159. public String serverHostname;
  160. // displayname of user for README in bundle
  161. public String userDisplayname;
  162. // serialnumber of generated or read certificate
  163. public String serialNumber;
  164. public X509Metadata(String cn, String pwd) {
  165. if (StringUtils.isEmpty(cn)) {
  166. throw new RuntimeException("Common name required!");
  167. }
  168. if (StringUtils.isEmpty(pwd)) {
  169. throw new RuntimeException("Password required!");
  170. }
  171. commonName = cn;
  172. password = pwd;
  173. Calendar c = Calendar.getInstance(TimeZone.getDefault());
  174. c.set(Calendar.SECOND, 0);
  175. c.set(Calendar.MILLISECOND, 0);
  176. notBefore = c.getTime();
  177. c.add(Calendar.YEAR, 1);
  178. c.add(Calendar.DATE, 1);
  179. notAfter = c.getTime();
  180. oids = new HashMap<String, String>();
  181. }
  182. public X509Metadata clone(String commonName, String password) {
  183. X509Metadata clone = new X509Metadata(commonName, password);
  184. clone.emailAddress = emailAddress;
  185. clone.notBefore = notBefore;
  186. clone.notAfter = notAfter;
  187. clone.oids.putAll(oids);
  188. clone.passwordHint = passwordHint;
  189. clone.serverHostname = serverHostname;
  190. clone.userDisplayname = userDisplayname;
  191. return clone;
  192. }
  193. public String getOID(String oid, String defaultValue) {
  194. if (oids.containsKey(oid)) {
  195. return oids.get(oid);
  196. }
  197. return defaultValue;
  198. }
  199. public void setOID(String oid, String value) {
  200. if (StringUtils.isEmpty(value)) {
  201. oids.remove(oid);
  202. } else {
  203. oids.put(oid, value);
  204. }
  205. }
  206. }
  207. /**
  208. * Prepare all the certificates and stores necessary for a Gitblit GO server.
  209. *
  210. * @param metadata
  211. * @param folder
  212. * @param x509log
  213. */
  214. public static void prepareX509Infrastructure(X509Metadata metadata, File folder, X509Log x509log) {
  215. // make the specified folder, if necessary
  216. folder.mkdirs();
  217. // Gitblit CA certificate
  218. File caKeyStore = new File(folder, CA_KEY_STORE);
  219. if (!caKeyStore.exists()) {
  220. logger.info(MessageFormat.format("Generating {0} ({1})", CA_CN, caKeyStore.getAbsolutePath()));
  221. X509Certificate caCert = newCertificateAuthority(metadata, caKeyStore, x509log);
  222. saveCertificate(caCert, new File(caKeyStore.getParentFile(), "ca.cer"));
  223. }
  224. // Gitblit CRL
  225. File caRevocationList = new File(folder, CA_REVOCATION_LIST);
  226. if (!caRevocationList.exists()) {
  227. logger.info(MessageFormat.format("Generating {0} CRL ({1})", CA_CN, caRevocationList.getAbsolutePath()));
  228. newCertificateRevocationList(caRevocationList, caKeyStore, metadata.password);
  229. x509log.log("new certificate revocation list created");
  230. }
  231. // rename the old keystore to the new name
  232. File oldKeyStore = new File(folder, "keystore");
  233. if (oldKeyStore.exists()) {
  234. oldKeyStore.renameTo(new File(folder, SERVER_KEY_STORE));
  235. logger.info(MessageFormat.format("Renaming {0} to {1}", oldKeyStore.getName(), SERVER_KEY_STORE));
  236. }
  237. // create web SSL certificate signed by CA
  238. File serverKeyStore = new File(folder, SERVER_KEY_STORE);
  239. if (!serverKeyStore.exists()) {
  240. logger.info(MessageFormat.format("Generating SSL certificate for {0} signed by {1} ({2})", metadata.commonName, CA_CN, serverKeyStore.getAbsolutePath()));
  241. PrivateKey caPrivateKey = getPrivateKey(CA_ALIAS, caKeyStore, metadata.password);
  242. X509Certificate caCert = getCertificate(CA_ALIAS, caKeyStore, metadata.password);
  243. newSSLCertificate(metadata, caPrivateKey, caCert, serverKeyStore, x509log);
  244. }
  245. // server certificate trust store holds trusted public certificates
  246. File serverTrustStore = new File(folder, X509Utils.SERVER_TRUST_STORE);
  247. if (!serverTrustStore.exists()) {
  248. logger.info(MessageFormat.format("Importing {0} into trust store ({1})", CA_ALIAS, serverTrustStore.getAbsolutePath()));
  249. X509Certificate caCert = getCertificate(CA_ALIAS, caKeyStore, metadata.password);
  250. addTrustedCertificate(CA_ALIAS, caCert, serverTrustStore, metadata.password);
  251. }
  252. }
  253. /**
  254. * Open a keystore. Store type is determined by file extension of name. If
  255. * undetermined, JKS is assumed. The keystore does not need to exist.
  256. *
  257. * @param storeFile
  258. * @param storePassword
  259. * @return a KeyStore
  260. */
  261. public static KeyStore openKeyStore(File storeFile, String storePassword) {
  262. String lc = storeFile.getName().toLowerCase();
  263. String type = "JKS";
  264. String provider = null;
  265. if (lc.endsWith(".p12") || lc.endsWith(".pfx")) {
  266. type = "PKCS12";
  267. provider = BC;
  268. }
  269. try {
  270. KeyStore store;
  271. if (provider == null) {
  272. store = KeyStore.getInstance(type);
  273. } else {
  274. store = KeyStore.getInstance(type, provider);
  275. }
  276. if (storeFile.exists()) {
  277. FileInputStream fis = null;
  278. try {
  279. fis = new FileInputStream(storeFile);
  280. store.load(fis, storePassword.toCharArray());
  281. } finally {
  282. if (fis != null) {
  283. fis.close();
  284. }
  285. }
  286. } else {
  287. store.load(null);
  288. }
  289. return store;
  290. } catch (Exception e) {
  291. throw new RuntimeException("Could not open keystore " + storeFile, e);
  292. }
  293. }
  294. /**
  295. * Saves the keystore to the specified file.
  296. *
  297. * @param targetStoreFile
  298. * @param store
  299. * @param password
  300. */
  301. public static void saveKeyStore(File targetStoreFile, KeyStore store, String password) {
  302. File folder = targetStoreFile.getAbsoluteFile().getParentFile();
  303. if (!folder.exists()) {
  304. folder.mkdirs();
  305. }
  306. File tmpFile = new File(folder, Long.toHexString(System.currentTimeMillis()) + ".tmp");
  307. FileOutputStream fos = null;
  308. try {
  309. fos = new FileOutputStream(tmpFile);
  310. store.store(fos, password.toCharArray());
  311. fos.flush();
  312. fos.close();
  313. if (targetStoreFile.exists()) {
  314. targetStoreFile.delete();
  315. }
  316. tmpFile.renameTo(targetStoreFile);
  317. } catch (IOException e) {
  318. String message = e.getMessage().toLowerCase();
  319. if (message.contains("illegal key size")) {
  320. throw new RuntimeException("Illegal Key Size! You might consider installing the JCE Unlimited Strength Jurisdiction Policy files for your JVM.");
  321. } else {
  322. throw new RuntimeException("Could not save keystore " + targetStoreFile, e);
  323. }
  324. } catch (Exception e) {
  325. throw new RuntimeException("Could not save keystore " + targetStoreFile, e);
  326. } finally {
  327. if (fos != null) {
  328. try {
  329. fos.close();
  330. } catch (IOException e) {
  331. }
  332. }
  333. if (tmpFile.exists()) {
  334. tmpFile.delete();
  335. }
  336. }
  337. }
  338. /**
  339. * Retrieves the X509 certificate with the specified alias from the certificate
  340. * store.
  341. *
  342. * @param alias
  343. * @param storeFile
  344. * @param storePassword
  345. * @return the certificate
  346. */
  347. public static X509Certificate getCertificate(String alias, File storeFile, String storePassword) {
  348. try {
  349. KeyStore store = openKeyStore(storeFile, storePassword);
  350. X509Certificate caCert = (X509Certificate) store.getCertificate(alias);
  351. return caCert;
  352. } catch (Exception e) {
  353. throw new RuntimeException(e);
  354. }
  355. }
  356. /**
  357. * Retrieves the private key for the specified alias from the certificate
  358. * store.
  359. *
  360. * @param alias
  361. * @param storeFile
  362. * @param storePassword
  363. * @return the private key
  364. */
  365. public static PrivateKey getPrivateKey(String alias, File storeFile, String storePassword) {
  366. try {
  367. KeyStore store = openKeyStore(storeFile, storePassword);
  368. PrivateKey key = (PrivateKey) store.getKey(alias, storePassword.toCharArray());
  369. return key;
  370. } catch (Exception e) {
  371. throw new RuntimeException(e);
  372. }
  373. }
  374. /**
  375. * Saves the certificate to the file system. If the destination filename
  376. * ends with the pem extension, the certificate is written in the PEM format,
  377. * otherwise the certificate is written in the DER format.
  378. *
  379. * @param cert
  380. * @param targetFile
  381. */
  382. public static void saveCertificate(X509Certificate cert, File targetFile) {
  383. File folder = targetFile.getAbsoluteFile().getParentFile();
  384. if (!folder.exists()) {
  385. folder.mkdirs();
  386. }
  387. File tmpFile = new File(folder, Long.toHexString(System.currentTimeMillis()) + ".tmp");
  388. try {
  389. boolean asPem = targetFile.getName().toLowerCase().endsWith(".pem");
  390. if (asPem) {
  391. // PEM encoded X509
  392. JcaPEMWriter pemWriter = null;
  393. try {
  394. pemWriter = new JcaPEMWriter(new FileWriter(tmpFile));
  395. pemWriter.writeObject(cert);
  396. pemWriter.flush();
  397. } finally {
  398. if (pemWriter != null) {
  399. pemWriter.close();
  400. }
  401. }
  402. } else {
  403. // DER encoded X509
  404. FileOutputStream fos = null;
  405. try {
  406. fos = new FileOutputStream(tmpFile);
  407. fos.write(cert.getEncoded());
  408. fos.flush();
  409. } finally {
  410. if (fos != null) {
  411. fos.close();
  412. }
  413. }
  414. }
  415. // rename tmp file to target
  416. if (targetFile.exists()) {
  417. targetFile.delete();
  418. }
  419. tmpFile.renameTo(targetFile);
  420. } catch (Exception e) {
  421. if (tmpFile.exists()) {
  422. tmpFile.delete();
  423. }
  424. throw new RuntimeException("Failed to save certificate " + cert.getSubjectX500Principal().getName(), e);
  425. }
  426. }
  427. /**
  428. * Generate a new keypair.
  429. *
  430. * @return a keypair
  431. * @throws Exception
  432. */
  433. private static KeyPair newKeyPair() throws Exception {
  434. KeyPairGenerator kpGen = KeyPairGenerator.getInstance(KEY_ALGORITHM, BC);
  435. kpGen.initialize(KEY_LENGTH, new SecureRandom());
  436. return kpGen.generateKeyPair();
  437. }
  438. /**
  439. * Builds a distinguished name from the X509Metadata.
  440. *
  441. * @return a DN
  442. */
  443. private static X500Name buildDistinguishedName(X509Metadata metadata) {
  444. X500NameBuilder dnBuilder = new X500NameBuilder(BCStyle.INSTANCE);
  445. setOID(dnBuilder, metadata, "C", null);
  446. setOID(dnBuilder, metadata, "ST", null);
  447. setOID(dnBuilder, metadata, "L", null);
  448. setOID(dnBuilder, metadata, "O", Constants.NAME);
  449. setOID(dnBuilder, metadata, "OU", Constants.NAME);
  450. setOID(dnBuilder, metadata, "E", metadata.emailAddress);
  451. setOID(dnBuilder, metadata, "CN", metadata.commonName);
  452. X500Name dn = dnBuilder.build();
  453. return dn;
  454. }
  455. private static void setOID(X500NameBuilder dnBuilder, X509Metadata metadata,
  456. String oid, String defaultValue) {
  457. String value = null;
  458. if (metadata.oids != null && metadata.oids.containsKey(oid)) {
  459. value = metadata.oids.get(oid);
  460. }
  461. if (StringUtils.isEmpty(value)) {
  462. value = defaultValue;
  463. }
  464. if (!StringUtils.isEmpty(value)) {
  465. try {
  466. Field field = BCStyle.class.getField(oid);
  467. ASN1ObjectIdentifier objectId = (ASN1ObjectIdentifier) field.get(null);
  468. dnBuilder.addRDN(objectId, value);
  469. } catch (Exception e) {
  470. logger.error(MessageFormat.format("Failed to set OID \"{0}\"!", oid) ,e);
  471. }
  472. }
  473. }
  474. /**
  475. * Creates a new SSL certificate signed by the CA private key and stored in
  476. * keyStore.
  477. *
  478. * @param sslMetadata
  479. * @param caPrivateKey
  480. * @param caCert
  481. * @param targetStoreFile
  482. * @param x509log
  483. */
  484. public static X509Certificate newSSLCertificate(X509Metadata sslMetadata, PrivateKey caPrivateKey, X509Certificate caCert, File targetStoreFile, X509Log x509log) {
  485. try {
  486. KeyPair pair = newKeyPair();
  487. X500Name webDN = buildDistinguishedName(sslMetadata);
  488. X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName());
  489. X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
  490. issuerDN,
  491. BigInteger.valueOf(System.currentTimeMillis()),
  492. sslMetadata.notBefore,
  493. sslMetadata.notAfter,
  494. webDN,
  495. pair.getPublic());
  496. JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
  497. certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
  498. certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
  499. certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
  500. // support alternateSubjectNames for SSL certificates
  501. List<GeneralName> altNames = new ArrayList<GeneralName>();
  502. if (HttpUtils.isIpAddress(sslMetadata.commonName)) {
  503. altNames.add(new GeneralName(GeneralName.iPAddress, sslMetadata.commonName));
  504. }
  505. if (altNames.size() > 0) {
  506. GeneralNames subjectAltName = new GeneralNames(altNames.toArray(new GeneralName [altNames.size()]));
  507. certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
  508. }
  509. ContentSigner caSigner = new JcaContentSignerBuilder(SIGNING_ALGORITHM)
  510. .setProvider(BC).build(caPrivateKey);
  511. X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
  512. .getCertificate(certBuilder.build(caSigner));
  513. cert.checkValidity(new Date());
  514. cert.verify(caCert.getPublicKey());
  515. // Save to keystore
  516. KeyStore serverStore = openKeyStore(targetStoreFile, sslMetadata.password);
  517. serverStore.setKeyEntry(sslMetadata.commonName, pair.getPrivate(), sslMetadata.password.toCharArray(),
  518. new Certificate[] { cert, caCert });
  519. saveKeyStore(targetStoreFile, serverStore, sslMetadata.password);
  520. x509log.log(MessageFormat.format("New SSL certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName()));
  521. // update serial number in metadata object
  522. sslMetadata.serialNumber = cert.getSerialNumber().toString();
  523. return cert;
  524. } catch (Throwable t) {
  525. throw new RuntimeException("Failed to generate SSL certificate!", t);
  526. }
  527. }
  528. /**
  529. * Creates a new certificate authority PKCS#12 store. This function will
  530. * destroy any existing CA store.
  531. *
  532. * @param metadata
  533. * @param storeFile
  534. * @param keystorePassword
  535. * @param x509log
  536. * @return
  537. */
  538. public static X509Certificate newCertificateAuthority(X509Metadata metadata, File storeFile, X509Log x509log) {
  539. try {
  540. KeyPair caPair = newKeyPair();
  541. ContentSigner caSigner = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPair.getPrivate());
  542. // clone metadata
  543. X509Metadata caMetadata = metadata.clone(CA_CN, metadata.password);
  544. X500Name issuerDN = buildDistinguishedName(caMetadata);
  545. // Generate self-signed certificate
  546. X509v3CertificateBuilder caBuilder = new JcaX509v3CertificateBuilder(
  547. issuerDN,
  548. BigInteger.valueOf(System.currentTimeMillis()),
  549. caMetadata.notBefore,
  550. caMetadata.notAfter,
  551. issuerDN,
  552. caPair.getPublic());
  553. JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
  554. caBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(caPair.getPublic()));
  555. caBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caPair.getPublic()));
  556. caBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
  557. caBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign));
  558. JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(BC);
  559. X509Certificate cert = converter.getCertificate(caBuilder.build(caSigner));
  560. // confirm the validity of the CA certificate
  561. cert.checkValidity(new Date());
  562. cert.verify(cert.getPublicKey());
  563. // Delete existing keystore
  564. if (storeFile.exists()) {
  565. storeFile.delete();
  566. }
  567. // Save private key and certificate to new keystore
  568. KeyStore store = openKeyStore(storeFile, caMetadata.password);
  569. store.setKeyEntry(CA_ALIAS, caPair.getPrivate(), caMetadata.password.toCharArray(),
  570. new Certificate[] { cert });
  571. saveKeyStore(storeFile, store, caMetadata.password);
  572. x509log.log(MessageFormat.format("New CA certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getIssuerDN().getName()));
  573. // update serial number in metadata object
  574. caMetadata.serialNumber = cert.getSerialNumber().toString();
  575. return cert;
  576. } catch (Throwable t) {
  577. throw new RuntimeException("Failed to generate Gitblit CA certificate!", t);
  578. }
  579. }
  580. /**
  581. * Creates a new certificate revocation list (CRL). This function will
  582. * destroy any existing CRL file.
  583. *
  584. * @param caRevocationList
  585. * @param storeFile
  586. * @param keystorePassword
  587. * @return
  588. */
  589. public static void newCertificateRevocationList(File caRevocationList, File caKeystoreFile, String caKeystorePassword) {
  590. try {
  591. // read the Gitblit CA key and certificate
  592. KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword);
  593. PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray());
  594. X509Certificate caCert = (X509Certificate) store.getCertificate(CA_ALIAS);
  595. X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName());
  596. X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date());
  597. // build and sign CRL with CA private key
  598. ContentSigner signer = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPrivateKey);
  599. X509CRLHolder crl = crlBuilder.build(signer);
  600. File tmpFile = new File(caRevocationList.getParentFile(), Long.toHexString(System.currentTimeMillis()) + ".tmp");
  601. FileOutputStream fos = null;
  602. try {
  603. fos = new FileOutputStream(tmpFile);
  604. fos.write(crl.getEncoded());
  605. fos.flush();
  606. fos.close();
  607. if (caRevocationList.exists()) {
  608. caRevocationList.delete();
  609. }
  610. tmpFile.renameTo(caRevocationList);
  611. } finally {
  612. if (fos != null) {
  613. fos.close();
  614. }
  615. if (tmpFile.exists()) {
  616. tmpFile.delete();
  617. }
  618. }
  619. } catch (Exception e) {
  620. throw new RuntimeException("Failed to create new certificate revocation list " + caRevocationList, e);
  621. }
  622. }
  623. /**
  624. * Imports a certificate into the trust store.
  625. *
  626. * @param alias
  627. * @param cert
  628. * @param storeFile
  629. * @param storePassword
  630. */
  631. public static void addTrustedCertificate(String alias, X509Certificate cert, File storeFile, String storePassword) {
  632. try {
  633. KeyStore store = openKeyStore(storeFile, storePassword);
  634. store.setCertificateEntry(alias, cert);
  635. saveKeyStore(storeFile, store, storePassword);
  636. } catch (Exception e) {
  637. throw new RuntimeException("Failed to import certificate into trust store " + storeFile, e);
  638. }
  639. }
  640. /**
  641. * Creates a new client certificate PKCS#12 and PEM store. Any existing
  642. * stores are destroyed. After generation, the certificates are bundled
  643. * into a zip file with a personalized README file.
  644. *
  645. * The zip file reference is returned.
  646. *
  647. * @param clientMetadata a container for dynamic parameters needed for generation
  648. * @param caKeystoreFile
  649. * @param caKeystorePassword
  650. * @param x509log
  651. * @return a zip file containing the P12, PEM, and personalized README
  652. */
  653. public static File newClientBundle(X509Metadata clientMetadata, File caKeystoreFile,
  654. String caKeystorePassword, X509Log x509log) {
  655. return newClientBundle(null,clientMetadata,caKeystoreFile,caKeystorePassword,x509log);
  656. }
  657. /**
  658. * Creates a new client certificate PKCS#12 and PEM store. Any existing
  659. * stores are destroyed. After generation, the certificates are bundled
  660. * into a zip file with a personalized README file.
  661. *
  662. * The zip file reference is returned.
  663. *
  664. * @param user
  665. * @param clientMetadata a container for dynamic parameters needed for generation
  666. * @param caKeystoreFile
  667. * @param caKeystorePassword
  668. * @param x509log
  669. * @return a zip file containing the P12, PEM, and personalized README
  670. */
  671. public static File newClientBundle(com.gitblit.models.UserModel user,X509Metadata clientMetadata, File caKeystoreFile,
  672. String caKeystorePassword, X509Log x509log) {
  673. try {
  674. // read the Gitblit CA key and certificate
  675. KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword);
  676. PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray());
  677. X509Certificate caCert = (X509Certificate) store.getCertificate(CA_ALIAS);
  678. // generate the P12 and PEM files
  679. File targetFolder = new File(caKeystoreFile.getParentFile(), clientMetadata.commonName);
  680. X509Certificate cert = newClientCertificate(clientMetadata, caPrivateKey, caCert, targetFolder);
  681. x509log.log(MessageFormat.format("New client certificate {0,number,0} [{1}]", cert.getSerialNumber(), cert.getSubjectDN().getName()));
  682. // process template message
  683. String readme = null;
  684. String sInstructionsFileName = "instructions.tmpl";
  685. if( user == null )
  686. readme = processTemplate(new File(caKeystoreFile.getParentFile(),sInstructionsFileName), clientMetadata);
  687. else{
  688. File fileInstructionsTmp = null;
  689. if( (fileInstructionsTmp = new File(caKeystoreFile.getParentFile(),sInstructionsFileName+"_"+user.getPreferences().getLocale())).exists() )
  690. readme = processTemplate(fileInstructionsTmp,clientMetadata);
  691. else
  692. readme = processTemplate(new File(caKeystoreFile.getParentFile(),sInstructionsFileName),clientMetadata);
  693. }
  694. // Create a zip bundle with the p12, pem, and a personalized readme
  695. File zipFile = new File(targetFolder, clientMetadata.commonName + ".zip");
  696. if (zipFile.exists()) {
  697. zipFile.delete();
  698. }
  699. ZipOutputStream zos = null;
  700. try {
  701. zos = new ZipOutputStream(new FileOutputStream(zipFile));
  702. File p12File = new File(targetFolder, clientMetadata.commonName + ".p12");
  703. if (p12File.exists()) {
  704. zos.putNextEntry(new ZipEntry(p12File.getName()));
  705. zos.write(FileUtils.readContent(p12File));
  706. zos.closeEntry();
  707. }
  708. File pemFile = new File(targetFolder, clientMetadata.commonName + ".pem");
  709. if (pemFile.exists()) {
  710. zos.putNextEntry(new ZipEntry(pemFile.getName()));
  711. zos.write(FileUtils.readContent(pemFile));
  712. zos.closeEntry();
  713. }
  714. // include user's public certificate
  715. zos.putNextEntry(new ZipEntry(clientMetadata.commonName + ".cer"));
  716. zos.write(cert.getEncoded());
  717. zos.closeEntry();
  718. // include CA public certificate
  719. zos.putNextEntry(new ZipEntry("ca.cer"));
  720. zos.write(caCert.getEncoded());
  721. zos.closeEntry();
  722. if (readme != null) {
  723. zos.putNextEntry(new ZipEntry("README.TXT"));
  724. zos.write(readme.getBytes("UTF-8"));
  725. zos.closeEntry();
  726. }
  727. zos.flush();
  728. } finally {
  729. if (zos != null) {
  730. zos.close();
  731. }
  732. }
  733. return zipFile;
  734. } catch (Throwable t) {
  735. throw new RuntimeException("Failed to generate client bundle!", t);
  736. }
  737. }
  738. /**
  739. * Creates a new client certificate PKCS#12 and PEM store. Any existing
  740. * stores are destroyed.
  741. *
  742. * @param clientMetadata a container for dynamic parameters needed for generation
  743. * @param caKeystoreFile
  744. * @param caKeystorePassword
  745. * @param targetFolder
  746. * @return
  747. */
  748. public static X509Certificate newClientCertificate(X509Metadata clientMetadata,
  749. PrivateKey caPrivateKey, X509Certificate caCert, File targetFolder) {
  750. try {
  751. KeyPair pair = newKeyPair();
  752. X500Name userDN = buildDistinguishedName(clientMetadata);
  753. X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(caCert).getName());
  754. // create a new certificate signed by the Gitblit CA certificate
  755. X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
  756. issuerDN,
  757. BigInteger.valueOf(System.currentTimeMillis()),
  758. clientMetadata.notBefore,
  759. clientMetadata.notAfter,
  760. userDN,
  761. pair.getPublic());
  762. JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
  763. certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(pair.getPublic()));
  764. certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
  765. certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert.getPublicKey()));
  766. certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyEncipherment | KeyUsage.digitalSignature));
  767. if (!StringUtils.isEmpty(clientMetadata.emailAddress)) {
  768. GeneralNames subjectAltName = new GeneralNames(
  769. new GeneralName(GeneralName.rfc822Name, clientMetadata.emailAddress));
  770. certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
  771. }
  772. ContentSigner signer = new JcaContentSignerBuilder(SIGNING_ALGORITHM).setProvider(BC).build(caPrivateKey);
  773. X509Certificate userCert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certBuilder.build(signer));
  774. PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier)pair.getPrivate();
  775. bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,
  776. extUtils.createSubjectKeyIdentifier(pair.getPublic()));
  777. // confirm the validity of the user certificate
  778. userCert.checkValidity();
  779. userCert.verify(caCert.getPublicKey());
  780. userCert.getIssuerDN().equals(caCert.getSubjectDN());
  781. // verify user certificate chain
  782. verifyChain(userCert, caCert);
  783. targetFolder.mkdirs();
  784. // save certificate, stamped with unique name
  785. String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
  786. String id = date;
  787. File certFile = new File(targetFolder, id + ".cer");
  788. int count = 0;
  789. while (certFile.exists()) {
  790. id = date + "_" + Character.toString((char) (0x61 + count));
  791. certFile = new File(targetFolder, id + ".cer");
  792. count++;
  793. }
  794. // save user private key, user certificate and CA certificate to a PKCS#12 store
  795. File p12File = new File(targetFolder, clientMetadata.commonName + ".p12");
  796. if (p12File.exists()) {
  797. p12File.delete();
  798. }
  799. KeyStore userStore = openKeyStore(p12File, clientMetadata.password);
  800. userStore.setKeyEntry(MessageFormat.format("Gitblit ({0}) {1} {2}", clientMetadata.serverHostname, clientMetadata.userDisplayname, id), pair.getPrivate(), null, new Certificate [] { userCert });
  801. userStore.setCertificateEntry(MessageFormat.format("Gitblit ({0}) Certificate Authority", clientMetadata.serverHostname), caCert);
  802. saveKeyStore(p12File, userStore, clientMetadata.password);
  803. // save user private key, user certificate, and CA certificate to a PEM store
  804. File pemFile = new File(targetFolder, clientMetadata.commonName + ".pem");
  805. if (pemFile.exists()) {
  806. pemFile.delete();
  807. }
  808. JcePEMEncryptorBuilder builder = new JcePEMEncryptorBuilder("DES-EDE3-CBC");
  809. builder.setSecureRandom(new SecureRandom());
  810. PEMEncryptor pemEncryptor = builder.build(clientMetadata.password.toCharArray());
  811. JcaPEMWriter pemWriter = new JcaPEMWriter(new FileWriter(pemFile));
  812. pemWriter.writeObject(pair.getPrivate(), pemEncryptor);
  813. pemWriter.writeObject(userCert);
  814. pemWriter.writeObject(caCert);
  815. pemWriter.flush();
  816. pemWriter.close();
  817. // save certificate after successfully creating the key stores
  818. saveCertificate(userCert, certFile);
  819. // update serial number in metadata object
  820. clientMetadata.serialNumber = userCert.getSerialNumber().toString();
  821. return userCert;
  822. } catch (Throwable t) {
  823. throw new RuntimeException("Failed to generate client certificate!", t);
  824. }
  825. }
  826. /**
  827. * Verifies a certificate's chain to ensure that it will function properly.
  828. *
  829. * @param testCert
  830. * @param additionalCerts
  831. * @return
  832. */
  833. public static PKIXCertPathBuilderResult verifyChain(X509Certificate testCert, X509Certificate... additionalCerts) {
  834. try {
  835. // Check for self-signed certificate
  836. if (isSelfSigned(testCert)) {
  837. throw new RuntimeException("The certificate is self-signed. Nothing to verify.");
  838. }
  839. // Prepare a set of all certificates
  840. // chain builder must have all certs, including cert to validate
  841. // http://stackoverflow.com/a/10788392
  842. Set<X509Certificate> certs = new HashSet<X509Certificate>();
  843. certs.add(testCert);
  844. certs.addAll(Arrays.asList(additionalCerts));
  845. // Attempt to build the certification chain and verify it
  846. // Create the selector that specifies the starting certificate
  847. X509CertSelector selector = new X509CertSelector();
  848. selector.setCertificate(testCert);
  849. // Create the trust anchors (set of root CA certificates)
  850. Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
  851. for (X509Certificate cert : additionalCerts) {
  852. if (isSelfSigned(cert)) {
  853. trustAnchors.add(new TrustAnchor(cert, null));
  854. }
  855. }
  856. // Configure the PKIX certificate builder
  857. PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
  858. pkixParams.setRevocationEnabled(false);
  859. pkixParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certs), BC));
  860. // Build and verify the certification chain
  861. CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", BC);
  862. PKIXCertPathBuilderResult verifiedCertChain = (PKIXCertPathBuilderResult) builder.build(pkixParams);
  863. // The chain is built and verified
  864. return verifiedCertChain;
  865. } catch (CertPathBuilderException e) {
  866. throw new RuntimeException("Error building certification path: " + testCert.getSubjectX500Principal(), e);
  867. } catch (Exception e) {
  868. throw new RuntimeException("Error verifying the certificate: " + testCert.getSubjectX500Principal(), e);
  869. }
  870. }
  871. /**
  872. * Checks whether given X.509 certificate is self-signed.
  873. *
  874. * @param cert
  875. * @return true if the certificate is self-signed
  876. */
  877. public static boolean isSelfSigned(X509Certificate cert) {
  878. try {
  879. cert.verify(cert.getPublicKey());
  880. return true;
  881. } catch (SignatureException e) {
  882. return false;
  883. } catch (InvalidKeyException e) {
  884. return false;
  885. } catch (Exception e) {
  886. throw new RuntimeException(e);
  887. }
  888. }
  889. public static String processTemplate(File template, X509Metadata metadata) {
  890. String content = null;
  891. if (template.exists()) {
  892. String message = FileUtils.readContent(template, "\n");
  893. if (!StringUtils.isEmpty(message)) {
  894. content = message;
  895. if (!StringUtils.isEmpty(metadata.serverHostname)) {
  896. content = content.replace("$serverHostname", metadata.serverHostname);
  897. }
  898. if (!StringUtils.isEmpty(metadata.commonName)) {
  899. content = content.replace("$username", metadata.commonName);
  900. }
  901. if (!StringUtils.isEmpty(metadata.userDisplayname)) {
  902. content = content.replace("$userDisplayname", metadata.userDisplayname);
  903. }
  904. if (!StringUtils.isEmpty(metadata.passwordHint)) {
  905. content = content.replace("$storePasswordHint", metadata.passwordHint);
  906. }
  907. }
  908. }
  909. return content;
  910. }
  911. /**
  912. * Revoke a certificate.
  913. *
  914. * @param cert
  915. * @param reason
  916. * @param caRevocationList
  917. * @param caKeystoreFile
  918. * @param caKeystorePassword
  919. * @param x509log
  920. * @return true if the certificate has been revoked
  921. */
  922. public static boolean revoke(X509Certificate cert, RevocationReason reason,
  923. File caRevocationList, File caKeystoreFile, String caKeystorePassword,
  924. X509Log x509log) {
  925. try {
  926. // read the Gitblit CA key and certificate
  927. KeyStore store = openKeyStore(caKeystoreFile, caKeystorePassword);
  928. PrivateKey caPrivateKey = (PrivateKey) store.getKey(CA_ALIAS, caKeystorePassword.toCharArray());
  929. return revoke(cert, reason, caRevocationList, caPrivateKey, x509log);
  930. } catch (Exception e) {
  931. logger.error(MessageFormat.format("Failed to revoke certificate {0,number,0} [{1}] in {2}",
  932. cert.getSerialNumber(), cert.getSubjectDN().getName(), caRevocationList));
  933. }
  934. return false;
  935. }
  936. /**
  937. * Revoke a certificate.
  938. *
  939. * @param cert
  940. * @param reason
  941. * @param caRevocationList
  942. * @param caPrivateKey
  943. * @param x509log
  944. * @return true if the certificate has been revoked
  945. */
  946. public static boolean revoke(X509Certificate cert, RevocationReason reason,
  947. File caRevocationList, PrivateKey caPrivateKey, X509Log x509log) {
  948. try {
  949. X500Name issuerDN = new X500Name(PrincipalUtil.getIssuerX509Principal(cert).getName());
  950. X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerDN, new Date());
  951. if (caRevocationList.exists()) {
  952. byte [] data = FileUtils.readContent(caRevocationList);
  953. X509CRLHolder crl = new X509CRLHolder(data);
  954. crlBuilder.addCRL(crl);
  955. }
  956. crlBuilder.addCRLEntry(cert.getSerialNumber(), new Date(), reason.ordinal());
  957. // build and sign CRL with CA private key
  958. ContentSigner signer = new JcaContentSignerBuilder("SHA1WithRSA").setProvider(BC).build(caPrivateKey);
  959. X509CRLHolder crl = crlBuilder.build(signer);
  960. File tmpFile = new File(caRevocationList.getParentFile(), Long.toHexString(System.currentTimeMillis()) + ".tmp");
  961. FileOutputStream fos = null;
  962. try {
  963. fos = new FileOutputStream(tmpFile);
  964. fos.write(crl.getEncoded());
  965. fos.flush();
  966. fos.close();
  967. if (caRevocationList.exists()) {
  968. caRevocationList.delete();
  969. }
  970. tmpFile.renameTo(caRevocationList);
  971. } finally {
  972. if (fos != null) {
  973. fos.close();
  974. }
  975. if (tmpFile.exists()) {
  976. tmpFile.delete();
  977. }
  978. }
  979. x509log.log(MessageFormat.format("Revoked certificate {0,number,0} reason: {1} [{2}]",
  980. cert.getSerialNumber(), reason.toString(), cert.getSubjectDN().getName()));
  981. return true;
  982. } catch (IOException | OperatorCreationException | CertificateEncodingException e) {
  983. logger.error(MessageFormat.format("Failed to revoke certificate {0,number,0} [{1}] in {2}",
  984. cert.getSerialNumber(), cert.getSubjectDN().getName(), caRevocationList));
  985. }
  986. return false;
  987. }
  988. /**
  989. * Returns true if the certificate has been revoked.
  990. *
  991. * @param cert
  992. * @param caRevocationList
  993. * @return true if the certificate is revoked
  994. */
  995. public static boolean isRevoked(X509Certificate cert, File caRevocationList) {
  996. if (!caRevocationList.exists()) {
  997. return false;
  998. }
  999. InputStream inStream = null;
  1000. try {
  1001. inStream = new FileInputStream(caRevocationList);
  1002. CertificateFactory cf = CertificateFactory.getInstance("X.509");
  1003. X509CRL crl = (X509CRL)cf.generateCRL(inStream);
  1004. return crl.isRevoked(cert);
  1005. } catch (Exception e) {
  1006. logger.error(MessageFormat.format("Failed to check revocation status for certificate {0,number,0} [{1}] in {2}",
  1007. cert.getSerialNumber(), cert.getSubjectDN().getName(), caRevocationList));
  1008. } finally {
  1009. if (inStream != null) {
  1010. try {
  1011. inStream.close();
  1012. } catch (Exception e) {
  1013. }
  1014. }
  1015. }
  1016. return false;
  1017. }
  1018. public static X509Metadata getMetadata(X509Certificate cert) {
  1019. Map<String, String> oids = new HashMap<String, String>();
  1020. try {
  1021. String dn = cert.getSubjectDN().getName();
  1022. LdapName ldapName = new LdapName(dn);
  1023. for (int i = 0; i < ldapName.size(); i++) {
  1024. String [] val = ldapName.get(i).trim().split("=", 2);
  1025. String oid = val[0].toUpperCase().trim();
  1026. String data = val[1].trim();
  1027. oids.put(oid, data);
  1028. }
  1029. } catch (Exception e) {
  1030. throw new RuntimeException(e);
  1031. }
  1032. X509Metadata metadata = new X509Metadata(oids.get("CN"), "whocares");
  1033. metadata.oids.putAll(oids);
  1034. metadata.serialNumber = cert.getSerialNumber().toString();
  1035. metadata.notAfter = cert.getNotAfter();
  1036. metadata.notBefore = cert.getNotBefore();
  1037. metadata.emailAddress = metadata.getOID("E", null);
  1038. if (metadata.emailAddress == null) {
  1039. metadata.emailAddress = metadata.getOID("EMAILADDRESS", null);
  1040. }
  1041. return metadata;
  1042. }
  1043. }