📄 certificatemanager.java
字号:
/** * $RCSfile$ * $Revision: $ * $Date: $ * * Copyright (C) 2008 Jive Software. All rights reserved. * * This software is published under the terms of the GNU Public License (GPL), * a copy of which is included in this distribution, or a commercial license * agreement with Jive. */package org.jivesoftware.util;import org.bouncycastle.asn1.*;import org.bouncycastle.asn1.x509.GeneralName;import org.bouncycastle.asn1.x509.GeneralNames;import org.bouncycastle.asn1.x509.X509Extensions;import org.bouncycastle.asn1.x509.X509Name;import org.bouncycastle.jce.PKCS10CertificationRequest;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.bouncycastle.openssl.PEMReader;import org.bouncycastle.openssl.PasswordFinder;import org.bouncycastle.x509.X509V3CertificateGenerator;import java.io.*;import java.math.BigInteger;import java.security.*;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.security.cert.CertificateParsingException;import java.security.cert.X509Certificate;import java.util.*;import java.util.LinkedList;import java.util.concurrent.CopyOnWriteArrayList;import java.util.regex.Matcher;import java.util.regex.Pattern;/** * Utility class that provides similar functionality to the keytool tool. Generated certificates * conform to the XMPP spec where domains are kept in the subject alternative names extension. * * @author Gaston Dombiak */public class CertificateManager { private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)"); private static Pattern valuesPattern = Pattern.compile("(?i)(=)([^,]*)"); private static Provider provider = new BouncyCastleProvider(); /** * The maximum length of lines in certification requests */ private static final int CERT_REQ_LINE_LENGTH = 76; private static List<CertificateEventListener> listeners = new CopyOnWriteArrayList<CertificateEventListener>(); static { // Add the BC provider to the list of security providers Security.addProvider(provider); } /** * Creates a new X509 certificate using the DSA algorithm. The new certificate together with its private * key are stored in the specified key store. However, the key store is not saved to the disk. This means * that it is up to the "caller" to save the key store to disk after new certificates have been added * to the store. * * @param ksKeys key store where the new certificate and private key are going to be stored. * @param keyPassword password of the keystore. * @param alias name to use when storing the certificate in the key store. * @param issuerDN Issuer string e.g "O=Grid,OU=OGSA,CN=ACME" * @param subjectDN Subject string e.g "O=Grid,OU=OGSA,CN=John Doe" * @param domain domain of the server to store in the subject alternative name extension. * @return the new X509 V3 Certificate. * @throws GeneralSecurityException * @throws IOException */ public static X509Certificate createDSACert(KeyStore ksKeys, String keyPassword, String alias, String issuerDN, String subjectDN, String domain) throws GeneralSecurityException, IOException { // Generate public and private keys KeyPair keyPair = generateKeyPair("DSA", 1024); // Create X509 certificate with keys and specified domain X509Certificate cert = createX509V3Certificate(keyPair, 60, issuerDN, subjectDN, domain, "SHA1withDSA"); // Store new certificate and private key in the keystore ksKeys.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new X509Certificate[]{cert}); // Notify listeners that a new certificate has been created for (CertificateEventListener listener : listeners) { try { listener.certificateCreated(ksKeys, alias, cert); } catch (Exception e) { Log.error(e); } } // Return new certificate return cert; } /** * Creates a new X509 certificate using the RSA algorithm. The new certificate together with its private * key are stored in the specified key store. However, the key store is not saved to the disk. This means * that it is up to the "caller" to save the key store to disk after new certificates have been added * to the store. * * @param ksKeys key store where the new certificate and private key are going to be stored. * @param keyPassword password of the keystore. * @param alias name to use when storing the certificate in the key store. * @param issuerDN Issuer string e.g "O=Grid,OU=OGSA,CN=ACME" * @param subjectDN Subject string e.g "O=Grid,OU=OGSA,CN=John Doe" * @param domain domain of the server to store in the subject alternative name extension. * @return the new X509 V3 Certificate. * @throws GeneralSecurityException * @throws IOException */ public static X509Certificate createRSACert(KeyStore ksKeys, String keyPassword, String alias, String issuerDN, String subjectDN, String domain) throws GeneralSecurityException, IOException { // Generate public and private keys KeyPair keyPair = generateKeyPair("RSA", 1024); // Create X509 certificate with keys and specified domain X509Certificate cert = createX509V3Certificate(keyPair, 60, issuerDN, subjectDN, domain, "MD5withRSA"); // Store new certificate and private key in the keystore ksKeys.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new X509Certificate[]{cert}); // Notify listeners that a new certificate has been created for (CertificateEventListener listener : listeners) { try { listener.certificateCreated(ksKeys, alias, cert); } catch (Exception e) { Log.error(e); } } // Return new certificate return cert; } /** * Deletes the specified certificate from the * * @param ksKeys key store where the certificate is stored. * @param alias alias of the certificate to delete. * @throws GeneralSecurityException * @throws IOException */ public static void deleteCertificate(KeyStore ksKeys, String alias) throws GeneralSecurityException, IOException { ksKeys.deleteEntry(alias); // Notify listeners that a new certificate has been created for (CertificateEventListener listener : listeners) { try { listener.certificateDeleted(ksKeys, alias); } catch (Exception e) { Log.error(e); } } } /** * Returns the identities of the remote server as defined in the specified certificate. The * identities are defined in the subjectDN of the certificate and it can also be defined in * the subjectAltName extensions of type "xmpp". When the extension is being used then the * identities defined in the extension are going to be returned. Otherwise, the value stored in * the subjectDN is returned. * * @param x509Certificate the certificate the holds the identities of the remote server. * @return the identities of the remote server as defined in the specified certificate. */ public static List<String> getPeerIdentities(X509Certificate x509Certificate) { // Look the identity in the subjectAltName extension if available List<String> names = getSubjectAlternativeNames(x509Certificate); if (names.isEmpty()) { String name = x509Certificate.getSubjectDN().getName(); Matcher matcher = cnPattern.matcher(name); if (matcher.find()) { name = matcher.group(2); } // Create an array with the unique identity names = new ArrayList<String>(); names.add(name); } return names; } /** * Returns the JID representation of an XMPP entity contained as a SubjectAltName extension * in the certificate. If none was found then return <tt>null</tt>. * * @param certificate the certificate presented by the remote entity. * @return the JID representation of an XMPP entity contained as a SubjectAltName extension * in the certificate. If none was found then return <tt>null</tt>. */ private static List<String> getSubjectAlternativeNames(X509Certificate certificate) { List<String> identities = new ArrayList<String>(); try { Collection<List<?>> altNames = certificate.getSubjectAlternativeNames(); // Check that the certificate includes the SubjectAltName extension if (altNames == null) { return Collections.emptyList(); } // Use the type OtherName to search for the certified server name for (List item : altNames) { Integer type = (Integer) item.get(0); if (type == 0) { // Type OtherName found so return the associated value try { // Value is encoded using ASN.1 so decode it to get the server's identity ASN1InputStream decoder = new ASN1InputStream((byte[]) item.toArray()[1]); DEREncodable encoded = decoder.readObject(); encoded = ((DERSequence) encoded).getObjectAt(1); encoded = ((DERTaggedObject) encoded).getObject(); encoded = ((DERTaggedObject) encoded).getObject(); String identity = ((DERUTF8String) encoded).getString(); if (!"".equals(identity)) { // Add the decoded server name to the list of identities identities.add(identity); } } catch (UnsupportedEncodingException e) { // Ignore } catch (IOException e) { // Ignore } catch (Exception e) { Log.error("CertificateManager: Error decoding subjectAltName", e); } } // Other types are not good for XMPP so ignore them else if (Log.isDebugEnabled()) { Log.debug("CertificateManager: SubjectAltName of invalid type found: " + certificate.getSubjectDN()); } } } catch (CertificateParsingException e) { Log.error("CertificateManager: Error parsing SubjectAltName in certificate: " + certificate.getSubjectDN(), e); } return identities; } /** * Returns true if an RSA certificate was found in the specified keystore for the specified domain. * * @param ksKeys the keystore that contains the certificates. * @param domain domain of the server signed by the certificate. * @return true if an RSA certificate was found in the specified keystore for the specified domain. * @throws KeyStoreException */ public static boolean isRSACertificate(KeyStore ksKeys, String domain) throws KeyStoreException { return isCertificate(ksKeys, domain, "RSA"); } /** * Returns true if an DSA certificate was found in the specified keystore for the specified domain. * * @param ksKeys the keystore that contains the certificates. * @param domain domain of the server signed by the certificate. * @return true if an DSA certificate was found in the specified keystore for the specified domain. * @throws KeyStoreException */ public static boolean isDSACertificate(KeyStore ksKeys, String domain) throws KeyStoreException { return isCertificate(ksKeys, domain, "DSA"); } /** * Returns true if the specified certificate is using the DSA algorithm. The DSA algorithm is not * good for encryption but only for authentication. On the other hand, the RSA algorithm is good * for encryption and authentication. * * @param certificate the certificate to analyze. * @return true if the specified certificate is using the DSA algorithm. * @throws KeyStoreException */ public static boolean isDSACertificate(X509Certificate certificate) throws KeyStoreException { return certificate.getPublicKey().getAlgorithm().equals("DSA"); } /** * Returns true if a certificate with the specifed configuration was found in the key store. * * @param ksKeys the keystore to use for searching the certificate. * @param domain the domain present in the subjectAltName or "*" if anything is accepted. * @param algorithm the DSA or RSA algorithm used by the certificate.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -