/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.util.jmx.jmxmp;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
import java.util.Properties;
import javax.management.remote.JMXConnectorServer;
import org.apache.brooklyn.util.core.crypto.FluentKeySigner;
import org.apache.brooklyn.util.core.crypto.SecureKeys;
import org.apache.brooklyn.util.jmx.jmxmp.JmxmpAgent;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class JmxmpAgentSslTest {
KeyPair caRootKey;
FluentKeySigner caRootSigner;
X509Certificate caRootCert;
KeyPair caChildKey;
X509Certificate caChildCert;
FluentKeySigner caChildSigner;
KeyPair grandchildKey;
X509Certificate grandchildCert;
KeyPair child2Key;
X509Certificate child2Cert;
KeyPair selfSign1Key;
X509Certificate selfSign1Cert;
KeyPair selfSign2Key;
X509Certificate selfSign2Cert;
KeyStore serverKeystore;
KeyStore serverTruststore;
KeyStore clientTruststore;
KeyStore clientKeystore;
JMXConnectorServer server;
static { Security.addProvider(new BouncyCastleProvider()); }
@BeforeMethod
public void setup() throws Exception {
caRootSigner = new FluentKeySigner("ca-root").selfsign();
caRootKey = caRootSigner.getKey();
caRootCert = caRootSigner.getAuthorityCertificate();
caChildKey = SecureKeys.newKeyPair();
caChildCert = caRootSigner.newCertificateFor("ca-child", caChildKey);
caChildSigner = new FluentKeySigner("ca-child", caChildKey).
authorityKeyIdentifier(new AuthorityKeyIdentifierStructure(caChildCert));
grandchildKey = SecureKeys.newKeyPair();
grandchildCert = caChildSigner.newCertificateFor("grandchild", grandchildKey);
child2Key = SecureKeys.newKeyPair();
child2Cert =
caRootSigner.
newCertificateFor("child-2", child2Key);
selfSign1Key = SecureKeys.newKeyPair();
selfSign1Cert =
new FluentKeySigner("self-1", selfSign1Key).
newCertificateFor("self-1", selfSign1Key);
selfSign2Key = SecureKeys.newKeyPair();
selfSign2Cert =
new FluentKeySigner("self-2", selfSign2Key).
newCertificateFor("self-2", selfSign2Key);
serverKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
serverKeystore.load(null, null);
serverTruststore = KeyStore.getInstance(KeyStore.getDefaultType());
serverTruststore.load(null, null);
clientTruststore = KeyStore.getInstance(KeyStore.getDefaultType());
clientTruststore.load(null, null);
clientKeystore = KeyStore.getInstance(KeyStore.getDefaultType());
clientKeystore.load(null, null);
}
@AfterMethod
public void teardown() throws Exception {
if (server!=null) server.stop();
server = null;
}
private Properties saveStoresAndGetConnectorProperties() throws
KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException, FileNotFoundException {
String keystoreFile = File.createTempFile("server-keystore", ".jmx.test").getAbsolutePath();
String truststoreFile = File.createTempFile("server-truststore", ".jmx.test").getAbsolutePath();
if (serverKeystore!=null) serverKeystore.store( new FileOutputStream(keystoreFile), new char[0]);
if (serverTruststore!=null) serverTruststore.store( new FileOutputStream(truststoreFile), new char[0]);
Properties p = new Properties();
p.put(JmxmpAgent.JMXMP_KEYSTORE_FILE_PROPERTY, keystoreFile);
p.put(JmxmpAgent.JMXMP_TRUSTSTORE_FILE_PROPERTY, truststoreFile);
p.put(JmxmpAgent.USE_SSL_PROPERTY, "true");
p.put(JmxmpAgent.AUTHENTICATE_CLIENTS_PROPERTY, "true");
return p;
}
@SuppressWarnings("rawtypes")
@Test
public void testNoAuth() throws Exception {
serverKeystore = null;
serverTruststore = null;
clientKeystore = null;
clientTruststore = null;
Properties p = saveStoresAndGetConnectorProperties();
p.put(JmxmpAgent.USE_SSL_PROPERTY, "false");
p.put(JmxmpAgent.AUTHENTICATE_CLIENTS_PROPERTY, "false");
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connect("service:jmx:jmxmp://localhost:11099", new LinkedHashMap());
}
@SuppressWarnings("rawtypes")
@Test(expectedExceptions = { IllegalStateException.class })
public void testAuthWithoutSslFails() throws Exception {
serverKeystore = null;
serverTruststore = null;
clientKeystore = null;
clientTruststore = null;
Properties p = saveStoresAndGetConnectorProperties();
p.put(JmxmpAgent.USE_SSL_PROPERTY, "false");
p.put(JmxmpAgent.AUTHENTICATE_CLIENTS_PROPERTY, "true");
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connect("service:jmx:jmxmp://localhost:11099", new LinkedHashMap());
}
@Test
public void testAllGoodSignatures() throws Exception {
serverKeystore.setKeyEntry("child-2", child2Key.getPrivate(), new char[]{},
new java.security.cert.Certificate[]{ child2Cert, caRootCert });
serverTruststore.setCertificateEntry("ca-child", caChildCert);
clientKeystore.setKeyEntry("grandchild", grandchildKey.getPrivate(), new char[] {},
new java.security.cert.Certificate[]{ grandchildCert });
clientTruststore.setCertificateEntry("ca-root", caRootCert);
Properties p = saveStoresAndGetConnectorProperties();
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connectTls("service:jmx:jmxmp://localhost:11099",
clientKeystore, "", clientTruststore);
}
@Test(expectedExceptions = { Exception.class })
public void testWrongServerKey() throws Exception {
/** not a trusted key */
serverKeystore.setKeyEntry("self-1", selfSign1Key.getPrivate(), new char[]{},
new java.security.cert.Certificate[]{ selfSign1Cert });
serverTruststore.setCertificateEntry("ca-child", caChildCert);
clientKeystore.setKeyEntry("grandchild", grandchildKey.getPrivate(), new char[] {},
new java.security.cert.Certificate[]{ grandchildCert });
clientTruststore.setCertificateEntry("ca-root", caRootCert);
Properties p = saveStoresAndGetConnectorProperties();
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connectTls("service:jmx:jmxmp://localhost:11099",
clientKeystore, "", clientTruststore);
}
@Test(expectedExceptions = { Exception.class })
public void testLyingServerChain() throws Exception {
/** caChildCert hasn't signed this */
serverKeystore.setKeyEntry("self-1", selfSign1Key.getPrivate(), new char[]{},
new java.security.cert.Certificate[]{ selfSign1Cert, caChildCert });
serverTruststore.setCertificateEntry("ca-child", caChildCert);
clientKeystore.setKeyEntry("grandchild", grandchildKey.getPrivate(), new char[] {},
new java.security.cert.Certificate[]{ grandchildCert, caChildCert });
clientTruststore.setCertificateEntry("ca-root", caRootCert);
Properties p = saveStoresAndGetConnectorProperties();
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connectTls("service:jmx:jmxmp://localhost:11099",
clientKeystore, "", clientTruststore);
}
@Test(expectedExceptions = { Exception.class })
public void testWrongClientKey() throws Exception {
serverKeystore.setKeyEntry("child-2", child2Key.getPrivate(), new char[]{},
new java.security.cert.Certificate[]{ child2Cert, caRootCert });
serverTruststore.setCertificateEntry("ca-child", caChildCert);
/** this key should not have access */
clientKeystore.setKeyEntry("self-1", selfSign1Key.getPrivate(), new char[] {},
new java.security.cert.Certificate[]{ selfSign1Cert });
clientTruststore.setCertificateEntry("ca-root", caRootCert);
Properties p = saveStoresAndGetConnectorProperties();
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connectTls("service:jmx:jmxmp://localhost:11099",
clientKeystore, "", clientTruststore);
}
@Test(expectedExceptions = { Exception.class })
public void testLyingClientChain() throws Exception {
serverKeystore.setKeyEntry("child-2", child2Key.getPrivate(), new char[]{},
new java.security.cert.Certificate[]{ child2Cert, caRootCert });
serverTruststore.setCertificateEntry("ca-child", caChildCert);
/** caChildCert hasn't signed this */
clientKeystore.setKeyEntry("self-1", selfSign1Key.getPrivate(), new char[] {},
new java.security.cert.Certificate[]{ selfSign1Cert, caChildCert });
clientTruststore.setCertificateEntry("ca-root", caRootCert);
Properties p = saveStoresAndGetConnectorProperties();
server = new JmxmpAgent().startJmxmpConnector(p);
new JmxmpClient().connectTls("service:jmx:jmxmp://localhost:11099",
clientKeystore, "", clientTruststore);
}
}