Commit 5a52d003 authored by Guus der Kinderen's avatar Guus der Kinderen

Restoring 'import private key & certificate' functionality.

parent 75494821
......@@ -3106,9 +3106,7 @@ mediaproxy.summary.stopbutton = Stop Active Sessions
# Import keystore certificate page
ssl.import.certificate.keystore.socket.title=Import Signed Certificate for Socket-based Communication
ssl.import.certificate.keystore.bosh.title=Import Signed Certificate for BOSH-based Communication
ssl.import.certificate.keystore.administrative.title=Import Signed Certificate for Administrative Purposes
ssl.import.certificate.keystore.title=Import Signed Certificate for Socket-based Communication
ssl.import.certificate.keystore.info=Use the form below to import a private key and certificate that was provided by a \
Certificate Authority. Make sure that root certificates of the CA signing the certificate are present in the \
truststore. Otherwise you will need to manually import them using the "keytool" command line tool. If you are \
......
......@@ -211,8 +211,46 @@ public class IdentityStore extends CertificateStore
/**
* Imports a certificate and the private key that was used to generate the certificate.
*
* This method will import the certificate and key in the store using a unique alias. This alias is returned.
*
* This method will fail when the provided certificate does not match the domain of this XMPP service.
*
* @param pemCertificates a PEM representation of the certificate or certificate chain (cannot be null or empty).
* @param pemPrivateKey a PEM representation of the private key (cannot be null or empty).
* @param passPhrase optional pass phrase (must be present if the private key is encrypted).
* @return The alias that was used (never null).
*/
public String installCertificate( String pemCertificates, String pemPrivateKey, String passPhrase ) throws CertificateStoreConfigException
{
// Generate a unique alias.
final String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
int index = 1;
String alias = domain + "_" + index;
try
{
while ( store.containsAlias( alias ) )
{
index = index + 1;
alias = domain + "_" + index;
}
}
catch ( KeyStoreException e )
{
throw new CertificateStoreConfigException( "Unable to install a certificate into an identity store.", e );
}
// Perform the installation using the generated alias.
installCertificate( alias, pemCertificates, pemPrivateKey, passPhrase );
return alias;
}
/**
* Imports a certificate and the private key that was used to generate the certificate.
*
* This method will fail when the provided certificate does not match the domain of this XMPP service, or when the
* provided alias refers to an existing entry.
*
* @param alias the name (key) under which the certificate is to be stored in the store (cannot be null or empty).
* @param pemCertificates a PEM representation of the certificate or certificate chain (cannot be null or empty).
* @param pemPrivateKey a PEM representation of the private key (cannot be null or empty).
......
......@@ -49,6 +49,7 @@ import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.X509Extensions;
......@@ -59,7 +60,12 @@ import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.jivesoftware.openfire.keystore.CertificateStore;
import org.jivesoftware.openfire.keystore.CertificateStoreConfigException;
......@@ -659,6 +665,9 @@ public class CertificateManager {
if ( pemRepresentation == null || pemRepresentation.trim().isEmpty() ) {
throw new IllegalArgumentException( "Argument 'pemRepresentation' cannot be null or an empty String.");
}
if ( passPhrase == null ) {
passPhrase = "";
}
try ( Reader reader = new StringReader( pemRepresentation.trim() ))
{
final Object object = new PEMParser( reader ).readObject();
......@@ -672,6 +681,25 @@ public class CertificateManager {
final PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build( passPhrase.toCharArray() );
kp = converter.getKeyPair( ( (PEMEncryptedKeyPair) object ).decryptKeyPair( decProv ) );
}
else if ( object instanceof PKCS8EncryptedPrivateKeyInfo )
{
// Encrypted key - we will use provided password
try
{
final PKCS8EncryptedPrivateKeyInfo encryptedInfo = (PKCS8EncryptedPrivateKeyInfo) object;
final InputDecryptorProvider provider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build( passPhrase.toCharArray() );
final PrivateKeyInfo privateKeyInfo = encryptedInfo.decryptPrivateKeyInfo( provider );
return converter.getPrivateKey( privateKeyInfo );
}
catch ( PKCSException | OperatorCreationException e )
{
throw new IOException( "Unable to decrypt private key.", e );
}
}
else if ( object instanceof PrivateKeyInfo )
{
return converter.getPrivateKey( (PrivateKeyInfo) object );
}
else
{
// Unencrypted key - no password needed
......
This diff is collapsed.
......@@ -279,6 +279,7 @@
<c:forEach items="${identities}" var="currentItem" varStatus="stat">
<c:out value="${stat.first ? '' : ','} ${currentItem}"/>
</c:forEach>
(<c:out value="${alias}"/>)
</a>
</td>
<td>
......
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