Commit 93a49fe9 authored by Guus der Kinderen's avatar Guus der Kinderen

Merge pull request #526 from guusdk/aberenguel-certificate-issues

Aberenguel certificate issues
parents 8c7a56f4 436da497
package org.jivesoftware.openfire.keystore;
import org.bouncycastle.operator.OperatorCreationException;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.net.DNSUtil;
import org.jivesoftware.util.CertificateManager;
......@@ -99,7 +100,7 @@ public class IdentityStore extends CertificateStore
return pemCSR;
}
catch ( IOException | NoSuchProviderException | SignatureException | InvalidKeyException | KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException e )
catch ( IOException | KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | OperatorCreationException e )
{
throw new CertificateStoreConfigException( "Cannot generate CSR for alias '"+ alias +"'", e );
}
......@@ -194,7 +195,7 @@ public class IdentityStore extends CertificateStore
final X509Certificate x509Certificate = (X509Certificate) certificate;
// First certificate in the chain should correspond with the certificate in the store
if ( !x509Certificate.getSerialNumber().equals( certificates.get( 0 ).getSerialNumber() ) )
if ( !x509Certificate.getPublicKey().equals(certificates.get(0).getPublicKey()) )
{
return false;
}
......@@ -402,8 +403,7 @@ public class IdentityStore extends CertificateStore
final String name = JiveGlobals.getProperty( "xmpp.domain" ).toLowerCase();
final String alias = name + "_" + algorithm.toLowerCase();
final String distinctName = "cn=" + name;
final int validityInDays = 60;
final int validityInDays = 5*365;
Log.info( "Generating a new private key and corresponding self-signed certificate for domain name '{}', using the {} algorithm (sign-algorithm: {} with a key size of {} bits). Certificate will be valid for {} days.", name, algorithm, signAlgorithm, keySize, validityInDays );
// Generate public and private keys
......@@ -412,7 +412,7 @@ public class IdentityStore extends CertificateStore
final KeyPair keyPair = generateKeyPair( algorithm.toUpperCase(), keySize );
// Create X509 certificate with keys and specified domain
final X509Certificate cert = CertificateManager.createX509V3Certificate( keyPair, validityInDays, distinctName, distinctName, name, signAlgorithm );
final X509Certificate cert = CertificateManager.createX509V3Certificate( keyPair, validityInDays, name, name, name, signAlgorithm );
// Store new certificate and private key in the key store
store.setKeyEntry( alias, keyPair.getPrivate(), configuration.getPassword(), new X509Certificate[]{cert} );
......
......@@ -20,7 +20,6 @@
package org.jivesoftware.openfire.session;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
......@@ -36,7 +35,6 @@ import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.StreamID;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.keystore.CertificateStoreManager;
import org.jivesoftware.openfire.net.SASLAuthentication;
import org.jivesoftware.openfire.net.SocketConnection;
import org.jivesoftware.openfire.server.ServerDialback;
......@@ -371,8 +369,7 @@ public class LocalIncomingServerSession extends LocalServerSession implements In
usingSelfSigned = true;
} else {
try {
final KeyStore keyStore = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore( ConnectionType.SOCKET_S2S ).getStore();
usingSelfSigned = CertificateManager.isSelfSignedCertificate(keyStore, (X509Certificate) chain[0]);
usingSelfSigned = CertificateManager.isSelfSignedCertificate((X509Certificate) chain[0]);
} catch (KeyStoreException ex) {
Log.warn("Exception occurred while trying to determine whether local certificate is self-signed. Proceeding as if it is.", ex);
usingSelfSigned = true;
......
......@@ -20,9 +20,23 @@
package org.jivesoftware.util;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CertPath;
import java.security.cert.CertPathBuilder;
import java.security.cert.CertPathBuilderException;
......@@ -36,42 +50,62 @@ import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.Extension;
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.openssl.PEMParser;
import org.bouncycastle.cert.CertException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.openssl.MiscPEMGenerator;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemObjectGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import org.jivesoftware.openfire.keystore.CertificateStore;
import org.jivesoftware.openfire.keystore.CertificateStoreConfigException;
import org.jivesoftware.openfire.keystore.CertificateUtils;
import org.jivesoftware.util.cert.CertificateIdentityMapping;
import org.jivesoftware.util.cert.CNCertificateIdentityMapping;
import org.jivesoftware.util.cert.CertificateIdentityMapping;
import org.jivesoftware.util.cert.SANCertificateIdentityMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -89,11 +123,6 @@ public class CertificateManager {
private static Pattern valuesPattern = Pattern.compile("(?i)(=)([^,]*)");
/**
* The maximum length of lines in certification requests
*/
private static final int CERT_REQ_LINE_LENGTH = 76;
private static List<CertificateEventListener> listeners = new CopyOnWriteArrayList<>();
private static List<CertificateIdentityMapping> serverCertMapping = new ArrayList<>();
......@@ -108,7 +137,7 @@ public class CertificateManager {
while (st.hasMoreTokens()) {
String s_provider = st.nextToken();
try {
Class c_provider = ClassUtils.forName(s_provider);
Class<?> c_provider = ClassUtils.forName(s_provider);
CertificateIdentityMapping provider =
(CertificateIdentityMapping)(c_provider.newInstance());
Log.debug("CertificateManager: Loaded server identity mapping " + s_provider);
......@@ -132,7 +161,7 @@ public class CertificateManager {
while (st.hasMoreTokens()) {
String s_provider = st.nextToken();
try {
Class c_provider = ClassUtils.forName(s_provider);
Class<?> c_provider = ClassUtils.forName(s_provider);
CertificateIdentityMapping provider =
(CertificateIdentityMapping)(c_provider.newInstance());
Log.debug("CertificateManager: Loaded client identity mapping " + s_provider);
......@@ -393,28 +422,13 @@ public class CertificateManager {
* @return true if the specified certificate is a self-signed certificate.
* @throws KeyStoreException if an error happens while usign the keystore
*/
public static boolean isSelfSignedCertificate(KeyStore keyStore, String alias) throws KeyStoreException {
// Get certificate chain
java.security.cert.Certificate[] certificateChain = keyStore.getCertificateChain( alias );
// Verify that the chain is empty or was signed by himself
return certificateChain == null || certificateChain.length == 1;
}
/**
* Returns true if the specified certificate is a self-signed certificate. If the certificate
* was not found in the store then a KeyStoreException is returned.
*
* @param keyStore key store that holds the certificate to verify.
* @param certificate the certificate in the key store.
* @return true if the specified certificate is a self-signed certificate.
* @throws KeyStoreException if an error happens while usign the keystore
*/
public static boolean isSelfSignedCertificate(KeyStore keyStore, X509Certificate certificate) throws KeyStoreException {
String alias = keyStore.getCertificateAlias( certificate );
if (alias == null) {
throw new KeyStoreException("Certificate not found in store: " + certificate);
public static boolean isSelfSignedCertificate(X509Certificate certificate) throws KeyStoreException {
try {
certificate.verify(certificate.getPublicKey());
return true;
} catch (GeneralSecurityException e) {
return false;
}
return isSelfSignedCertificate(keyStore, alias);
}
/**
......@@ -427,14 +441,13 @@ public class CertificateManager {
* @return true if the specified certificate is ready to be signed by a Certificate Authority.
* @throws KeyStoreException if an error happens while usign the keystore
*/
public static boolean isSigningRequestPending(KeyStore keyStore, String alias) throws KeyStoreException {
public static boolean isSigningRequestPending(X509Certificate certificate) throws KeyStoreException {
// Verify that this is a self-signed certificate
if (!isSelfSignedCertificate(keyStore, alias)) {
if (!isSelfSignedCertificate(certificate)) {
return false;
}
// Verify that the issuer information has been entered
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
Matcher matcher = valuesPattern.matcher( certificate.getIssuerDN().toString() );
Matcher matcher = valuesPattern.matcher(certificate.getIssuerDN().toString());
return matcher.find() && matcher.find();
}
......@@ -450,44 +463,26 @@ public class CertificateManager {
* @param privKey the private key of the certificate.
* @return the content of a new singing request for the specified certificate.
*/
public static String createSigningRequest(X509Certificate cert, PrivateKey privKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException, IOException
{
StringBuilder sb = new StringBuilder();
String subject = cert.getSubjectDN().getName();
X509Name xname = new X509Name(subject);
PublicKey pubKey = cert.getPublicKey();
String signatureAlgorithm = "DSA".equals(pubKey.getAlgorithm()) ? "SHA1withDSA" : "SHA1WITHRSAENCRYPTION";
public static String createSigningRequest(X509Certificate cert, PrivateKey privKey) throws OperatorCreationException, IOException {
PKCS10CertificationRequest csr =
new PKCS10CertificationRequest(signatureAlgorithm, xname, pubKey, null, privKey);
JcaPKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder( //
cert.getSubjectX500Principal(), //
cert.getPublicKey() //
);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DEROutputStream deros = new DEROutputStream(baos);
deros.writeObject(csr.toASN1Primitive());
String sTmp = new String(org.bouncycastle.util.encoders.Base64.encode(baos.toByteArray()));
String signatureAlgorithm = "SHA256WITH" + cert.getPublicKey().getAlgorithm();
// Header
sb.append("-----BEGIN NEW CERTIFICATE REQUEST-----\n");
ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).build(privKey);
PKCS10CertificationRequest csr = csrBuilder.build(signer);
// Add signing request content (base 64 encoded)
for (int iCnt = 0; iCnt < sTmp.length(); iCnt += CERT_REQ_LINE_LENGTH) {
int iLineLength;
StringWriter string = new StringWriter();
PemWriter pemWriter = new PemWriter(string);
if ((iCnt + CERT_REQ_LINE_LENGTH) > sTmp.length()) {
iLineLength = sTmp.length() - iCnt;
} else {
iLineLength = CERT_REQ_LINE_LENGTH;
}
PemObjectGenerator objGen = new MiscPEMGenerator(csr);
pemWriter.writeObject(objGen);
pemWriter.close();
sb.append(sTmp.substring(iCnt, iCnt + iLineLength)).append("\n");
}
// Footer
sb.append("-----END NEW CERTIFICATE REQUEST-----\n");
return sb.toString();
return string.toString();
}
/**
......@@ -508,7 +503,7 @@ public class CertificateManager {
* @return true if the CA reply was successfully processed.
* @throws Exception
*/
public static boolean installReply(KeyStore keyStore, KeyStore trustStore, String keyPassword, String alias, InputStream inputStream) throws Exception {
public static boolean installReply(KeyStore keyStore, KeyStore trustStore, char[] keyPassword, String alias, InputStream inputStream) throws Exception {
// Check that there is a certificate for the specified alias
X509Certificate certificate = (X509Certificate) keyStore.getCertificate( alias );
......@@ -517,7 +512,7 @@ public class CertificateManager {
return false;
}
// Retrieve the private key of the stored certificate
PrivateKey privKey = (PrivateKey) keyStore.getKey(alias, keyPassword.toCharArray());
PrivateKey privKey = (PrivateKey) keyStore.getKey(alias, keyPassword);
// Load certificates found in the PEM input stream
Collection<X509Certificate> certs = parseCertificates( inputStream );
if (certs.isEmpty()) {
......@@ -535,7 +530,7 @@ public class CertificateManager {
{
return false;
}
keyStore.setKeyEntry(alias, privKey, keyPassword.toCharArray(), newCerts.toArray(new X509Certificate[newCerts.size()]));
keyStore.setKeyEntry(alias, privKey, keyPassword, newCerts.toArray(new X509Certificate[newCerts.size()]));
// Notify listeners that a new certificate has been created
for (CertificateEventListener listener : listeners) {
......@@ -613,15 +608,15 @@ public class CertificateManager {
return true;
}
/**
* @deprecated Use {@link #parsePrivateKey(String, String)} instead.
*/
@Deprecated
public static PrivateKey parsePrivateKey( InputStream pemRepresentation, String passPhrase ) throws IOException
{
// see http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
final java.util.Scanner s = new java.util.Scanner( pemRepresentation ).useDelimiter("\\A");
return parsePrivateKey( s.hasNext() ? s.next() : "", passPhrase );
public static PrivateKey parsePrivateKey(String pemRepresentation, String passPhrase) throws IOException {
if (pemRepresentation == null || pemRepresentation.trim().isEmpty()) {
throw new IllegalArgumentException("Argument 'pemRepresentation' cannot be null or an empty String.");
}
ByteArrayInputStream input = new ByteArrayInputStream(pemRepresentation.getBytes(StandardCharsets.UTF_8));
return parsePrivateKey(input, passPhrase);
}
/**
......@@ -633,17 +628,15 @@ public class CertificateManager {
* @param passPhrase optional pass phrase (must be present if the private key is encrypted).
* @return a PrivateKey instance (never null)
*/
public static PrivateKey parsePrivateKey( String pemRepresentation, String passPhrase ) throws IOException
{
if ( pemRepresentation == null || pemRepresentation.trim().isEmpty() ) {
throw new IllegalArgumentException( "Argument 'pemRepresentation' cannot be null or an empty String.");
}
public static PrivateKey parsePrivateKey(InputStream pemRepresentation, String passPhrase) throws IOException {
if ( passPhrase == null ) {
passPhrase = "";
}
try ( Reader reader = new StringReader( pemRepresentation.trim() ))
{
final Object object = new PEMParser( reader ).readObject();
try (Reader reader = new InputStreamReader(pemRepresentation); //
PEMParser pemParser = new PEMParser(reader)) {
final Object object = pemParser.readObject();
final JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider( "BC" );
final KeyPair kp;
......@@ -682,15 +675,16 @@ public class CertificateManager {
}
}
/**
* @deprecated Use {@link #parseCertificates(String)} instead.
*/
@Deprecated
public static Collection<X509Certificate> parseCertificates( InputStream pemRepresentation ) throws IOException, CertificateException
{
// see http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
final java.util.Scanner s = new java.util.Scanner( pemRepresentation ).useDelimiter("\\A");
return parseCertificates( s.hasNext() ? s.next() : "" );
public static Collection<X509Certificate> parseCertificates(String pemRepresentation) throws IOException,
CertificateException {
// The parser is very picky. We should trim each line of the input string.
final String pem = pemRepresentation //
.replaceAll("(?m) +$", "") // remove trailing whitespace
.replaceAll("(?m)^ +", ""); // remove leading whitespace
ByteArrayInputStream input = new ByteArrayInputStream(pem.getBytes(StandardCharsets.UTF_8));
return parseCertificates(input);
}
/**
......@@ -699,22 +693,17 @@ public class CertificateManager {
* @param pemRepresentation a PEM representation of a certificate or certificate chain (cannot be null or empty)
* @return A collection of certificates (possibly empty, but never null).
*/
public static Collection<X509Certificate> parseCertificates( String pemRepresentation ) throws IOException, CertificateException
{
if ( pemRepresentation == null || pemRepresentation.trim().isEmpty() ) {
throw new IllegalArgumentException( "Argument 'pemRepresentation' cannot be null or an empty String.");
}
// The parser is very picky. We should trim each line of the input string.
final String pem = pemRepresentation
.replaceAll( "(?m) +$", "" ) // remove trailing whitespace
.replaceAll( "(?m)^ +", "" ); // remove leading whitespace
@SuppressWarnings("unchecked")
public static Collection<X509Certificate> parseCertificates(InputStream pemRepresentation) throws IOException,
CertificateException {
try ( InputStream inputStream = new ByteArrayInputStream( pem.getBytes() ) )
{
final CertificateFactory certificateFactory = CertificateFactory.getInstance( "X509" );
return (Collection<X509Certificate>) certificateFactory.generateCertificates( inputStream );
CertificateFactory certificateFactory;
try {
certificateFactory = CertificateFactory.getInstance("X509", "BC");
} catch (NoSuchProviderException e) {
certificateFactory = CertificateFactory.getInstance("X509");
}
return (Collection<X509Certificate>) certificateFactory.generateCertificates(pemRepresentation);
}
/**
......@@ -752,7 +741,7 @@ public class CertificateManager {
throw new Exception("Certificate reply and certificate in keystore are identical");
}
}
Map<Principal, List<X509Certificate>> knownCerts = new Hashtable<>();
Map<String, List<X509Certificate>> knownCerts = new Hashtable<>();
// TODO Figure out why we add keystore issuers. This implies that we always trust the issuer of our identitity (which probably is right, but shouldn't be required)
if (keyStore.size() > 0) {
......@@ -781,7 +770,7 @@ public class CertificateManager {
* @return true if the entire chain of all certificates was successfully built.
*/
private static boolean buildChain(X509Certificate certificate, LinkedList<X509Certificate> answer,
Map<Principal, List<X509Certificate>> knownCerts) {
Map<String, List<X509Certificate>> knownCerts) {
Principal subject = certificate.getSubjectDN();
Principal issuer = certificate.getIssuerDN();
// Check if the certificate is a root certificate (i.e. was issued by the same Principal that
......@@ -791,7 +780,7 @@ public class CertificateManager {
return true;
}
// Get the list of known certificates of the certificate's issuer
List<X509Certificate> issuerCerts = knownCerts.get(issuer);
List<X509Certificate> issuerCerts = knownCerts.get(issuer.getName());
if (issuerCerts == null || issuerCerts.isEmpty()) {
// No certificates were found so building of chain failed
return false;
......@@ -822,9 +811,9 @@ public class CertificateManager {
* @return a map with the certificates per issuer.
* @throws Exception
*/
private static Map<Principal, List<X509Certificate>> getCertsByIssuer(KeyStore ks)
private static Map<String, List<X509Certificate>> getCertsByIssuer(KeyStore ks)
throws Exception {
Map<Principal, List<X509Certificate>> answer = new HashMap<>();
Map<String, List<X509Certificate>> answer = new HashMap<>();
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
......@@ -841,7 +830,7 @@ public class CertificateManager {
vec.add(cert);
}
}
answer.put(subjectDN, vec);
answer.put(subjectDN.getName(), vec);
}
}
return answer;
......@@ -971,19 +960,46 @@ public class CertificateManager {
* Creates an X509 version3 certificate.
*
* @param kp KeyPair that keeps the public and private keys for the new certificate.
* @param months time to live
* @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 days time to live
* @param issuerCommonName Issuer CN string
* @param subjectCommonName Subject CN string
* @param domain Domain of the server.
* @param signAlgoritm Signature algorithm. This can be either a name or an OID.
* @return X509 V3 Certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public static synchronized X509Certificate createX509V3Certificate(KeyPair kp, int months, String issuerDN,
String subjectDN, String domain,
public static synchronized X509Certificate createX509V3Certificate(KeyPair kp, int days, String issuerCommonName,
String subjectCommonName, String domain,
String signAlgoritm)
throws GeneralSecurityException, IOException {
// subjectDN
X500NameBuilder subjectBuilder = new X500NameBuilder();
subjectBuilder.addRDN(BCStyle.CN, subjectCommonName);
// issuerDN
X500NameBuilder issuerBuilder = new X500NameBuilder();
issuerBuilder.addRDN(BCStyle.CN, issuerCommonName);
return createX509V3Certificate(kp, days, issuerBuilder, subjectBuilder, domain, signAlgoritm);
}
/**
* Creates an X509 version3 certificate.
*
* @param kp KeyPair that keeps the public and private keys for the new certificate.
* @param days time to live
* @param issuerBuilder IssuerDN builder
* @param subjectBuilder SubjectDN builder
* @param domain Domain of the server.
* @param signAlgoritm Signature algorithm. This can be either a name or an OID.
* @return X509 V3 Certificate
* @throws GeneralSecurityException
* @throws IOException
*/
public static synchronized X509Certificate createX509V3Certificate(KeyPair kp, int days, X500NameBuilder issuerBuilder,
X500NameBuilder subjectBuilder, String domain, String signAlgoritm) throws GeneralSecurityException, IOException {
PublicKey pubKey = kp.getPublic();
PrivateKey privKey = kp.getPrivate();
......@@ -993,32 +1009,52 @@ public class CertificateManager {
random.nextBytes(serno);
BigInteger serial = (new java.math.BigInteger(serno)).abs();
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
certGenerator.reset();
certGenerator.setSerialNumber(serial);
certGenerator.setIssuerDN(new X509Name(issuerDN));
certGenerator.setNotBefore(new Date(System.currentTimeMillis()));
certGenerator.setNotAfter(
new Date(System.currentTimeMillis() + months * (1000L * 60 * 60 * 24 * 30)));
certGenerator.setSubjectDN(new X509Name(subjectDN));
certGenerator.setPublicKey(pubKey);
certGenerator.setSignatureAlgorithm(signAlgoritm);
// Generate the subject alternative name
boolean critical = subjectDN == null || "".equals(subjectDN.trim());
X500Name issuerDN = issuerBuilder.build();
X500Name subjectDN = subjectBuilder.build();
// builder
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( //
issuerDN, //
serial, //
new Date(), //
new Date(System.currentTimeMillis() + days * (1000L * 60 * 60 * 24)), //
subjectDN, //
pubKey //
);
// add subjectAlternativeName extension
boolean critical = subjectDN.getRDNs().length == 0;
ASN1Sequence othernameSequence = new DERSequence(new ASN1Encodable[]{
new DERObjectIdentifier("1.3.6.1.5.5.7.8.5"), new DERTaggedObject(true, 0, new DERUTF8String(domain))});
new ASN1ObjectIdentifier("1.3.6.1.5.5.7.8.5"), new DERTaggedObject(true, 0, new DERUTF8String(domain))});
GeneralName othernameGN = new GeneralName(GeneralName.otherName, othernameSequence);
GeneralNames subjectAltNames = new GeneralNames(new GeneralName[]{othernameGN});
// Add subject alternative name extension
certGenerator.addExtension(X509Extensions.SubjectAlternativeName, critical, subjectAltNames);
certBuilder.addExtension(Extension.subjectAlternativeName, critical, subjectAltNames);
// add keyIdentifiers extensions
JcaX509ExtensionUtils utils = new JcaX509ExtensionUtils();
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, utils.createSubjectKeyIdentifier(pubKey));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, utils.createAuthorityKeyIdentifier(pubKey));
try {
// build the certificate
ContentSigner signer = new JcaContentSignerBuilder(signAlgoritm).build(privKey);
X509CertificateHolder cert = certBuilder.build(signer);
X509Certificate cert =
certGenerator.generateX509Certificate(privKey, "BC", new SecureRandom());
cert.checkValidity(new Date());
cert.verify(pubKey);
// verify the validity
if (!cert.isValidOn(new Date())) {
throw new GeneralSecurityException("Certificate validity not valid");
}
// verify the signature (self-signed)
ContentVerifierProvider verifierProvider = new JcaContentVerifierProviderBuilder().build(pubKey);
if (!cert.isSignatureValid(verifierProvider)) {
throw new GeneralSecurityException("Certificate signature not valid");
}
return cert;
return new JcaX509CertificateConverter().getCertificate(cert);
} catch (OperatorCreationException | CertException e) {
throw new GeneralSecurityException(e);
}
}
}
<%@page import="java.util.Enumeration"%>
<%@page import="org.jivesoftware.openfire.XMPPServer"%>
<%@page import="java.security.PublicKey"%>
<%@page import="java.security.KeyPair"%>
<%@page import="java.security.cert.X509Certificate"%>
<%@page import="java.security.PrivateKey"%>
<%@page import="org.jivesoftware.openfire.keystore.IdentityStore"%>
<%@page import="java.security.KeyStore"%>
<%@page import="org.bouncycastle.asn1.x500.X500Name"%>
<%@page import="org.bouncycastle.asn1.x500.style.BCStyle"%>
<%@page import="org.bouncycastle.asn1.x509.Extension"%>
<%@page import="org.bouncycastle.asn1.x500.X500NameBuilder"%>
<%@page import="org.jivesoftware.util.CertificateManager"%>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="java.util.HashMap" %>
......@@ -10,99 +23,101 @@
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<% webManager.init(request, response, session, application, out ); %>
<% // Get parameters:
<%
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
// Get parameters:
final boolean save = ParamUtils.getParameter(request, "save") != null;
final String name = ParamUtils.getParameter(request, "name");
final String name = domain;
final String organizationalUnit = ParamUtils.getParameter(request, "ou");
final String organization = ParamUtils.getParameter(request, "o");
final String city = ParamUtils.getParameter(request, "city");
final String state = ParamUtils.getParameter(request, "state");
final String countryCode = ParamUtils.getParameter(request, "country");
final String storePurposeText = ParamUtils.getParameter(request, "connectionType");
final String connectionTypeText = ParamUtils.getParameter( request, "connectionType" );
final Map<String, String> errors = new HashMap<String, String>();
ConnectionType connectionType;
ConnectionType connectionType = null;
IdentityStore identityStore = null;
try
{
connectionType = ConnectionType.valueOf( storePurposeText );
} catch (RuntimeException ex) {
connectionType = ConnectionType.valueOf( connectionTypeText );
identityStore = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore( connectionType );
if ( identityStore == null )
{
errors.put( "identityStore", "Unable to get an instance." );
}
}
catch (RuntimeException ex)
{
errors.put( "connectionType", ex.getMessage() );
connectionType = null;
}
pageContext.setAttribute( "connectionType", connectionType );
// if (save) {
//
// // Verify that fields were completed
// if (name == null) {
// errors.put("name", "");
// }
// if (organizationalUnit == null) {
// errors.put("organizationalUnit", "");
// }
// if (organization == null) {
// errors.put("organization", "");
// }
// if (city == null) {
// errors.put("city", "");
// }
// if (state == null) {
// errors.put("state", "");
// }
// if (countryCode == null) {
// errors.put("countryCode", "");
// }
// if (errors.size() == 0) {
// try {
// final IdentityStore identityStoreConfig = (IdentityStore) SSLConfig.getInstance().getStoreConfig( connectionType );
//
// identityStoreConfig.ensureSelfSignedDomainCertificates( name, organizationalUnit, organization, city, state, countryCode, "rsa", "dsa" );
// // Regenerate self-sign certs whose subjectDN matches the issuerDN and set the new issuerDN
// String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
// StringBuilder issuerDN = new StringBuilder();
// issuerDN.append("CN=").append(name);
// issuerDN.append(", OU=").append(organizationalUnit);
// issuerDN.append(", O=").append(organization);
// issuerDN.append(", L=").append(city);
// issuerDN.append(", ST=").append(state);
// issuerDN.append(", C=").append(countryCode);
// StringBuilder subjectDN = new StringBuilder();
// subjectDN.append("CN=").append(domain);
// subjectDN.append(", OU=").append(organizationalUnit);
// subjectDN.append(", O=").append(organization);
// subjectDN.append(", L=").append(city);
// subjectDN.append(", ST=").append(state);
// subjectDN.append(", C=").append(countryCode);
// // Update certs with new issuerDN information
// for (Enumeration<String> certAliases = keyStore.aliases(); certAliases.hasMoreElements();) {
// String alias = certAliases.nextElement();
// X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
// // Update only Self-signed certs
// if (CertificateManager.isSelfSignedCertificate(keyStore, alias)) {
// if (CertificateManager.isDSACertificate(certificate)) {
// CertificateManager.createDSACert(keyStore, sslConfig.getKeyStorePassword(), alias,
// issuerDN.toString(), subjectDN.toString(), "*." + domain);
// } else {
// CertificateManager.createRSACert(keyStore, sslConfig.getKeyStorePassword(), alias,
// issuerDN.toString(), subjectDN.toString(), "*." + domain);
// }
// }
// }
// // Save keystore
// sslConfig.saveStores();
// // Log the event
// webManager.logEvent("generated SSL signing request", null);
// response.sendRedirect("security-keystore.jsp?connectivityType="+connectivityType);
// return;
// }
// catch (Exception e) {
// e.printStackTrace();
// errors.put("general", "");
// }
// }
// }
if (save) {
// Verify that fields were completed
if (organizationalUnit == null) {
errors.put("organizationalUnit", "");
}
if (organization == null) {
errors.put("organization", "");
}
if (city == null) {
errors.put("city", "");
}
if (state == null) {
errors.put("state", "");
}
if (countryCode == null) {
errors.put("countryCode", "");
}
if (errors.size() == 0) {
try {
X500NameBuilder builder = new X500NameBuilder();
builder.addRDN(BCStyle.CN, name);
builder.addRDN(BCStyle.OU, organizationalUnit);
builder.addRDN(BCStyle.O, organization);
builder.addRDN(BCStyle.L, city);
builder.addRDN(BCStyle.ST, state);
builder.addRDN(BCStyle.C, countryCode);
// Update certs with new issuerDN information
KeyStore keyStore = identityStore.getStore();
for (Enumeration<String> certAliases = keyStore.aliases(); certAliases.hasMoreElements();) {
String alias = certAliases.nextElement();
X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias);
// Update only Self-signed certs
if (CertificateManager.isSelfSignedCertificate(certificate)) {
PrivateKey privKey = (PrivateKey) keyStore.getKey(alias, identityStore.getConfiguration().getPassword());
PublicKey pubKey = certificate.getPublicKey();
String signAlgoritm = "SHA256WITH" + pubKey.getAlgorithm();
int days = 60;
// Regenerate self-sign certs whose subjectDN matches the issuerDN and set the new issuerDN
X509Certificate newCertificate = CertificateManager.createX509V3Certificate(new KeyPair(pubKey, privKey), days, builder, builder, domain, signAlgoritm);
keyStore.setKeyEntry(alias, privKey, identityStore.getConfiguration().getPassword(), new X509Certificate[] { newCertificate });
}
}
// Save keystore
identityStore.persist();
// Log the event
webManager.logEvent("generated SSL signing request", null);
response.sendRedirect("security-keystore.jsp?connectionType="+connectionType);
return;
}
catch (Exception e) {
e.printStackTrace();
errors.put("general", "");
}
}
}
%>
<html>
......@@ -110,7 +125,7 @@
<title>
<fmt:message key="ssl.signing-request.title"/>
</title>
<meta name="pageID" content="security-keystore-${connectivityType}"/>
<meta name="pageID" content="security-keystore-${connectionType}"/>
</head>
<body>
......@@ -118,9 +133,6 @@
<c:forEach var="err" items="${errors}">
<admin:infobox type="error">
<c:choose>
<c:when test="${err.key eq 'name'}">
<fmt:message key="ssl.signing-request.enter_name" />
</c:when>
<c:when test="${err.key eq 'organizationalUnit'}">
<fmt:message key="ssl.signing-request.enter_ou" />
</c:when>
......@@ -147,9 +159,9 @@
</c:forEach>
<!-- BEGIN 'Issuer information form' -->
<form action="ssl-signing-request.jsp" method="post">
<form action="security-keystore-signing-request.jsp" method="post">
<input type="hidden" name="save" value="true">
<input type="hidden" name="connectivityType" value="${connectivityType}">
<input type="hidden" name="connectionType" value="${connectionType}">
<div class="jive-contentBoxHeader">
<fmt:message key="ssl.signing-request.issuer_information"/>
</div>
......@@ -167,7 +179,7 @@
</td>
<td width="99%">
<input type="text" name="name" size="50" maxlength="75"
value="<%= ((name!=null) ? StringUtils.escapeForXML(name) : "") %>" id="namef">
value="<%= ((name!=null) ? StringUtils.escapeForXML(name) : "") %>" id="namef" disabled="disabled">
</td>
</tr>
<tr>
......
<%@page import="org.jivesoftware.util.StringUtils"%>
<%@page import="java.util.LinkedHashMap"%>
<%@page import="java.security.PrivateKey"%>
<%@page import="org.jivesoftware.util.CertificateManager"%>
<%@ page errorPage="error.jsp" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
......@@ -87,27 +91,20 @@
pageContext.setAttribute( "errors", errors );
/**
if (generate) {
String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
try {
if (errors.containsKey("ioerror") && keyStore == null) {
keyStore = sslConfig.initializeKeyStore();
}
if (errors.containsKey("ioerror") || !CertificateManager.isDSACertificate(keyStore, domain)) {
CertificateManager
.createDSACert(keyStore, sslConfig.getKeyStorePassword(), domain + "_dsa", "cn=" + domain, "cn=" + domain, "*." + domain);
if (errors.containsKey("ioerror") || !identityStore.containsDomainCertificate("DSA")) {
identityStore.addSelfSignedDomainCertificate("DSA");
}
if (errors.containsKey("ioerror") || !CertificateManager.isRSACertificate(keyStore, domain)) {
CertificateManager
.createRSACert(keyStore, sslConfig.getKeyStorePassword(), domain + "_rsa", "cn=" + domain, "cn=" + domain, "*." + domain);
if (errors.containsKey("ioerror") || !identityStore.containsDomainCertificate("RSA")) {
identityStore.addSelfSignedDomainCertificate("RSA");
}
// Save new certificates into the key store
sslConfig.saveStores();
identityStore.persist();
// Log the event
webManager.logEvent("generated SSL self-signed certs", null);
response.sendRedirect("security-keystore.jsp?connectivityType="+connectivityType);
response.sendRedirect("security-keystore.jsp?connectionType="+connectionType);
return;
} catch (Exception e) {
e.printStackTrace();
......@@ -119,12 +116,11 @@
String reply = ParamUtils.getParameter(request, "reply");
if (alias != null && reply != null && reply.trim().length() > 0) {
try {
CertificateManager.installReply( keyStore, s2sTrustStore,
sslConfig.getKeyStorePassword(), alias, new ByteArrayInputStream( reply.getBytes() ) );
sslConfig.saveStores();
identityStore.installCSRReply(alias, reply);
identityStore.persist();
// Log the event
webManager.logEvent( "imported SSL certificate with alias " + alias, null );
response.sendRedirect("security-keystore.jsp?connectivityType="+connectivityType);
response.sendRedirect("security-keystore.jsp?connectionType="+connectionType);
return;
} catch (Exception e) {
e.printStackTrace();
......@@ -132,10 +128,12 @@
}
}
}
*/
final boolean restartNeeded = ( (AdminConsolePlugin) XMPPServer.getInstance().getPluginManager().getPlugin( "admin" ) ).isRestartNeeded();
pageContext.setAttribute( "restartNeeded", restartNeeded );
boolean offerUpdateIssuer = false;
Map<String, String> signingRequests = new LinkedHashMap<String, String>();
%>
<html>
......@@ -240,38 +238,19 @@
<c:set var="alias" value="${certificateEntry.key}"/>
<c:set var="identities" value="${admin:serverIdentities(certificateEntry.value)}"/>
<%
// TODO restore this functionality
// int i = 0;
// boolean offerUpdateIssuer = false;
// Map<String, String> signingRequests = new LinkedHashMap<String, String>();
// if (keyStore != null && keyStore.aliases().hasMoreElements()) {
// for (Enumeration aliases = keyStore.aliases(); aliases.hasMoreElements(); ) {
// i++;
// String a = (String) aliases.nextElement();
// X509Certificate c = (X509Certificate) keyStore.getCertificate(a);
// StringBuffer identities = new StringBuffer();
// for (String identity : CertificateManager.getServerIdentities(c)) {
// identities.append(identity).append(", ");
// }
// if (identities.length() > 0) {
// identities.setLength(identities.length() - 2);
// }
// // Self-signed certs are certs generated by Openfire whose IssueDN equals SubjectDN
// boolean isSelfSigned = CertificateManager.isSelfSignedCertificate(keyStore, a);
// // Signing Request pending = not self signed certs whose chain has only 1 cert (the same cert)
// boolean isSigningPending = CertificateManager.isSigningRequestPending(keyStore, a);
//
// offerUpdateIssuer = offerUpdateIssuer || isSelfSigned || isSigningPending;
// if (isSigningPending) {
// // Generate new signing request for certificate
// PrivateKey privKey = (PrivateKey) keyStore.getKey(a, sslConfig.getKeyStorePassword().toCharArray());
// if (privKey != null) {
// signingRequests.put(a, CertificateManager.createSigningRequest(c, privKey));
// }
// }
// pageContext.setAttribute("identities", identities);
// pageContext.setAttribute("alias", a);
// pageContext.setAttribute("certificate", c);
String rowAlias = (String) pageContext.getAttribute("alias");
X509Certificate certificate = (X509Certificate) pageContext.getAttribute("certificate");
boolean isSelfSigned = CertificateManager.isSelfSignedCertificate(certificate);
boolean isSigningPending = CertificateManager.isSigningRequestPending(certificate);
offerUpdateIssuer = offerUpdateIssuer || isSelfSigned || isSigningPending;
if (isSigningPending) {
// Generate new signing request for certificate
signingRequests.put(rowAlias, identityStore.generateCSR(rowAlias));
}
%>
<tr valign="top">
<td>
......@@ -300,30 +279,28 @@
</c:otherwise>
</c:choose>
</td>
<%--<% if (isSelfSigned && !isSigningPending) { %>--%>
<%--<td width="1%"><img src="images/certificate_warning-16x16.png" width="16" height="16" border="0"--%>
<%--alt="<fmt:message key="ssl.certificates.keystore.self-signed.info"/>"--%>
<%--title="<fmt:message key="ssl.certificates.keystore.self-signed.info"/>"></td>--%>
<%--<td width="1%" nowrap>--%>
<%--<fmt:message key="ssl.certificates.self-signed"/>--%>
<%--</td>--%>
<%--<% } else if (isSigningPending) { %>--%>
<%--<td width="1%"><img src="images/certificate_warning-16x16.png" width="16" height="16" border="0"--%>
<%--alt="<fmt:message key="ssl.certificates.keystore.signing-pending.info"/>"--%>
<%--title="<fmt:message key="ssl.certificates.keystore.signing-pending.info"/>"></td>--%>
<%--<td width="1%" nowrap>--%>
<%--<fmt:message key="ssl.certificates.signing-pending"/>--%>
<%--</td>--%>
<%--<% } else { %>--%>
<%--<td width="1%"><img src="images/certificate_ok-16x16.png" width="16" height="16" border="0"--%>
<%--alt="<fmt:message key="ssl.certificates.keystore.ca-signed.info"/>"--%>
<%--title="<fmt:message key="ssl.certificates.keystore.ca-signed.info"/>"></td>--%>
<%--<td width="1%" nowrap>--%>
<%--<fmt:message key="ssl.certificates.ca-signed"/>--%>
<%--</td>--%>
<%--<% } %>--%>
<td width="1%" nowrap><%-- Restore functionality above --%></td>
<td width="1%" nowrap><%-- Restore functionality above --%></td>
<% if (isSelfSigned && !isSigningPending) { %>
<td width="1%"><img src="images/certificate_warning-16x16.png" width="16" height="16" border="0"
alt="<fmt:message key="ssl.certificates.keystore.self-signed.info"/>"
title="<fmt:message key="ssl.certificates.keystore.self-signed.info"/>"></td>
<td width="1%" nowrap>
<fmt:message key="ssl.certificates.self-signed"/>
</td>
<% } else if (isSigningPending) { %>
<td width="1%"><img src="images/certificate_warning-16x16.png" width="16" height="16" border="0"
alt="<fmt:message key="ssl.certificates.keystore.signing-pending.info"/>"
title="<fmt:message key="ssl.certificates.keystore.signing-pending.info"/>"></td>
<td width="1%" nowrap>
<fmt:message key="ssl.certificates.signing-pending"/>
</td>
<% } else { %>
<td width="1%"><img src="images/certificate_ok-16x16.png" width="16" height="16" border="0"
alt="<fmt:message key="ssl.certificates.keystore.ca-signed.info"/>"
title="<fmt:message key="ssl.certificates.keystore.ca-signed.info"/>"></td>
<td width="1%" nowrap>
<fmt:message key="ssl.certificates.ca-signed"/>
</td>
<% } %>
<td width="2%">
<c:out value="${certificate.publicKey.algorithm}"/>
</td>
......@@ -334,85 +311,82 @@
><img src="images/delete-16x16.gif" width="16" height="16" border="0" alt=""></a>
</td>
</tr>
<% if (isSigningPending) { %>
<form action="security-keystore.jsp?connectionType=${connectionType}" method="post">
<input type="hidden" name="importReply" value="true">
<input type="hidden" name="alias" value="${alias}">
<tr>
<td colspan="5">
<span class="jive-description">
<fmt:message key="ssl.certificates.truststore.ca-reply"/>
</span>
<textarea name="reply" rows="8" style="width:100%;font-size:8pt;" wrap="virtual"></textarea>
</td>
<td valign="bottom">
<input type="submit" name="install" value="<fmt:message key="global.save"/>">
</td>
</tr>
</form>
<% } %>
</c:forEach>
</c:otherwise>
</c:choose>
<%-- FIXME restore functionality below. --%>
<%--<% if (isSigningPending) { %>--%>
<%--<form action="security-keystore.jsp" method="post">--%>
<%--<input type="hidden" name="importReply" value="true">--%>
<%--<input type="hidden" name="alias" value="${alias}">--%>
<%--<input type="hidden" name="connectivityType" value="${connecticvityType}">--%>
<%--<tr id="pk<%=i%>">--%>
<%--<td colspan="5">--%>
<%--<span class="jive-description">--%>
<%--<fmt:message key="ssl.certificates.truststore.ca-reply"/>--%>
<%--</span>--%>
<%--<textarea name="reply" cols="40" rows="3" style="width:100%;font-size:8pt;" wrap="virtual"></textarea>--%>
<%--</td>--%>
<%--<td valign="bottom">--%>
<%--<input type="submit" name="install" value="<fmt:message key="global.save"/>">--%>
<%--</td>--%>
<%--</tr>--%>
<%--</form>--%>
</tbody>
</table>
<!-- END 'Installed Certificates' -->
<!-- BEGIN 'Signing request' -->
<%-- FIXME restore functionality below. --%>
<%--<% if (offerUpdateIssuer || !signingRequests.isEmpty()) { %>--%>
<%--<br>--%>
<%--<div class="jive-contentBoxHeader">--%>
<%--<fmt:message key="ssl.signing-request.title"/>--%>
<%--</div>--%>
<%--<div class="jive-contentBox">--%>
<%--<% if (offerUpdateIssuer) { %>--%>
<%--<p>--%>
<%--<fmt:message key="ssl.signing-request.offer-issuer-information">--%>
<%--<fmt:param value="<a href='ssl-signing-request.jsp?connectivityType=${connectivityType}'>"/>--%>
<%--<fmt:param value="</a>"/>--%>
<%--</fmt:message>--%>
<%--</p>--%>
<%--<% } %>--%>
<%--<% if (!signingRequests.isEmpty()) { %>--%>
<%--<p>--%>
<%--<fmt:message key="ssl.signing-request.requests_info"/>--%>
<%--</p>--%>
<%--<table cellpadding="3" cellspacing="2" border="0">--%>
<%--<thead>--%>
<%--<tr>--%>
<%--<th>--%>
<%--<fmt:message key="ssl.signing-request.alias"/>--%>
<%--</th>--%>
<%--<th>--%>
<%--<fmt:message key="ssl.signing-request.signing-request"/>--%>
<%--</th>--%>
<%--</tr>--%>
<%--</thead>--%>
<%--<tbody>--%>
<%--<% for (Map.Entry<String, String> entry : signingRequests.entrySet()) { %>--%>
<%--<tr>--%>
<%--<td valign="top">--%>
<%--<%= entry.getKey() %>--%>
<%--</td>--%>
<%--<td style="font-family: monospace;">--%>
<%--<%= StringUtils.escapeHTMLTags(entry.getValue()) %>--%>
<%--</td>--%>
<%--</tr>--%>
<%--<% } %>--%>
<%--</tbody>--%>
<%--</table>--%>
<%--<% } %>--%>
<%--</div>--%>
<%--<% } %>--%>
<%--<!-- END 'Signing request' -->--%>
<%--<form action="/security-certificate-store-management.jsp">--%>
<%--<input type="submit" name="done" value="<fmt:message key="global.done" />">--%>
<%--</form>--%>
<% if (offerUpdateIssuer || !signingRequests.isEmpty()) { %>
<br>
<div class="jive-contentBoxHeader">
<fmt:message key="ssl.signing-request.title"/>
</div>
<div class="jive-contentBox">
<% if (offerUpdateIssuer) { %>
<p>
<fmt:message key="ssl.signing-request.offer-issuer-information">
<fmt:param value="<a href='security-keystore-signing-request.jsp?connectionType=${connectionType}'>"/>
<fmt:param value="</a>"/>
</fmt:message>
</p>
<% } %>
<% if (!signingRequests.isEmpty()) { %>
<p>
<fmt:message key="ssl.signing-request.requests_info"/>
</p>
<table cellpadding="3" cellspacing="2" border="0">
<thead>
<tr>
<th>
<fmt:message key="ssl.signing-request.alias"/>
</th>
<th>
<fmt:message key="ssl.signing-request.signing-request"/>
</th>
</tr>
</thead>
<tbody>
<% for (Map.Entry<String, String> entry : signingRequests.entrySet()) { %>
<tr>
<td valign="top">
<%= entry.getKey() %>
</td>
<td style="font-family: monospace;">
<%= StringUtils.escapeHTMLTags(entry.getValue()) %>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } %>
</div>
<% } %>
<!-- END 'Signing request' -->
<form action="/security-certificate-store-management.jsp">
<input type="submit" name="done" value="<fmt:message key="global.done" />">
</form>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment