NIFI-2193 - Added functionality to automate certificate generation, keystore and truststore generation, and nifi.properties keystore and truststore password population. Follow-on changes will be made under NIFI-2476.

This closes #695.

Signed-off-by: Andy LoPresto <alopresto@apache.org>

Defaulting to same keyStore, key password (+18 squashed commits)
Squashed commits:
[9d01ba0] NIFI-2193 - Fixing typo
[55440bc] NIFI-2193 - Standalone can run as long as there are no conflicting files/folders
[0ca34ed] NIFI-2193 - Fixing some filename, absolute path issues
[9d4f65b] NIFI-2193 - Incorporating feedback
[f7550b4] NIFI-2193 - Cleaning up imports
[59a7637] NIFI-2193 - Updating umask to allow owner to execute
[cf824e7] NIFI-2193 - Moving DN arg to CA service specific parent class
[921ee13] NIFI-2193 - Making keystore getInstance more consistent
[a283c4b] NIFI-2193 - Updating sample config files in assembly to reflect new structure
[8d3a21d] NIFI-2193 - Making TlsHelper static, adding option to use same password for Key, KeyStore
[b13d247] NIFI-2193 - Addressing PR feedback
[46ef8ed] NIFI-2193 - Removing commons-logging, log4j from notice
[d4cf41a] NIFI-2193 - Adding option to specify output file for CA certificate when using cli client
[b74bf25] NIFI-2193 - Removing Bouncy Castle from notice
[6e34f9a] NIFI-2193 - Adding CLI client for easier generation of client certificates
[2924fca] NIFI-2193 - nifi-toolkit-ssl -> nifi-toolkit-tls, removing unused constants
[886167e] NIFI-2193 - Adding slf4j to avoid runtime issue
[082de46] NIFI-2193 - Command line SSL config utility as well as certificate authority client/server
This commit is contained in:
Bryan Rosander 2016-07-06 16:56:08 -04:00 committed by Andy LoPresto
parent f0401e4774
commit fa4c6ab03c
No known key found for this signature in database
GPG Key ID: 3C6EF65B2F7DEF69
62 changed files with 6000 additions and 251 deletions

View File

@ -368,147 +368,6 @@ language governing permissions and limitations under the License. -->
</dependency>
</dependencies>
<properties>
<!--Wrapper Properties -->
<nifi.jvm.heap.mb>512</nifi.jvm.heap.mb>
<nifi.jvm.permgen.mb>128</nifi.jvm.permgen.mb>
<nifi.run.as />
<!-- nifi.properties: core properties -->
<nifi.version>${project.version}</nifi.version>
<nifi.flowcontroller.autoResumeState>true</nifi.flowcontroller.autoResumeState>
<nifi.flowcontroller.graceful.shutdown.period>10 sec</nifi.flowcontroller.graceful.shutdown.period>
<nifi.flowservice.writedelay.interval>500 ms</nifi.flowservice.writedelay.interval>
<nifi.administrative.yield.duration>30 sec</nifi.administrative.yield.duration>
<nifi.bored.yield.duration>10 millis</nifi.bored.yield.duration>
<nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
<nifi.flow.configuration.archive.enabled>true</nifi.flow.configuration.archive.enabled>
<nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
<nifi.flow.configuration.archive.max.time>30 days</nifi.flow.configuration.archive.max.time>
<nifi.flow.configuration.archive.max.storage>500 MB</nifi.flow.configuration.archive.max.storage>
<nifi.login.identity.provider.configuration.file>./conf/login-identity-providers.xml</nifi.login.identity.provider.configuration.file>
<nifi.authorizer.configuration.file>./conf/authorizers.xml</nifi.authorizer.configuration.file>
<nifi.templates.directory>./conf/templates</nifi.templates.directory>
<nifi.database.directory>./database_repository</nifi.database.directory>
<nifi.state.management.configuration.file>./conf/state-management.xml</nifi.state.management.configuration.file>
<nifi.state.management.embedded.zookeeper.start>false</nifi.state.management.embedded.zookeeper.start>
<nifi.state.management.embedded.zookeeper.properties>./conf/zookeeper.properties</nifi.state.management.embedded.zookeeper.properties>
<nifi.state.management.provider.local>local-provider</nifi.state.management.provider.local>
<nifi.state.management.provider.cluster>zk-provider</nifi.state.management.provider.cluster>
<nifi.flowfile.repository.implementation>org.apache.nifi.controller.repository.WriteAheadFlowFileRepository</nifi.flowfile.repository.implementation>
<nifi.flowfile.repository.directory>./flowfile_repository</nifi.flowfile.repository.directory>
<nifi.flowfile.repository.partitions>256</nifi.flowfile.repository.partitions>
<nifi.flowfile.repository.checkpoint.interval>2 mins</nifi.flowfile.repository.checkpoint.interval>
<nifi.flowfile.repository.always.sync>false</nifi.flowfile.repository.always.sync>
<nifi.swap.manager.implementation>org.apache.nifi.controller.FileSystemSwapManager</nifi.swap.manager.implementation>
<nifi.queue.swap.threshold>20000</nifi.queue.swap.threshold>
<nifi.swap.in.period>5 sec</nifi.swap.in.period>
<nifi.swap.in.threads>1</nifi.swap.in.threads>
<nifi.swap.out.period>5 sec</nifi.swap.out.period>
<nifi.swap.out.threads>4</nifi.swap.out.threads>
<nifi.content.repository.implementation>org.apache.nifi.controller.repository.FileSystemRepository</nifi.content.repository.implementation>
<nifi.content.claim.max.appendable.size>10 MB</nifi.content.claim.max.appendable.size>
<nifi.content.claim.max.flow.files>100</nifi.content.claim.max.flow.files>
<nifi.content.repository.directory.default>./content_repository</nifi.content.repository.directory.default>
<nifi.content.repository.archive.max.retention.period>12 hours</nifi.content.repository.archive.max.retention.period>
<nifi.content.repository.archive.max.usage.percentage>50%</nifi.content.repository.archive.max.usage.percentage>
<nifi.content.repository.archive.enabled>true</nifi.content.repository.archive.enabled>
<nifi.content.repository.always.sync>false</nifi.content.repository.always.sync>
<nifi.content.viewer.url>/nifi-content-viewer/</nifi.content.viewer.url>
<nifi.restore.directory />
<nifi.ui.banner.text />
<nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval>
<nifi.nar.library.directory>./lib</nifi.nar.library.directory>
<nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory>
<nifi.documentation.working.directory>./work/docs/components</nifi.documentation.working.directory>
<nifi.sensitive.props.algorithm>PBEWITHMD5AND256BITAES-CBC-OPENSSL</nifi.sensitive.props.algorithm>
<nifi.sensitive.props.provider>BC</nifi.sensitive.props.provider>
<nifi.h2.url.append>;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.h2.url.append>
<nifi.remote.input.socket.port>9990</nifi.remote.input.socket.port>
<!-- persistent provenance repository properties -->
<nifi.provenance.repository.implementation>org.apache.nifi.provenance.PersistentProvenanceRepository</nifi.provenance.repository.implementation>
<nifi.provenance.repository.directory.default>./provenance_repository</nifi.provenance.repository.directory.default>
<nifi.provenance.repository.max.storage.time>24 hours</nifi.provenance.repository.max.storage.time>
<nifi.provenance.repository.max.storage.size>1 GB</nifi.provenance.repository.max.storage.size>
<nifi.provenance.repository.rollover.time>30 secs</nifi.provenance.repository.rollover.time>
<nifi.provenance.repository.rollover.size>100 MB</nifi.provenance.repository.rollover.size>
<nifi.provenance.repository.query.threads>2</nifi.provenance.repository.query.threads>
<nifi.provenance.repository.index.threads>1</nifi.provenance.repository.index.threads>
<nifi.provenance.repository.compress.on.rollover>true</nifi.provenance.repository.compress.on.rollover>
<nifi.provenance.repository.indexed.fields>EventType, FlowFileUUID, Filename, ProcessorID, Relationship</nifi.provenance.repository.indexed.fields>
<nifi.provenance.repository.indexed.attributes />
<nifi.provenance.repository.index.shard.size>500 MB</nifi.provenance.repository.index.shard.size>
<nifi.provenance.repository.always.sync>false</nifi.provenance.repository.always.sync>
<nifi.provenance.repository.journal.count>16</nifi.provenance.repository.journal.count>
<nifi.provenance.repository.max.attribute.length>65536</nifi.provenance.repository.max.attribute.length>
<!-- volatile provenance repository properties -->
<nifi.provenance.repository.buffer.size>100000</nifi.provenance.repository.buffer.size>
<!-- Component status repository properties -->
<nifi.components.status.repository.implementation>org.apache.nifi.controller.status.history.VolatileComponentStatusRepository</nifi.components.status.repository.implementation>
<nifi.components.status.repository.buffer.size>1440</nifi.components.status.repository.buffer.size>
<nifi.components.status.snapshot.frequency>1 min</nifi.components.status.snapshot.frequency>
<!-- nifi.properties: web properties -->
<nifi.web.war.directory>./lib</nifi.web.war.directory>
<nifi.web.http.host />
<nifi.web.http.port>8080</nifi.web.http.port>
<nifi.web.https.host />
<nifi.web.https.port />
<nifi.jetty.work.dir>./work/jetty</nifi.jetty.work.dir>
<nifi.web.jetty.threads>200</nifi.web.jetty.threads>
<!-- nifi.properties: security properties -->
<nifi.security.keystore />
<nifi.security.keystoreType />
<nifi.security.keystorePasswd />
<nifi.security.keyPasswd />
<nifi.security.truststore />
<nifi.security.truststoreType />
<nifi.security.truststorePasswd />
<nifi.security.needClientAuth />
<nifi.security.user.authorizer>file-provider</nifi.security.user.authorizer>
<nifi.security.user.login.identity.provider />
<nifi.security.x509.principal.extractor />
<nifi.security.ocsp.responder.url />
<nifi.security.ocsp.responder.certificate />
<!-- nifi.properties: cluster common properties (cluster manager and nodes must have same values) -->
<nifi.cluster.protocol.heartbeat.interval>5 sec</nifi.cluster.protocol.heartbeat.interval>
<nifi.cluster.protocol.is.secure>false</nifi.cluster.protocol.is.secure>
<!-- nifi.properties: cluster node properties (only configure for cluster nodes) -->
<nifi.cluster.is.node>false</nifi.cluster.is.node>
<nifi.cluster.node.address />
<nifi.cluster.node.protocol.port />
<nifi.cluster.node.protocol.threads>10</nifi.cluster.node.protocol.threads>
<nifi.cluster.node.event.history.size>25</nifi.cluster.node.event.history.size>
<nifi.cluster.node.connection.timeout>5 sec</nifi.cluster.node.connection.timeout>
<nifi.cluster.node.read.timeout>5 sec</nifi.cluster.node.read.timeout>
<nifi.cluster.firewall.file />
<nifi.cluster.request.replication.claim.timeout>15 secs</nifi.cluster.request.replication.claim.timeout>
<!-- nifi.properties: zookeeper properties -->
<nifi.zookeeper.connect.string></nifi.zookeeper.connect.string>
<nifi.zookeeper.connect.timeout>3 secs</nifi.zookeeper.connect.timeout>
<nifi.zookeeper.session.timeout>3 secs</nifi.zookeeper.session.timeout>
<nifi.zookeeper.root.node>/nifi</nifi.zookeeper.root.node>
<!-- nifi.properties: kerberos properties -->
<nifi.kerberos.krb5.file> </nifi.kerberos.krb5.file>
<nifi.kerberos.service.principal />
<nifi.kerberos.keytab.location />
<nifi.kerberos.authentication.expiration>12 hours</nifi.kerberos.authentication.expiration>
</properties>
<profiles>
<profile>
<id>rpm</id>

View File

@ -33,12 +33,10 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -16,12 +16,41 @@
*/
package org.apache.nifi.security.util;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.Socket;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@ -29,15 +58,9 @@ import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
public final class CertificateUtils {
@ -326,6 +349,100 @@ public final class CertificateUtils {
}
}
/**
* Generates a self-signed {@link X509Certificate} suitable for use as a Certificate Authority.
*
* @param keyPair the {@link KeyPair} to generate the {@link X509Certificate} for
* @param dn the distinguished name to user for the {@link X509Certificate}
* @param signingAlgorithm the signing algorithm to use for the {@link X509Certificate}
* @param certificateDurationDays the duration in days for which the {@link X509Certificate} should be valid
* @return a self-signed {@link X509Certificate} suitable for use as a Certificate Authority
* @throws CertificateException if there is an generating the new certificate
*/
public static X509Certificate generateSelfSignedX509Certificate(KeyPair keyPair, String dn, String signingAlgorithm, int certificateDurationDays)
throws CertificateException {
try {
ContentSigner sigGen = new JcaContentSignerBuilder(signingAlgorithm).setProvider(BouncyCastleProvider.PROVIDER_NAME).build(keyPair.getPrivate());
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + TimeUnit.DAYS.toMillis(certificateDurationDays));
X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
new X500Name(dn),
BigInteger.valueOf(System.currentTimeMillis()),
startDate, endDate,
new X500Name(dn),
subPubKeyInfo);
// Set certificate extensions
// (1) digitalSignature extension
certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment
| KeyUsage.keyAgreement | KeyUsage.nonRepudiation | KeyUsage.cRLSign | KeyUsage.keyCertSign));
certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, new JcaX509ExtensionUtils().createSubjectKeyIdentifier(keyPair.getPublic()));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(keyPair.getPublic()));
// (2) extendedKeyUsage extension
certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth}));
// Sign the certificate
X509CertificateHolder certificateHolder = certBuilder.build(sigGen);
return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certificateHolder);
} catch (CertIOException | NoSuchAlgorithmException | OperatorCreationException e) {
throw new CertificateException(e);
}
}
/**
* Generates an issued {@link X509Certificate} from the given issuer certificate and {@link KeyPair}
*
* @param dn the distinguished name to use
* @param publicKey the public key to issue the certificate to
* @param issuer the issuer's certificate
* @param issuerKeyPair the issuer's keypair
* @param signingAlgorithm the signing algorithm to use
* @param days the number of days it should be valid for
* @return an issued {@link X509Certificate} from the given issuer certificate and {@link KeyPair}
* @throws CertificateException if there is an error issueing the certificate
*/
public static X509Certificate generateIssuedCertificate(String dn, PublicKey publicKey, X509Certificate issuer, KeyPair issuerKeyPair, String signingAlgorithm, int days)
throws CertificateException {
try {
ContentSigner sigGen = new JcaContentSignerBuilder(signingAlgorithm).setProvider(BouncyCastleProvider.PROVIDER_NAME).build(issuerKeyPair.getPrivate());
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
Date startDate = new Date();
Date endDate = new Date(startDate.getTime() + TimeUnit.DAYS.toMillis(days));
X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
new X500Name(issuer.getSubjectDN().getName()),
BigInteger.valueOf(System.currentTimeMillis()),
startDate, endDate,
new X500Name(dn),
subPubKeyInfo);
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, new JcaX509ExtensionUtils().createSubjectKeyIdentifier(publicKey));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(issuerKeyPair.getPublic()));
// Set certificate extensions
// (1) digitalSignature extension
certBuilder.addExtension(Extension.keyUsage, true,
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.keyAgreement | KeyUsage.nonRepudiation));
certBuilder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
// (2) extendedKeyUsage extension
certBuilder.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth}));
X509CertificateHolder certificateHolder = certBuilder.build(sigGen);
return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certificateHolder);
} catch (CertIOException | NoSuchAlgorithmException | OperatorCreationException e) {
throw new CertificateException(e);
}
}
/**
* Returns true if the two provided DNs are equivalent, regardless of the order of the elements. Returns false if one or both are invalid DNs.
*

View File

@ -16,19 +16,8 @@
*/
package org.apache.nifi.security.util
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.ExtendedKeyUsage
import org.bouncycastle.asn1.x509.KeyPurposeId
import org.bouncycastle.asn1.x509.KeyUsage
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.asn1.x509.X509Extension
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.X509v3CertificateBuilder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.operator.ContentSigner
import org.bouncycastle.operator.OperatorCreationException
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import org.junit.After
import org.junit.Before
import org.junit.BeforeClass
@ -46,13 +35,14 @@ import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.NoSuchAlgorithmException
import java.security.NoSuchProviderException
import java.security.PrivateKey
import java.security.PublicKey
import java.security.Security
import java.security.SignatureException
import java.security.cert.Certificate
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import static org.junit.Assert.assertEquals
@RunWith(JUnit4.class)
class CertificateUtilsTest extends GroovyTestCase {
@ -116,53 +106,7 @@ class CertificateUtilsTest extends GroovyTestCase {
private
static X509Certificate generateCertificate(String dn) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, InvalidKeyException, OperatorCreationException {
KeyPair keyPair = generateKeyPair();
return generateCertificate(dn, keyPair);
}
/**
* Generates a signed certificate with a specific keypair.
*
* @param dn the DN
* @param keyPair the public key will be included in the certificate and the the private key is used to sign the certificate
* @return the certificate
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws InvalidKeyException
* @throws OperatorCreationException
*/
private
static X509Certificate generateCertificate(String dn, KeyPair keyPair) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, InvalidKeyException, OperatorCreationException {
PrivateKey privateKey = keyPair.getPrivate();
ContentSigner sigGen = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(PROVIDER).build(privateKey);
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded());
Date startDate = new Date(YESTERDAY);
Date endDate = new Date(ONE_YEAR_FROM_NOW);
X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(
new X500Name(dn),
BigInteger.valueOf(System.currentTimeMillis()),
startDate, endDate,
new X500Name(dn),
subPubKeyInfo);
// Set certificate extensions
// (1) digitalSignature extension
certBuilder.addExtension(X509Extension.keyUsage, true,
new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.keyAgreement));
// (2) extendedKeyUsage extension
Vector<KeyPurposeId> ekUsages = new Vector<>();
ekUsages.add(KeyPurposeId.id_kp_clientAuth);
ekUsages.add(KeyPurposeId.id_kp_serverAuth);
certBuilder.addExtension(X509Extension.extendedKeyUsage, false, new ExtendedKeyUsage(ekUsages));
// Sign the certificate
X509CertificateHolder certificateHolder = certBuilder.build(sigGen);
return new JcaX509CertificateConverter().setProvider(PROVIDER)
.getCertificate(certificateHolder);
return CertificateUtils.generateSelfSignedX509Certificate(keyPair, dn, SIGNATURE_ALGORITHM, 365);
}
/**
@ -181,52 +125,16 @@ class CertificateUtilsTest extends GroovyTestCase {
* @throws OperatorCreationException
*/
private
static X509Certificate generateIssuedCertificate(String dn, String issuerDn, PrivateKey issuerKey) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, InvalidKeyException, OperatorCreationException {
static X509Certificate generateIssuedCertificate(String dn, X509Certificate issuer, KeyPair issuerKey) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, InvalidKeyException, OperatorCreationException {
KeyPair keyPair = generateKeyPair();
return generateIssuedCertificate(dn, keyPair.getPublic(), issuerDn, issuerKey);
}
/**
* Generates a certificate with a specific public key signed by the issuer key.
*
* @param dn the subject DN
* @param publicKey the subject public key
* @param issuerDn the issuer DN
* @param issuerKey the issuer private key
* @return the certificate
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws InvalidKeyException
* @throws OperatorCreationException
*/
private
static X509Certificate generateIssuedCertificate(String dn, PublicKey publicKey, String issuerDn, PrivateKey issuerKey) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, SignatureException, InvalidKeyException, OperatorCreationException {
ContentSigner sigGen = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(PROVIDER).build(issuerKey);
SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
Date startDate = new Date(YESTERDAY);
Date endDate = new Date(ONE_YEAR_FROM_NOW);
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
new X500Name(issuerDn),
BigInteger.valueOf(System.currentTimeMillis()),
startDate, endDate,
new X500Name(dn),
subPubKeyInfo);
X509CertificateHolder certificateHolder = v3CertGen.build(sigGen);
return new JcaX509CertificateConverter().setProvider(PROVIDER)
.getCertificate(certificateHolder);
return CertificateUtils.generateIssuedCertificate(dn, keyPair.getPublic(), issuer, issuerKey, SIGNATURE_ALGORITHM, 365);
}
private static X509Certificate[] generateCertificateChain(String dn = SUBJECT_DN, String issuerDn = ISSUER_DN) {
final KeyPair issuerKeyPair = generateKeyPair();
final PrivateKey issuerPrivateKey = issuerKeyPair.getPrivate();
final X509Certificate issuerCertificate = generateCertificate(issuerDn, issuerKeyPair);
final X509Certificate certificate = generateIssuedCertificate(dn, issuerDn, issuerPrivateKey);
final X509Certificate issuerCertificate = CertificateUtils.generateSelfSignedX509Certificate(issuerKeyPair, issuerDn, SIGNATURE_ALGORITHM, 365);
final X509Certificate certificate = generateIssuedCertificate(dn, issuerCertificate, issuerKeyPair);
[certificate, issuerCertificate] as X509Certificate[]
}
@ -238,6 +146,10 @@ class CertificateUtilsTest extends GroovyTestCase {
return x509Certificate as Certificate
}
private static Date inFuture(int days) {
return new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(days));
}
@Test
void testShouldConvertLegacyX509Certificate() {
// Arrange
@ -513,4 +425,58 @@ class CertificateUtilsTest extends GroovyTestCase {
assert !dn1MatchesDn2Reversed
assert !dn1MatchesEmpty
}
@Test
public void testShouldGenerateSelfSignedCert() throws Exception {
String dn = "CN=testDN,O=testOrg";
int days = 365;
X509Certificate x509Certificate = CertificateUtils.generateSelfSignedX509Certificate(generateKeyPair(), dn, SIGNATURE_ALGORITHM, days);
Date notAfter = x509Certificate.getNotAfter();
assertTrue(notAfter.after(inFuture(days - 1)));
assertTrue(notAfter.before(inFuture(days + 1)));
Date notBefore = x509Certificate.getNotBefore();
assertTrue(notBefore.after(inFuture(-1)));
assertTrue(notBefore.before(inFuture(1)));
assertEquals(dn, x509Certificate.getIssuerDN().getName());
assertEquals(SIGNATURE_ALGORITHM.toUpperCase(), x509Certificate.getSigAlgName().toUpperCase());
assertEquals("RSA", x509Certificate.getPublicKey().getAlgorithm());
x509Certificate.checkValidity();
}
@Test
public void testIssueCert() throws Exception {
int days = 365;
KeyPair issuerKeyPair = generateKeyPair();
X509Certificate issuer = CertificateUtils.generateSelfSignedX509Certificate(issuerKeyPair, "CN=testCa,O=testOrg", SIGNATURE_ALGORITHM, days);
String dn = "CN=testIssued,O=testOrg";
KeyPair keyPair = generateKeyPair();
X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(dn, keyPair.getPublic(), issuer, issuerKeyPair, SIGNATURE_ALGORITHM, days);
assertEquals(dn, x509Certificate.getSubjectDN().toString());
assertEquals(issuer.getSubjectDN().toString(), x509Certificate.getIssuerDN().toString());
assertEquals(keyPair.getPublic(), x509Certificate.getPublicKey());
Date notAfter = x509Certificate.getNotAfter();
assertTrue(notAfter.after(inFuture(days - 1)));
assertTrue(notAfter.before(inFuture(days + 1)));
Date notBefore = x509Certificate.getNotBefore();
assertTrue(notBefore.after(inFuture(-1)));
assertTrue(notBefore.before(inFuture(1)));
assertEquals(SIGNATURE_ALGORITHM.toUpperCase(), x509Certificate.getSigAlgName().toUpperCase());
assertEquals("RSA", x509Certificate.getPublicKey().getAlgorithm());
x509Certificate.verify(issuerKeyPair.getPublic());
}
}

View File

@ -23,6 +23,147 @@
<artifactId>nifi-resources</artifactId>
<packaging>pom</packaging>
<description>holds common resources used to build installers</description>
<properties>
<!--Wrapper Properties -->
<nifi.jvm.heap.mb>512</nifi.jvm.heap.mb>
<nifi.jvm.permgen.mb>128</nifi.jvm.permgen.mb>
<nifi.run.as />
<!-- nifi.properties: core properties -->
<nifi.version>${project.version}</nifi.version>
<nifi.flowcontroller.autoResumeState>true</nifi.flowcontroller.autoResumeState>
<nifi.flowcontroller.graceful.shutdown.period>10 sec</nifi.flowcontroller.graceful.shutdown.period>
<nifi.flowservice.writedelay.interval>500 ms</nifi.flowservice.writedelay.interval>
<nifi.administrative.yield.duration>30 sec</nifi.administrative.yield.duration>
<nifi.bored.yield.duration>10 millis</nifi.bored.yield.duration>
<nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
<nifi.flow.configuration.archive.enabled>true</nifi.flow.configuration.archive.enabled>
<nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
<nifi.flow.configuration.archive.max.time>30 days</nifi.flow.configuration.archive.max.time>
<nifi.flow.configuration.archive.max.storage>500 MB</nifi.flow.configuration.archive.max.storage>
<nifi.login.identity.provider.configuration.file>./conf/login-identity-providers.xml</nifi.login.identity.provider.configuration.file>
<nifi.authorizer.configuration.file>./conf/authorizers.xml</nifi.authorizer.configuration.file>
<nifi.templates.directory>./conf/templates</nifi.templates.directory>
<nifi.database.directory>./database_repository</nifi.database.directory>
<nifi.state.management.configuration.file>./conf/state-management.xml</nifi.state.management.configuration.file>
<nifi.state.management.embedded.zookeeper.start>false</nifi.state.management.embedded.zookeeper.start>
<nifi.state.management.embedded.zookeeper.properties>./conf/zookeeper.properties</nifi.state.management.embedded.zookeeper.properties>
<nifi.state.management.provider.local>local-provider</nifi.state.management.provider.local>
<nifi.state.management.provider.cluster>zk-provider</nifi.state.management.provider.cluster>
<nifi.flowfile.repository.implementation>org.apache.nifi.controller.repository.WriteAheadFlowFileRepository</nifi.flowfile.repository.implementation>
<nifi.flowfile.repository.directory>./flowfile_repository</nifi.flowfile.repository.directory>
<nifi.flowfile.repository.partitions>256</nifi.flowfile.repository.partitions>
<nifi.flowfile.repository.checkpoint.interval>2 mins</nifi.flowfile.repository.checkpoint.interval>
<nifi.flowfile.repository.always.sync>false</nifi.flowfile.repository.always.sync>
<nifi.swap.manager.implementation>org.apache.nifi.controller.FileSystemSwapManager</nifi.swap.manager.implementation>
<nifi.queue.swap.threshold>20000</nifi.queue.swap.threshold>
<nifi.swap.in.period>5 sec</nifi.swap.in.period>
<nifi.swap.in.threads>1</nifi.swap.in.threads>
<nifi.swap.out.period>5 sec</nifi.swap.out.period>
<nifi.swap.out.threads>4</nifi.swap.out.threads>
<nifi.content.repository.implementation>org.apache.nifi.controller.repository.FileSystemRepository</nifi.content.repository.implementation>
<nifi.content.claim.max.appendable.size>10 MB</nifi.content.claim.max.appendable.size>
<nifi.content.claim.max.flow.files>100</nifi.content.claim.max.flow.files>
<nifi.content.repository.directory.default>./content_repository</nifi.content.repository.directory.default>
<nifi.content.repository.archive.max.retention.period>12 hours</nifi.content.repository.archive.max.retention.period>
<nifi.content.repository.archive.max.usage.percentage>50%</nifi.content.repository.archive.max.usage.percentage>
<nifi.content.repository.archive.enabled>true</nifi.content.repository.archive.enabled>
<nifi.content.repository.always.sync>false</nifi.content.repository.always.sync>
<nifi.content.viewer.url>/nifi-content-viewer/</nifi.content.viewer.url>
<nifi.restore.directory />
<nifi.ui.banner.text />
<nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval>
<nifi.nar.library.directory>./lib</nifi.nar.library.directory>
<nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory>
<nifi.documentation.working.directory>./work/docs/components</nifi.documentation.working.directory>
<nifi.sensitive.props.algorithm>PBEWITHMD5AND256BITAES-CBC-OPENSSL</nifi.sensitive.props.algorithm>
<nifi.sensitive.props.provider>BC</nifi.sensitive.props.provider>
<nifi.h2.url.append>;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.h2.url.append>
<nifi.remote.input.socket.port>9990</nifi.remote.input.socket.port>
<!-- persistent provenance repository properties -->
<nifi.provenance.repository.implementation>org.apache.nifi.provenance.PersistentProvenanceRepository</nifi.provenance.repository.implementation>
<nifi.provenance.repository.directory.default>./provenance_repository</nifi.provenance.repository.directory.default>
<nifi.provenance.repository.max.storage.time>24 hours</nifi.provenance.repository.max.storage.time>
<nifi.provenance.repository.max.storage.size>1 GB</nifi.provenance.repository.max.storage.size>
<nifi.provenance.repository.rollover.time>30 secs</nifi.provenance.repository.rollover.time>
<nifi.provenance.repository.rollover.size>100 MB</nifi.provenance.repository.rollover.size>
<nifi.provenance.repository.query.threads>2</nifi.provenance.repository.query.threads>
<nifi.provenance.repository.index.threads>1</nifi.provenance.repository.index.threads>
<nifi.provenance.repository.compress.on.rollover>true</nifi.provenance.repository.compress.on.rollover>
<nifi.provenance.repository.indexed.fields>EventType, FlowFileUUID, Filename, ProcessorID, Relationship</nifi.provenance.repository.indexed.fields>
<nifi.provenance.repository.indexed.attributes />
<nifi.provenance.repository.index.shard.size>500 MB</nifi.provenance.repository.index.shard.size>
<nifi.provenance.repository.always.sync>false</nifi.provenance.repository.always.sync>
<nifi.provenance.repository.journal.count>16</nifi.provenance.repository.journal.count>
<nifi.provenance.repository.max.attribute.length>65536</nifi.provenance.repository.max.attribute.length>
<!-- volatile provenance repository properties -->
<nifi.provenance.repository.buffer.size>100000</nifi.provenance.repository.buffer.size>
<!-- Component status repository properties -->
<nifi.components.status.repository.implementation>org.apache.nifi.controller.status.history.VolatileComponentStatusRepository</nifi.components.status.repository.implementation>
<nifi.components.status.repository.buffer.size>1440</nifi.components.status.repository.buffer.size>
<nifi.components.status.snapshot.frequency>1 min</nifi.components.status.snapshot.frequency>
<!-- nifi.properties: web properties -->
<nifi.web.war.directory>./lib</nifi.web.war.directory>
<nifi.web.http.host />
<nifi.web.http.port>8080</nifi.web.http.port>
<nifi.web.https.host />
<nifi.web.https.port />
<nifi.jetty.work.dir>./work/jetty</nifi.jetty.work.dir>
<nifi.web.jetty.threads>200</nifi.web.jetty.threads>
<!-- nifi.properties: security properties -->
<nifi.security.keystore />
<nifi.security.keystoreType />
<nifi.security.keystorePasswd />
<nifi.security.keyPasswd />
<nifi.security.truststore />
<nifi.security.truststoreType />
<nifi.security.truststorePasswd />
<nifi.security.needClientAuth />
<nifi.security.user.authorizer>file-provider</nifi.security.user.authorizer>
<nifi.security.user.login.identity.provider />
<nifi.security.x509.principal.extractor />
<nifi.security.ocsp.responder.url />
<nifi.security.ocsp.responder.certificate />
<!-- nifi.properties: cluster common properties (cluster manager and nodes must have same values) -->
<nifi.cluster.protocol.heartbeat.interval>5 sec</nifi.cluster.protocol.heartbeat.interval>
<nifi.cluster.protocol.is.secure>false</nifi.cluster.protocol.is.secure>
<!-- nifi.properties: cluster node properties (only configure for cluster nodes) -->
<nifi.cluster.is.node>false</nifi.cluster.is.node>
<nifi.cluster.node.address />
<nifi.cluster.node.protocol.port />
<nifi.cluster.node.protocol.threads>10</nifi.cluster.node.protocol.threads>
<nifi.cluster.node.event.history.size>25</nifi.cluster.node.event.history.size>
<nifi.cluster.node.connection.timeout>5 sec</nifi.cluster.node.connection.timeout>
<nifi.cluster.node.read.timeout>5 sec</nifi.cluster.node.read.timeout>
<nifi.cluster.firewall.file />
<nifi.cluster.request.replication.claim.timeout>15 secs</nifi.cluster.request.replication.claim.timeout>
<!-- nifi.properties: zookeeper properties -->
<nifi.zookeeper.connect.string></nifi.zookeeper.connect.string>
<nifi.zookeeper.connect.timeout>3 secs</nifi.zookeeper.connect.timeout>
<nifi.zookeeper.session.timeout>3 secs</nifi.zookeeper.session.timeout>
<nifi.zookeeper.root.node>/nifi</nifi.zookeeper.root.node>
<!-- nifi.properties: kerberos properties -->
<nifi.kerberos.krb5.file> </nifi.kerberos.krb5.file>
<nifi.kerberos.service.principal />
<nifi.kerberos.keytab.location />
<nifi.kerberos.authentication.expiration>12 hours</nifi.kerberos.authentication.expiration>
</properties>
<build>
<plugins>
<plugin>

View File

@ -23,6 +23,7 @@
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>/</outputDirectory>
<filtered>true</filtered>
</fileSet>
<fileSet>
<directory>src/main/resources/bin</directory>

View File

@ -0,0 +1,256 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
APACHE NIFI TOOLKIT SUBCOMPONENTS:
The Apache NiFi Toolkit project contains subcomponents with separate copyright
notices and license terms. Your use of the source code for the these
subcomponents is subject to the terms and conditions of the following
licenses.
The binary distribution of this product bundles 'Bouncy Castle JDK 1.5 Provider'
under an MIT style license.
Copyright (c) 2000 - 2016 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
The binary distribution of this product bundles 'Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs'
under an MIT style license.
Copyright (c) 2000 - 2016 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,94 @@
Apache NiFi Toolkit
Copyright 2014-2016 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
===========================================
Apache Software License v2
===========================================
The following binary components are provided under the Apache Software License v2
(ASLv2) Apache NiFi
The following NOTICE information applies:
Apache NiFi
Copyright 2014-2016 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
This product includes the following work from the Apache Hadoop project:
BoundedByteArrayOutputStream.java adapted to SoftLimitBoundedByteArrayOutputStream.java
(ASLv2) Apache Commons CLI
The following NOTICE information applies:
Apache Commons IO
Copyright 2001-2015 The Apache Software Foundation
(ASLv2) Apache Commons Codec
The following NOTICE information applies:
Apache Commons Codec
Copyright 2002-2014 The Apache Software Foundation
src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
contains test data from http://aspell.net/test/orig/batch0.tab.
Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
===============================================================================
The content of package org.apache.commons.codec.language.bm has been translated
from the original php source code available at http://stevemorse.org/phoneticinfo.htm
with permission from the original authors.
Original source copyright:
Copyright (c) 2008 Alexander Beider & Stephen P. Morse.
(ASLv2) Apache Commons IO
The following NOTICE information applies:
Apache Commons IO
Copyright 2002-2012 The Apache Software Foundation
(ASLv2) Apache Commons Lang
The following NOTICE information applies:
Apache Commons Lang
Copyright 2001-2015 The Apache Software Foundation
(ASLv2) Apache HttpComponents
The following NOTICE information applies:
Apache HttpClient
Copyright 1999-2015 The Apache Software Foundation
Apache HttpCore
Copyright 2005-2015 The Apache Software Foundation
This project contains annotations derived from JCIP-ANNOTATIONS
Copyright (c) 2005 Brian Goetz and Tim Peierls. See http://www.jcip.net
(ASLv2) Jackson JSON processor
The following NOTICE information applies:
# Jackson JSON processor
Jackson is a high-performance, Free/Open Source JSON processing library.
It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
been in development since 2007.
It is currently developed by a community of developers, as well as supported
commercially by FasterXML.com.
## Licensing
Jackson core and extension components may licensed under different licenses.
To find the details that apply to this artifact see the accompanying LICENSE file.
For more information, including possible other licensing options, contact
FasterXML.com (http://fasterxml.com).
## Credits
A list of contributors may be found from CREDITS file, which is included
in some artifacts (usually source distributions); but is always available
from the source code management (SCM) system project uses.
(ASLv2) Jetty
The following NOTICE information applies:
Jetty Web Container
Copyright 1995-2015 Mort Bay Consulting Pty Ltd.

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-toolkit</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-toolkit-assembly</artifactId>
<packaging>pom</packaging>
<description>This is the assembly Apache NiFi Toolkit</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes combine.children="append">
<exclude>src/main/resources/conf/config-client.json</exclude>
<exclude>src/main/resources/conf/config-server.json</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>nifi-toolkit-${project.version}</finalName>
</configuration>
<executions>
<execution>
<id>make shared resource</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<archiverConfig>
<defaultDirectoryMode>0755</defaultDirectoryMode>
<directoryMode>0755</directoryMode>
<fileMode>0644</fileMode>
</archiverConfig>
<descriptors>
<descriptor>src/main/assembly/dependencies.xml</descriptor>
</descriptors>
<tarLongFileMode>posix</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-toolkit-tls</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,75 @@
<?xml version="1.0"?>
<!--
~ 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.
-->
<assembly>
<id>bin</id>
<formats>
<format>dir</format>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>nifi-toolkit-${project.version}</baseDirectory>
<dependencySets>
<!-- Write out all dependency artifacts to lib directory -->
<dependencySet>
<outputDirectory>lib</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
<directoryMode>0770</directoryMode>
<fileMode>0660</fileMode>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.basedir}/src/main/resources/bin</directory>
<outputDirectory>bin/</outputDirectory>
<fileMode>0700</fileMode>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/resources/conf</directory>
<outputDirectory>conf/</outputDirectory>
<fileMode>0600</fileMode>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/resources/classpath</directory>
<outputDirectory>classpath/</outputDirectory>
<fileMode>0600</fileMode>
</fileSet>
<fileSet>
<directory>${project.build.directory}/nifi-resources/conf</directory>
<outputDirectory>lib/</outputDirectory>
<fileMode>0600</fileMode>
</fileSet>
</fileSets>
<files>
<file>
<source>./LICENSE</source>
<outputDirectory>./</outputDirectory>
<destName>LICENSE</destName>
<fileMode>0644</fileMode>
<filtered>true</filtered>
</file>
<file>
<source>./NOTICE</source>
<outputDirectory>./</outputDirectory>
<destName>NOTICE</destName>
<fileMode>0644</fileMode>
<filtered>true</filtered>
</file>
</files>
</assembly>

View File

@ -0,0 +1,40 @@
@echo off
rem
rem Licensed to the Apache Software Foundation (ASF) under one or more
rem contributor license agreements. See the NOTICE file distributed with
rem this work for additional information regarding copyright ownership.
rem The ASF licenses this file to You under the Apache License, Version 2.0
rem (the "License"); you may not use this file except in compliance with
rem the License. You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License.
rem
rem Use JAVA_HOME if it's set; otherwise, just use java
if "%JAVA_HOME%" == "" goto noJavaHome
if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
set JAVA_EXE=%JAVA_HOME%\bin\java.exe
goto startConfig
:noJavaHome
echo The JAVA_HOME environment variable is not defined correctly.
echo Instead the PATH will be used to find the java executable.
echo.
set JAVA_EXE=java
goto startConfig
:startConfig
set LIB_DIR=%~dp0..\classpath;%~dp0..\lib
SET JAVA_PARAMS=-cp %LIB_DIR%\* -Xms12m -Xmx24m %JAVA_ARGS% org.apache.nifi.toolkit.tls.service.server.TlsCertificateAuthorityService
cmd.exe /C "%JAVA_EXE%" %JAVA_PARAMS% %*
popd

View File

@ -0,0 +1,120 @@
#!/bin/sh
#
# 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.
#
#
# Script structure inspired from Apache Karaf and other Apache projects with similar startup approaches
SCRIPT_DIR=$(dirname "$0")
SCRIPT_NAME=$(basename "$0")
NIFI_TOOLKIT_HOME=$(cd "${SCRIPT_DIR}" && cd .. && pwd)
PROGNAME=$(basename "$0")
warn() {
echo "${PROGNAME}: $*"
}
die() {
warn "$*"
exit 1
}
detectOS() {
# OS specific support (must be 'true' or 'false').
cygwin=false;
aix=false;
os400=false;
darwin=false;
case "$(uname)" in
CYGWIN*)
cygwin=true
;;
AIX*)
aix=true
;;
OS400*)
os400=true
;;
Darwin)
darwin=true
;;
esac
# For AIX, set an environment variable
if ${aix}; then
export LDR_CNTRL=MAXDATA=0xB0000000@DSA
echo ${LDR_CNTRL}
fi
}
locateJava() {
# Setup the Java Virtual Machine
if $cygwin ; then
[ -n "${JAVA}" ] && JAVA=$(cygpath --unix "${JAVA}")
[ -n "${JAVA_HOME}" ] && JAVA_HOME=$(cygpath --unix "${JAVA_HOME}")
fi
if [ "x${JAVA}" = "x" ] && [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
if [ "x${JAVA}" = "x" ]; then
if [ "x${JAVA_HOME}" != "x" ]; then
if [ ! -d "${JAVA_HOME}" ]; then
die "JAVA_HOME is not valid: ${JAVA_HOME}"
fi
JAVA="${JAVA_HOME}/bin/java"
else
warn "JAVA_HOME not set; results may vary"
JAVA=$(type java)
JAVA=$(expr "${JAVA}" : '.* \(/.*\)$')
if [ "x${JAVA}" = "x" ]; then
die "java command not found"
fi
fi
fi
}
init() {
# Determine if there is special OS handling we must perform
detectOS
# Locate the Java VM to execute
locateJava "$1"
}
run() {
LIBS="${NIFI_TOOLKIT_HOME}/lib/*"
sudo_cmd_prefix=""
if $cygwin; then
NIFI_TOOLKIT_HOME=$(cygpath --path --windows "${NIFI_TOOLKIT_HOME}")
CLASSPATH="$NIFI_TOOLKIT_HOME/classpath";$(cygpath --path --windows "${LIBS}")
else
CLASSPATH="$NIFI_TOOLKIT_HOME/classpath:${LIBS}"
fi
export JAVA_HOME="$JAVA_HOME"
export NIFI_TOOLKIT_HOME="$NIFI_TOOLKIT_HOME"
umask 0077
"${JAVA}" -cp "${CLASSPATH}" -Xms12m -Xmx24m org.apache.nifi.toolkit.tls.TlsToolkitMain "$@"
return $?
}
init "$1"
run "$@"

View File

@ -0,0 +1,22 @@
#
# 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.
#
log4j.rootLogger=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n

View File

@ -0,0 +1,14 @@
{
"keyStore" : "clientKeyStore",
"keyStoreType" : "jks",
"token" : "myTestTokenUseSomethingStronger",
"dn" : "CN=otherHostname,OU=NIFI",
"port" : 8443,
"caHostname" : "localhost",
"trustStore" : "clientTrustStore",
"trustStoreType" : "jks",
"days" : 1095,
"keySize" : 2048,
"keyPairAlgorithm" : "RSA",
"signingAlgorithm" : "SHA256WITHRSA"
}

View File

@ -0,0 +1,11 @@
{
"keyStore" : "serverKeyStore",
"keyStoreType" : "jks",
"token" : "myTestTokenUseSomethingStronger",
"caHostname" : "localhost",
"port" : 8443,
"days" : 1095,
"keySize" : 2048,
"keyPairAlgorithm" : "RSA",
"signingAlgorithm" : "SHA256WITHRSA"
}

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-toolkit</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-toolkit-tls</artifactId>
<description>Tooling to make tls configuration easier</description>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>process-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-resources</artifactId>
<type>zip</type>
<classifier>resources</classifier>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<includes>**/nifi.properties</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes combine.children="append">
<exclude>src/test/resources/rootCert.crt</exclude>
<exclude>src/test/resources/rootCert.key</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,97 @@
/*
* 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.nifi.toolkit.tls;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.service.client.TlsCertificateAuthorityClientCommandLine;
import org.apache.nifi.toolkit.tls.service.server.TlsCertificateAuthorityServiceCommandLine;
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandaloneCommandLine;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Command line entry point
*/
public class TlsToolkitMain {
public static final String DESCRIPTION = "DESCRIPTION";
private final Map<String, Class<?>> mainMap;
public TlsToolkitMain() {
mainMap = new LinkedHashMap<>();
mainMap.put("standalone", TlsToolkitStandaloneCommandLine.class);
mainMap.put("server", TlsCertificateAuthorityServiceCommandLine.class);
mainMap.put("client", TlsCertificateAuthorityClientCommandLine.class);
}
public static void main(String[] args) {
new TlsToolkitMain().doMain(args);
}
private void printUsageAndExit(String message, ExitCode exitCode) {
System.out.println(message);
System.out.println();
System.out.println("Usage: tls-toolkit service [-h] [args]");
System.out.println();
System.out.println("Services:");
mainMap.forEach((s, aClass) -> System.out.println(" " + s + ": " + getDescription(aClass)));
System.out.println();
System.exit(exitCode.ordinal());
}
private String getDescription(Class<?> clazz) {
try {
Field declaredField = clazz.getDeclaredField(DESCRIPTION);
return String.valueOf(declaredField.get(null));
} catch (Exception e) {
return "Unable to get description. (" + e.getMessage() + ")";
}
}
public void doMain(String[] args) {
if (args.length < 1) {
printUsageAndExit("Expected at least a service argument.", ExitCode.INVALID_ARGS);
}
String service = args[0].toLowerCase();
Class<?> mainClass = mainMap.get(service);
if (mainClass == null) {
printUsageAndExit("Unknown service: " + service, ExitCode.INVALID_ARGS);
}
Method main;
try {
main = mainClass.getDeclaredMethod("main", String[].class);
} catch (NoSuchMethodException e) {
printUsageAndExit("Service " + service + " is missing main method.", ExitCode.SERVICE_ERROR);
return;
}
try {
main.invoke(null, (Object) args);
} catch (IllegalAccessException e) {
printUsageAndExit("Service " + service + " has invalid main method.", ExitCode.SERVICE_ERROR);
} catch (InvocationTargetException e) {
printUsageAndExit("Service " + service + " error: " + e.getCause().getMessage(), ExitCode.SERVICE_ERROR);
}
return;
}
}

View File

@ -0,0 +1,178 @@
/*
* 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.nifi.toolkit.tls.commandLine;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.nifi.toolkit.tls.TlsToolkitMain;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
public abstract class BaseCommandLine {
public static final String HELP_ARG = "help";
public static final String JAVA_HOME = "JAVA_HOME";
public static final String NIFI_TOOLKIT_HOME = "NIFI_TOOLKIT_HOME";
public static final String FOOTER = new StringBuilder(System.lineSeparator()).append("Java home: ")
.append(System.getenv(JAVA_HOME)).append(System.lineSeparator()).append("NiFi Toolkit home: ").append(System.getenv(NIFI_TOOLKIT_HOME)).toString();
public static final String KEY_SIZE_ARG = "keySize";
public static final String KEY_ALGORITHM_ARG = "keyAlgorithm";
public static final String CERTIFICATE_AUTHORITY_HOSTNAME_ARG = "certificateAuthorityHostname";
public static final String DAYS_ARG = "days";
public static final String KEY_STORE_TYPE_ARG = "keyStoreType";
public static final String SIGNING_ALGORITHM_ARG = "signingAlgorithm";
public static final String DN_ARG = "dn";
public static final String DIFFERENT_KEY_AND_KEYSTORE_PASSWORDS_ARG = "differentKeyAndKeystorePasswords";
public static final String KEYSTORE = "keystore.";
public static final String TRUSTSTORE = "truststore.";
private final Options options;
private final String header;
private int keySize;
private String keyAlgorithm;
private String certificateAuthorityHostname;
private String keyStoreType;
private int days;
private String signingAlgorithm;
private boolean differentPasswordForKeyAndKeystore;
public BaseCommandLine(String header) {
this.header = System.lineSeparator() + header + System.lineSeparator() + System.lineSeparator();
this.options = new Options();
if (shouldAddDaysArg()) {
addOptionWithArg("d", DAYS_ARG, "Number of days issued certificate should be valid for.", TlsConfig.DEFAULT_DAYS);
}
addOptionWithArg("T", KEY_STORE_TYPE_ARG, "The type of keyStores to generate.", getKeyStoreTypeDefault());
options.addOption("h", HELP_ARG, false, "Print help and exit.");
addOptionWithArg("c", CERTIFICATE_AUTHORITY_HOSTNAME_ARG, "Hostname of NiFi Certificate Authority", TlsConfig.DEFAULT_HOSTNAME);
addOptionWithArg("a", KEY_ALGORITHM_ARG, "Algorithm to use for generated keys.", TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
addOptionWithArg("k", KEY_SIZE_ARG, "Number of bits for generated keys.", TlsConfig.DEFAULT_KEY_SIZE);
if (shouldAddSigningAlgorithmArg()) {
addOptionWithArg("s", SIGNING_ALGORITHM_ARG, "Algorithm to use for signing certificates.", TlsConfig.DEFAULT_SIGNING_ALGORITHM);
}
addOptionNoArg("g", DIFFERENT_KEY_AND_KEYSTORE_PASSWORDS_ARG, "Use different generated password for the key and the keyStore.");
}
protected String getKeyStoreTypeDefault() {
return TlsConfig.DEFAULT_KEY_STORE_TYPE;
}
protected boolean shouldAddSigningAlgorithmArg() {
return true;
}
protected boolean shouldAddDaysArg() {
return true;
}
protected void addOptionWithArg(String arg, String longArg, String description) {
addOptionWithArg(arg, longArg, description, null);
}
protected void addOptionNoArg(String arg, String longArg, String description) {
options.addOption(arg, longArg, false, description);
}
protected void addOptionWithArg(String arg, String longArg, String description, Object defaultVal) {
String fullDescription = description;
if (defaultVal != null) {
fullDescription += " (default: " + defaultVal + ")";
}
options.addOption(arg, longArg, true, fullDescription);
}
public void printUsage(String errorMessage) {
if (errorMessage != null) {
System.out.println(errorMessage);
System.out.println();
}
HelpFormatter helpFormatter = new HelpFormatter();
helpFormatter.setWidth(160);
helpFormatter.printHelp(TlsToolkitMain.class.getCanonicalName(), header, options, FOOTER, true);
}
protected <T> T printUsageAndThrow(String errorMessage, ExitCode exitCode) throws CommandLineParseException {
printUsage(errorMessage);
throw new CommandLineParseException(errorMessage, exitCode.ordinal());
}
protected int getIntValue(CommandLine commandLine, String arg, int defaultVal) throws CommandLineParseException {
try {
return Integer.parseInt(commandLine.getOptionValue(arg, Integer.toString(defaultVal)));
} catch (NumberFormatException e) {
return printUsageAndThrow("Expected integer for " + arg + " argument. (" + e.getMessage() + ")", ExitCode.ERROR_PARSING_INT_ARG);
}
}
public int getKeySize() {
return keySize;
}
public String getKeyAlgorithm() {
return keyAlgorithm;
}
public String getCertificateAuthorityHostname() {
return certificateAuthorityHostname;
}
public String getKeyStoreType() {
return keyStoreType;
}
public int getDays() {
return days;
}
public String getSigningAlgorithm() {
return signingAlgorithm;
}
public boolean differentPasswordForKeyAndKeystore() {
return differentPasswordForKeyAndKeystore;
}
protected CommandLine doParse(String[] args) throws CommandLineParseException {
CommandLineParser parser = new DefaultParser();
CommandLine commandLine;
try {
commandLine = parser.parse(options, args);
if (commandLine.hasOption(HELP_ARG)) {
return printUsageAndThrow(null, ExitCode.HELP);
}
certificateAuthorityHostname = commandLine.getOptionValue(CERTIFICATE_AUTHORITY_HOSTNAME_ARG, TlsConfig.DEFAULT_HOSTNAME);
days = getIntValue(commandLine, DAYS_ARG, TlsConfig.DEFAULT_DAYS);
keySize = getIntValue(commandLine, KEY_SIZE_ARG, TlsConfig.DEFAULT_KEY_SIZE);
keyAlgorithm = commandLine.getOptionValue(KEY_ALGORITHM_ARG, TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
keyStoreType = commandLine.getOptionValue(KEY_STORE_TYPE_ARG, getKeyStoreTypeDefault());
signingAlgorithm = commandLine.getOptionValue(SIGNING_ALGORITHM_ARG, TlsConfig.DEFAULT_SIGNING_ALGORITHM);
differentPasswordForKeyAndKeystore = commandLine.hasOption(DIFFERENT_KEY_AND_KEYSTORE_PASSWORDS_ARG);
} catch (ParseException e) {
return printUsageAndThrow("Error parsing command line. (" + e.getMessage() + ")", ExitCode.ERROR_PARSING_COMMAND_LINE);
}
return commandLine;
}
public void parse(String... args) throws CommandLineParseException {
doParse(args);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.nifi.toolkit.tls.commandLine;
public class CommandLineParseException extends Exception {
private final int exitCode;
public CommandLineParseException(String message, int exitCode) {
super(message);
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.nifi.toolkit.tls.commandLine;
public enum ExitCode {
SUCCESS,
HELP,
INVALID_ARGS,
SERVICE_ERROR,
ERROR_PARSING_COMMAND_LINE,
ERROR_GENERATING_CONFIG,
ERROR_SAME_KEY_AND_KEY_PASSWORD,
ERROR_INCORRECT_NUMBER_OF_PASSWORDS,
ERROR_PARSING_INT_ARG,
ERROR_TOKEN_ARG_EMPTY,
ERROR_READING_NIFI_PROPERTIES
}

View File

@ -0,0 +1,80 @@
/*
* 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.nifi.toolkit.tls.configuration;
import org.apache.nifi.toolkit.tls.service.client.TlsCertificateSigningRequestPerformer;
import org.apache.nifi.util.StringUtils;
import java.security.NoSuchAlgorithmException;
public class TlsClientConfig extends TlsConfig {
private String trustStore;
private String trustStorePassword;
private String trustStoreType = DEFAULT_KEY_STORE_TYPE;
public TlsClientConfig() {
}
public TlsClientConfig(TlsConfig tlsConfig) {
setToken(tlsConfig.getToken());
setCaHostname(tlsConfig.getCaHostname());
setPort(tlsConfig.getPort());
setKeyStoreType(tlsConfig.getKeyStoreType());
setTrustStoreType(tlsConfig.getKeyStoreType());
setKeyPairAlgorithm(tlsConfig.getKeyPairAlgorithm());
setKeySize(tlsConfig.getKeySize());
setSigningAlgorithm(tlsConfig.getSigningAlgorithm());
}
public String getTrustStoreType() {
return trustStoreType;
}
public void setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
}
public String getTrustStore() {
return trustStore;
}
public void setTrustStore(String trustStore) {
this.trustStore = trustStore;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}
public TlsCertificateSigningRequestPerformer createCertificateSigningRequestPerformer() throws NoSuchAlgorithmException {
return new TlsCertificateSigningRequestPerformer(this);
}
@Override
public void initDefaults() {
super.initDefaults();
if (StringUtils.isEmpty(trustStoreType)) {
trustStoreType = DEFAULT_KEY_STORE_TYPE;
}
}
}

View File

@ -0,0 +1,171 @@
/*
* 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.nifi.toolkit.tls.configuration;
import org.apache.nifi.util.StringUtils;
public class TlsConfig {
public static final String DEFAULT_HOSTNAME = "localhost";
public static final String DEFAULT_KEY_STORE_TYPE = "jks";
public static final int DEFAULT_PORT = 8443;
public static final int DEFAULT_DAYS = 3 * 365;
public static final int DEFAULT_KEY_SIZE = 2048;
public static final String DEFAULT_KEY_PAIR_ALGORITHM = "RSA";
public static final String DEFAULT_SIGNING_ALGORITHM = "SHA256WITHRSA";
private int days = DEFAULT_DAYS;
private int keySize = DEFAULT_KEY_SIZE;
private String keyPairAlgorithm = DEFAULT_KEY_PAIR_ALGORITHM;
private String signingAlgorithm = DEFAULT_SIGNING_ALGORITHM;
private String dn;
private String keyStore;
private String keyStoreType = DEFAULT_KEY_STORE_TYPE;
private String keyStorePassword;
private String keyPassword;
private String token;
private String caHostname = DEFAULT_HOSTNAME;
private int port = DEFAULT_PORT;
public static String calcDefaultDn(String hostname) {
return "CN=" + hostname + ",OU=NIFI";
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getKeyStore() {
return keyStore;
}
public void setKeyStore(String keyStore) {
this.keyStore = keyStore;
}
public String getKeyStoreType() {
return keyStoreType;
}
public void setKeyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
}
public String getKeyStorePassword() {
return keyStorePassword;
}
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
public String getKeyPassword() {
return keyPassword;
}
public void setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getCaHostname() {
return caHostname;
}
public void setCaHostname(String caHostname) {
this.caHostname = caHostname;
}
public String getDn() {
return dn;
}
public void setDn(String dn) {
this.dn = dn;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public int getKeySize() {
return keySize;
}
public void setKeySize(int keySize) {
this.keySize = keySize;
}
public String getKeyPairAlgorithm() {
return keyPairAlgorithm;
}
public void setKeyPairAlgorithm(String keyPairAlgorithm) {
this.keyPairAlgorithm = keyPairAlgorithm;
}
public String getSigningAlgorithm() {
return signingAlgorithm;
}
public void setSigningAlgorithm(String signingAlgorithm) {
this.signingAlgorithm = signingAlgorithm;
}
public void initDefaults() {
if (days == 0) {
days = DEFAULT_DAYS;
}
if (keySize == 0) {
keySize = DEFAULT_KEY_SIZE;
}
if (StringUtils.isEmpty(keyPairAlgorithm)) {
keyPairAlgorithm = DEFAULT_KEY_PAIR_ALGORITHM;
}
if (StringUtils.isEmpty(signingAlgorithm)) {
signingAlgorithm = DEFAULT_SIGNING_ALGORITHM;
}
if (port == 0) {
port = DEFAULT_PORT;
}
if (StringUtils.isEmpty(keyStoreType)) {
keyStoreType = DEFAULT_KEY_STORE_TYPE;
}
if (StringUtils.isEmpty(caHostname)) {
caHostname = DEFAULT_HOSTNAME;
}
if (StringUtils.isEmpty(dn)) {
dn = calcDefaultDn(caHostname);
}
}
}

View File

@ -0,0 +1,159 @@
/*
* 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.nifi.toolkit.tls.manager;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.manager.writer.ConfigurationWriter;
import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.toolkit.tls.util.PasswordUtil;
import org.apache.nifi.util.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableEntryException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
public class BaseTlsManager {
public static final String PKCS_12 = "PKCS12";
private final TlsConfig tlsConfig;
private final PasswordUtil passwordUtil;
private final InputStreamFactory inputStreamFactory;
private final KeyStore keyStore;
private final List<ConfigurationWriter<TlsConfig>> configurationWriters;
private boolean differentKeyAndKeyStorePassword = false;
public BaseTlsManager(TlsConfig tlsConfig) throws GeneralSecurityException, IOException {
this(tlsConfig, new PasswordUtil(), FileInputStream::new);
}
public BaseTlsManager(TlsConfig tlsConfig, PasswordUtil passwordUtil, InputStreamFactory inputStreamFactory) throws GeneralSecurityException, IOException {
this.tlsConfig = tlsConfig;
this.passwordUtil = passwordUtil;
this.inputStreamFactory = inputStreamFactory;
this.keyStore = loadKeystore(tlsConfig.getKeyStore(), tlsConfig.getKeyStoreType(), getKeyStorePassword());
this.configurationWriters = new ArrayList<>();
}
public KeyStore getKeyStore() {
return keyStore;
}
public KeyStore.Entry getEntry(String alias) throws UnrecoverableEntryException, NoSuchAlgorithmException, KeyStoreException {
String keyPassword = getKeyPassword();
return keyStore.getEntry(alias, new KeyStore.PasswordProtection(keyPassword == null ? null : keyPassword.toCharArray()));
}
public KeyStore.Entry addPrivateKeyToKeyStore(KeyPair keyPair, String alias, Certificate... certificates) throws GeneralSecurityException, IOException {
return addPrivateKeyToKeyStore(keyStore, keyPair, alias, getKeyPassword(), certificates);
}
private KeyStore.Entry addPrivateKeyToKeyStore(KeyStore keyStore, KeyPair keyPair, String alias, String passphrase, Certificate... certificates) throws GeneralSecurityException, IOException {
keyStore.setKeyEntry(alias, keyPair.getPrivate(), passphrase == null ? null : passphrase.toCharArray(), certificates);
return getEntry(alias);
}
public void setDifferentKeyAndKeyStorePassword(boolean differentKeyAndKeyStorePassword) {
this.differentKeyAndKeyStorePassword = differentKeyAndKeyStorePassword;
}
private String getKeyPassword() {
if (keyStore.getType().equalsIgnoreCase(PKCS_12)) {
tlsConfig.setKeyPassword(null);
return null;
} else {
String result = tlsConfig.getKeyPassword();
if (StringUtils.isEmpty(result)) {
if (differentKeyAndKeyStorePassword) {
result = passwordUtil.generatePassword();
} else {
result = getKeyStorePassword();
}
tlsConfig.setKeyPassword(result);
}
return result;
}
}
private String getKeyStorePassword() {
String result = tlsConfig.getKeyStorePassword();
if (StringUtils.isEmpty(result)) {
result = passwordUtil.generatePassword();
tlsConfig.setKeyStorePassword(result);
}
return result;
}
private KeyStore getInstance(String keyStoreType) throws KeyStoreException, NoSuchProviderException {
if (PKCS_12.equals(keyStoreType)) {
return KeyStore.getInstance(keyStoreType, BouncyCastleProvider.PROVIDER_NAME);
} else {
return KeyStore.getInstance(keyStoreType);
}
}
protected KeyStore loadKeystore(String keyStore, String keyStoreType, String keyStorePassword) throws GeneralSecurityException, IOException {
KeyStore result = getInstance(keyStoreType);
File file = new File(keyStore);
if (file.exists()) {
try (InputStream stream = inputStreamFactory.create(file)) {
result.load(stream, keyStorePassword.toCharArray());
}
return result;
}
result.load(null, null);
return result;
}
public void write(OutputStreamFactory outputStreamFactory) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
String keyStorePassword = getKeyStorePassword();
try (OutputStream outputStream = outputStreamFactory.create(new File(tlsConfig.getKeyStore()))) {
keyStore.store(outputStream, keyStorePassword.toCharArray());
}
for (ConfigurationWriter<TlsConfig> configurationWriter : configurationWriters) {
configurationWriter.write(tlsConfig);
}
}
public PasswordUtil getPasswordUtil() {
return passwordUtil;
}
public void addConfigurationWriter(ConfigurationWriter<TlsConfig> configurationWriter) {
configurationWriters.add(configurationWriter);
}
public TlsConfig getTlsConfig() {
return tlsConfig;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.nifi.toolkit.tls.manager;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
public class TlsCertificateAuthorityManager extends BaseTlsManager {
public TlsCertificateAuthorityManager(TlsConfig tlsConfig) throws GeneralSecurityException, IOException {
super(tlsConfig);
}
public KeyStore.PrivateKeyEntry getOrGenerateCertificateAuthority() throws GeneralSecurityException, IOException {
KeyStore.Entry entry = getEntry(TlsToolkitStandalone.NIFI_KEY);
if (entry == null) {
TlsConfig tlsConfig = getTlsConfig();
KeyPair keyPair = TlsHelper.generateKeyPair(tlsConfig.getKeyPairAlgorithm(), tlsConfig.getKeySize());
X509Certificate caCert = CertificateUtils.generateSelfSignedX509Certificate(keyPair, tlsConfig.getDn(), tlsConfig.getSigningAlgorithm(), tlsConfig.getDays());
entry = addPrivateKeyToKeyStore(keyPair, TlsToolkitStandalone.NIFI_KEY, caCert);
} else if (!KeyStore.PrivateKeyEntry.class.isInstance(entry)) {
throw new IOException("Expected " + TlsToolkitStandalone.NIFI_KEY + " alias to contain a private key entry");
}
return (KeyStore.PrivateKeyEntry) entry;
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.nifi.toolkit.tls.manager;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.manager.writer.ConfigurationWriter;
import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.toolkit.tls.util.PasswordUtil;
import org.apache.nifi.util.StringUtils;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TlsClientManager extends BaseTlsManager {
private final TlsClientConfig tlsClientConfig;
private final KeyStore trustStore;
private final List<ConfigurationWriter<TlsClientConfig>> configurationWriters;
private final Set<String> certificateAliases;
private File certificateAuthorityDirectory;
public TlsClientManager(TlsClientConfig tlsClientConfig) throws GeneralSecurityException, IOException {
this(tlsClientConfig, new PasswordUtil(), FileInputStream::new);
}
public TlsClientManager(TlsClientConfig tlsClientConfig, PasswordUtil passwordUtil, InputStreamFactory inputStreamFactory) throws GeneralSecurityException, IOException {
super(tlsClientConfig, passwordUtil, inputStreamFactory);
this.trustStore = loadKeystore(tlsClientConfig.getTrustStore(), tlsClientConfig.getTrustStoreType(), tlsClientConfig.getTrustStorePassword());
this.tlsClientConfig = tlsClientConfig;
this.configurationWriters = new ArrayList<>();
this.certificateAliases = new HashSet<>();
}
public void setCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
trustStore.setCertificateEntry(alias, cert);
certificateAliases.add(alias);
}
public void setCertificateAuthorityDirectory(File certificateAuthorityDirectory) {
this.certificateAuthorityDirectory = certificateAuthorityDirectory;
}
@Override
public void write(OutputStreamFactory outputStreamFactory) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException {
super.write(outputStreamFactory);
String trustStorePassword = tlsClientConfig.getTrustStorePassword();
if (StringUtils.isEmpty(trustStorePassword)) {
trustStorePassword = getPasswordUtil().generatePassword();
tlsClientConfig.setTrustStorePassword(trustStorePassword);
}
try (OutputStream outputStream = outputStreamFactory.create(new File(tlsClientConfig.getTrustStore()))) {
trustStore.store(outputStream, trustStorePassword.toCharArray());
}
for (ConfigurationWriter<TlsClientConfig> configurationWriter : configurationWriters) {
configurationWriter.write(tlsClientConfig);
}
if (certificateAuthorityDirectory != null) {
// Write out all trusted certificates from truststore
for (String alias : Collections.list(trustStore.aliases())) {
try {
KeyStore.Entry trustStoreEntry = trustStore.getEntry(alias, null);
if (trustStoreEntry instanceof KeyStore.TrustedCertificateEntry) {
Certificate trustedCertificate = ((KeyStore.TrustedCertificateEntry) trustStoreEntry).getTrustedCertificate();
try (OutputStream outputStream = outputStreamFactory.create(new File(certificateAuthorityDirectory, alias + ".pem"));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
PemWriter pemWriter = new PemWriter(outputStreamWriter)) {
pemWriter.writeObject(new JcaMiscPEMGenerator(trustedCertificate));
}
}
} catch (UnrecoverableEntryException e) {
// Ignore, not a trusted cert
}
}
}
}
public void addClientConfigurationWriter(ConfigurationWriter<TlsClientConfig> configurationWriter) {
configurationWriters.add(configurationWriter);
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.nifi.toolkit.tls.manager.writer;
import java.io.IOException;
public interface ConfigurationWriter<T> {
void write(T t) throws IOException;
}

View File

@ -0,0 +1,45 @@
/*
* 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.nifi.toolkit.tls.manager.writer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
public class JsonConfigurationWriter<T> implements ConfigurationWriter<T> {
private final ObjectWriter objectWriter;
private final OutputStreamFactory outputStreamFactory;
private final File file;
public JsonConfigurationWriter(ObjectMapper objectMapper, OutputStreamFactory outputStreamFactory, File file) {
this.objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
this.outputStreamFactory = outputStreamFactory;
this.file = file;
}
@Override
public void write(T tlsConfig) throws IOException {
try (OutputStream stream = outputStreamFactory.create(file)) {
objectWriter.writeValue(stream, tlsConfig);
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.nifi.toolkit.tls.manager.writer;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriter;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
public class NifiPropertiesTlsClientConfigWriter implements ConfigurationWriter<TlsClientConfig> {
private final NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
private final OutputStreamFactory outputStreamFactory;
private final File file;
private final String hostname;
private final String httpsPort;
public NifiPropertiesTlsClientConfigWriter(NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, OutputStreamFactory outputStreamFactory, File file, String hostname, String httpsPort) {
this.niFiPropertiesWriterFactory = niFiPropertiesWriterFactory;
this.outputStreamFactory = outputStreamFactory;
this.file = file;
this.hostname = hostname;
this.httpsPort = httpsPort;
}
@Override
public void write(TlsClientConfig tlsClientConfig) throws IOException {
NiFiPropertiesWriter niFiPropertiesWriter = niFiPropertiesWriterFactory.create();
updateProperties(niFiPropertiesWriter, tlsClientConfig);
try (OutputStream stream = outputStreamFactory.create(file)) {
niFiPropertiesWriter.writeNiFiProperties(stream);
}
}
protected void updateProperties(NiFiPropertiesWriter niFiPropertiesWriter, TlsClientConfig tlsClientConfig) {
Path parentPath = Paths.get(file.getParentFile().getAbsolutePath());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE, parentPath.relativize(Paths.get(tlsClientConfig.getKeyStore())).toString());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsClientConfig.getKeyStoreType());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsClientConfig.getKeyStorePassword());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_KEY_PASSWD, tlsClientConfig.getKeyPassword());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE, parentPath.relativize(Paths.get(tlsClientConfig.getTrustStore())).toString());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsClientConfig.getTrustStoreType());
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsClientConfig.getTrustStorePassword());
if (!StringUtils.isEmpty(httpsPort)) {
if (!StringUtils.isEmpty(hostname)) {
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTPS_HOST, hostname);
}
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTPS_PORT, httpsPort);
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTP_HOST, "");
niFiPropertiesWriter.setPropertyValue(NiFiProperties.WEB_HTTP_PORT, "");
niFiPropertiesWriter.setPropertyValue(NiFiProperties.SITE_TO_SITE_SECURE, "true");
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.nifi.toolkit.tls.properties;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class NiFiPropertiesWriter {
private final List<String> lines;
private final Map<String, String> updatedValues;
public NiFiPropertiesWriter(List<String> lines) {
this.lines = new ArrayList<>(lines);
this.updatedValues = new HashMap<>();
}
public void setPropertyValue(String key, String value) {
updatedValues.put(key, value);
}
public void writeNiFiProperties(OutputStream outputStream) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {
Map<String, String> remainingValues = new HashMap<>(updatedValues);
Set<String> keysSeen = new HashSet<>();
for (String line : lines) {
String key = line.split("=")[0].trim();
boolean outputLine = true;
if (!key.isEmpty() && !key.startsWith("#")) {
if (!keysSeen.add(key)) {
throw new IOException("Found key more than once in nifi.properties: " + key);
}
String value = remainingValues.remove(key);
if (value != null) {
writer.write(key);
writer.write("=");
writer.write(value);
outputLine = false;
}
}
if (outputLine) {
writer.write(line);
}
writer.newLine();
}
for (Map.Entry<String, String> keyValueEntry : remainingValues.entrySet()) {
writer.write(keyValueEntry.getKey());
writer.write("=");
writer.write(keyValueEntry.getValue());
writer.newLine();
}
}
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.nifi.toolkit.tls.properties;
import org.apache.nifi.toolkit.tls.TlsToolkitMain;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class NiFiPropertiesWriterFactory {
private final List<String> lines;
public NiFiPropertiesWriterFactory() throws IOException {
this(TlsToolkitMain.class.getClassLoader().getResourceAsStream("conf/nifi.properties"));
}
public NiFiPropertiesWriterFactory(InputStream inputStream) throws IOException {
List<String> lines = new ArrayList<>();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
lines.add(line.trim());
}
}
this.lines = Collections.unmodifiableList(lines);
}
public NiFiPropertiesWriter create() {
return new NiFiPropertiesWriter(lines);
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.nifi.toolkit.tls.service;
import org.apache.commons.cli.CommandLine;
import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.util.StringUtils;
import java.io.File;
public abstract class BaseCertificateAuthorityCommandLine extends BaseCommandLine {
public static final String TOKEN_ARG = "token";
public static final String CONFIG_JSON_ARG = "configJson";
public static final String USE_CONFIG_JSON_ARG = "useConfigJson";
public static final String PORT_ARG = "PORT";
public static final String DEFAULT_CONFIG_JSON = new File("config.json").getPath();
private String token;
private String configJson;
private boolean onlyUseConfigJson;
private int port;
private String dn;
public BaseCertificateAuthorityCommandLine(String header) {
super(header);
addOptionWithArg("t", TOKEN_ARG, getTokenDescription());
addOptionWithArg("f", CONFIG_JSON_ARG, "The place to write configuration info", DEFAULT_CONFIG_JSON);
addOptionNoArg("F", USE_CONFIG_JSON_ARG, "Flag specifying that all configuration is read from " + CONFIG_JSON_ARG + " to facilitate automated use (otherwise "
+ CONFIG_JSON_ARG + " will only be written to.");
addOptionWithArg("p", PORT_ARG, getPortDescription(), TlsConfig.DEFAULT_PORT);
addOptionWithArg("D", DN_ARG, getDnDescription(), TlsConfig.calcDefaultDn(getDnHostname()));
}
protected abstract String getTokenDescription();
protected abstract String getDnDescription();
protected abstract String getPortDescription();
protected abstract String getDnHostname();
@Override
protected CommandLine doParse(String[] args) throws CommandLineParseException {
CommandLine commandLine = super.doParse(args);
token = commandLine.getOptionValue(TOKEN_ARG);
onlyUseConfigJson = commandLine.hasOption(USE_CONFIG_JSON_ARG);
if (StringUtils.isEmpty(token) && !onlyUseConfigJson) {
printUsageAndThrow(TOKEN_ARG + " argument must not be empty unless " + USE_CONFIG_JSON_ARG + " set", ExitCode.ERROR_TOKEN_ARG_EMPTY);
}
configJson = commandLine.getOptionValue(CONFIG_JSON_ARG, DEFAULT_CONFIG_JSON);
port = getIntValue(commandLine, PORT_ARG, TlsConfig.DEFAULT_PORT);
dn = commandLine.getOptionValue(DN_ARG, TlsConfig.calcDefaultDn(getDnHostname()));
return commandLine;
}
public String getToken() {
return token;
}
public String getConfigJson() {
return configJson;
}
public boolean onlyUseConfigJson() {
return onlyUseConfigJson;
}
public int getPort() {
return port;
}
public String getDn() {
return dn;
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.nifi.toolkit.tls.service.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.manager.TlsClientManager;
import org.apache.nifi.toolkit.tls.manager.writer.JsonConfigurationWriter;
import org.apache.nifi.toolkit.tls.service.BaseCertificateAuthorityCommandLine;
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.apache.nifi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
/**
* Client that will generate a CSR and submit to a CA, writing out the results to a keystore and truststore along with a config file if successful
*/
public class TlsCertificateAuthorityClient {
private final Logger logger = LoggerFactory.getLogger(TlsCertificateAuthorityClient.class);
private final OutputStreamFactory outputStreamFactory;
public TlsCertificateAuthorityClient() {
this(FileOutputStream::new);
}
public TlsCertificateAuthorityClient(OutputStreamFactory outputStreamFactory) {
this.outputStreamFactory = outputStreamFactory;
}
public void generateCertificateAndGetItSigned(TlsClientConfig tlsClientConfig, String certificateDirectory, String configJson, boolean differentKeyAndKeyStorePassword) throws Exception {
TlsClientManager tlsClientManager;
try {
tlsClientManager = new TlsClientManager(tlsClientConfig);
} catch (IOException e) {
logger.error("Unable to open existing keystore, it can be reused by specifiying both " + BaseCertificateAuthorityCommandLine.CONFIG_JSON_ARG + " and " +
BaseCertificateAuthorityCommandLine.USE_CONFIG_JSON_ARG);
throw e;
}
tlsClientManager.setDifferentKeyAndKeyStorePassword(differentKeyAndKeyStorePassword);
if (!StringUtils.isEmpty(certificateDirectory)) {
tlsClientManager.setCertificateAuthorityDirectory(new File(certificateDirectory));
}
if (!StringUtils.isEmpty(configJson)) {
tlsClientManager.addClientConfigurationWriter(new JsonConfigurationWriter<>(new ObjectMapper(), outputStreamFactory, new File(configJson)));
}
if (tlsClientManager.getEntry(TlsToolkitStandalone.NIFI_KEY) == null) {
if (logger.isInfoEnabled()) {
logger.info("Requesting new certificate from " + tlsClientConfig.getCaHostname() + ":" + tlsClientConfig.getPort());
}
KeyPair keyPair = TlsHelper.generateKeyPair(tlsClientConfig.getKeyPairAlgorithm(), tlsClientConfig.getKeySize());
X509Certificate[] certificates = tlsClientConfig.createCertificateSigningRequestPerformer().perform(keyPair);
tlsClientManager.addPrivateKeyToKeyStore(keyPair, TlsToolkitStandalone.NIFI_KEY, certificates);
tlsClientManager.setCertificateEntry(TlsToolkitStandalone.NIFI_CERT, certificates[certificates.length - 1]);
} else {
if (logger.isInfoEnabled()) {
logger.info("Already had entry for " + TlsToolkitStandalone.NIFI_KEY + " not requesting new certificate.");
}
}
tlsClientManager.write(outputStreamFactory);
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.nifi.toolkit.tls.service.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.cli.CommandLine;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.service.BaseCertificateAuthorityCommandLine;
import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class TlsCertificateAuthorityClientCommandLine extends BaseCertificateAuthorityCommandLine {
public static final String DESCRIPTION = "Generates a private key and gets it signed by the certificate authority.";
public static final String CERTIFICATE_DIRECTORY = "certificateDirectory";
public static final String DEFAULT_CERTIFICATE_DIRECTORY = ".";
private final Logger logger = LoggerFactory.getLogger(TlsCertificateAuthorityClientCommandLine.class);
private final InputStreamFactory inputStreamFactory;
private String certificateDirectory;
public TlsCertificateAuthorityClientCommandLine() {
this(FileInputStream::new);
}
public TlsCertificateAuthorityClientCommandLine(InputStreamFactory inputStreamFactory) {
super(DESCRIPTION);
this.inputStreamFactory = inputStreamFactory;
addOptionWithArg("C", CERTIFICATE_DIRECTORY, "The file to write the CA certificate to", DEFAULT_CERTIFICATE_DIRECTORY);
}
public static void main(String[] args) throws Exception {
TlsHelper.addBouncyCastleProvider();
TlsCertificateAuthorityClientCommandLine tlsCertificateAuthorityClientCommandLine = new TlsCertificateAuthorityClientCommandLine();
try {
tlsCertificateAuthorityClientCommandLine.parse(args);
} catch (CommandLineParseException e) {
System.exit(e.getExitCode());
}
new TlsCertificateAuthorityClient().generateCertificateAndGetItSigned(tlsCertificateAuthorityClientCommandLine.createClientConfig(),
tlsCertificateAuthorityClientCommandLine.getCertificateDirectory(), tlsCertificateAuthorityClientCommandLine.getConfigJson(),
tlsCertificateAuthorityClientCommandLine.differentPasswordForKeyAndKeystore());
System.exit(ExitCode.SUCCESS.ordinal());
}
@Override
protected boolean shouldAddDaysArg() {
return false;
}
@Override
protected boolean shouldAddSigningAlgorithmArg() {
return false;
}
@Override
protected String getTokenDescription() {
return "The token to use to prevent MITM (required and must be same as one used by CA)";
}
@Override
protected String getDnDescription() {
return "The dn to use for the client certificate";
}
@Override
protected String getPortDescription() {
return "The port to use to communicate with the Certificate Authority";
}
@Override
protected String getDnHostname() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
logger.warn("Unable to determine hostname", e);
return "localhost";
}
}
@Override
protected CommandLine doParse(String[] args) throws CommandLineParseException {
CommandLine commandLine = super.doParse(args);
certificateDirectory = commandLine.getOptionValue(CERTIFICATE_DIRECTORY, DEFAULT_CERTIFICATE_DIRECTORY);
return commandLine;
}
public String getCertificateDirectory() {
return certificateDirectory;
}
public TlsClientConfig createClientConfig() throws IOException {
if (onlyUseConfigJson()) {
try (InputStream inputStream = inputStreamFactory.create(new File(getConfigJson()))) {
TlsClientConfig tlsClientConfig = new ObjectMapper().readValue(inputStream, TlsClientConfig.class);
tlsClientConfig.initDefaults();
return tlsClientConfig;
}
} else {
TlsClientConfig tlsClientConfig = new TlsClientConfig();
tlsClientConfig.setCaHostname(getCertificateAuthorityHostname());
tlsClientConfig.setDn(getDn());
tlsClientConfig.setToken(getToken());
tlsClientConfig.setPort(getPort());
tlsClientConfig.setKeyStore(KEYSTORE + getKeyStoreType().toLowerCase());
tlsClientConfig.setKeyStoreType(getKeyStoreType());
tlsClientConfig.setTrustStore(TRUSTSTORE + getKeyStoreType().toLowerCase());
tlsClientConfig.setTrustStoreType(getKeyStoreType());
tlsClientConfig.setKeySize(getKeySize());
tlsClientConfig.setKeyPairAlgorithm(getKeyAlgorithm());
tlsClientConfig.setSigningAlgorithm(getSigningAlgorithm());
return tlsClientConfig;
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.nifi.toolkit.tls.service.client;
import org.apache.http.HttpHost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.protocol.HttpContext;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* Socket Factory validates that it is talking to a RootCa claiming to have the given hostname. It adds the certificate
* to a list for later validation against the payload's hmac
*/
public class TlsCertificateAuthorityClientSocketFactory extends SSLConnectionSocketFactory {
private final String caHostname;
private final List<X509Certificate> certificates;
public TlsCertificateAuthorityClientSocketFactory(SSLContext sslContext, String caHostname, List<X509Certificate> certificates) {
super(sslContext);
this.caHostname = caHostname;
this.certificates = certificates;
}
@Override
public synchronized Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress,
InetSocketAddress localAddress, HttpContext context) throws IOException {
Socket result = super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
if (!SSLSocket.class.isInstance(result)) {
throw new IOException("Expected tls socket");
}
SSLSocket sslSocket = (SSLSocket) result;
java.security.cert.Certificate[] peerCertificateChain = sslSocket.getSession().getPeerCertificates();
if (peerCertificateChain.length != 1) {
throw new IOException("Expected root ca cert");
}
if (!X509Certificate.class.isInstance(peerCertificateChain[0])) {
throw new IOException("Expected root ca cert in X509 format");
}
String cn;
try {
X509Certificate certificate = (X509Certificate) peerCertificateChain[0];
cn = IETFUtils.valueToString(new JcaX509CertificateHolder(certificate).getSubject().getRDNs(BCStyle.CN)[0].getFirst().getValue());
certificates.add(certificate);
} catch (Exception e) {
throw new IOException(e);
}
if (!caHostname.equals(cn)) {
throw new IOException("Expected cn of " + caHostname + " but got " + cn);
}
return result;
}
}

View File

@ -0,0 +1,156 @@
/*
* 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.nifi.toolkit.tls.service.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BoundedInputStream;
import org.apache.http.HttpHost;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityRequest;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityResponse;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.eclipse.jetty.server.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class TlsCertificateSigningRequestPerformer {
public static final String RECEIVED_RESPONSE_CODE = "Received response code ";
public static final String EXPECTED_ONE_CERTIFICATE = "Expected one certificate";
public static final String EXPECTED_RESPONSE_TO_CONTAIN_HMAC = "Expected response to contain hmac";
public static final String UNEXPECTED_HMAC_RECEIVED_POSSIBLE_MAN_IN_THE_MIDDLE = "Unexpected hmac received, possible man in the middle";
public static final String EXPECTED_RESPONSE_TO_CONTAIN_CERTIFICATE = "Expected response to contain certificate";
private final Logger logger = LoggerFactory.getLogger(TlsCertificateSigningRequestPerformer.class);
private final Supplier<HttpClientBuilder> httpClientBuilderSupplier;
private final String caHostname;
private final String dn;
private final String token;
private final int port;
private final ObjectMapper objectMapper;
private final String signingAlgorithm;
public TlsCertificateSigningRequestPerformer(TlsClientConfig tlsClientConfig) throws NoSuchAlgorithmException {
this(HttpClientBuilder::create, tlsClientConfig.getCaHostname(), tlsClientConfig.getDn(), tlsClientConfig.getToken(), tlsClientConfig.getPort(), tlsClientConfig.getSigningAlgorithm());
}
protected TlsCertificateSigningRequestPerformer(Supplier<HttpClientBuilder> httpClientBuilderSupplier, TlsClientConfig tlsClientConfig) throws NoSuchAlgorithmException {
this(httpClientBuilderSupplier, tlsClientConfig.getCaHostname(), tlsClientConfig.getDn(), tlsClientConfig.getToken(), tlsClientConfig.getPort(), tlsClientConfig.getSigningAlgorithm());
}
private TlsCertificateSigningRequestPerformer(Supplier<HttpClientBuilder> httpClientBuilderSupplier, String caHostname, String dn, String token, int port, String signingAlgorithm) {
this.httpClientBuilderSupplier = httpClientBuilderSupplier;
this.caHostname = caHostname;
this.dn = dn;
this.token = token;
this.port = port;
this.objectMapper = new ObjectMapper();
this.signingAlgorithm = signingAlgorithm;
}
/**
* Submits a CSR to the Certificate authority, checks the resulting hmac, and returns the chain if everything succeeds
*
* @param keyPair the keypair to generate the csr for
* @throws IOException if there is a problem during the process
* @returnd the resulting certificate chain
*/
public X509Certificate[] perform(KeyPair keyPair) throws IOException {
try {
List<X509Certificate> certificates = new ArrayList<>();
HttpClientBuilder httpClientBuilder = httpClientBuilderSupplier.get();
SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
sslContextBuilder.useProtocol("TLSv1.2");
// We will be validating that we are talking to the correct host once we get the response's hmac of the token and public key of the ca
sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
httpClientBuilder.setSSLSocketFactory(new TlsCertificateAuthorityClientSocketFactory(sslContextBuilder.build(), caHostname, certificates));
String jsonResponseString;
int responseCode;
try (CloseableHttpClient client = httpClientBuilder.build()) {
JcaPKCS10CertificationRequest request = TlsHelper.generateCertificationRequest(dn, keyPair, signingAlgorithm);
TlsCertificateAuthorityRequest tlsCertificateAuthorityRequest = new TlsCertificateAuthorityRequest(TlsHelper.calculateHMac(token, request.getPublicKey()),
TlsHelper.pemEncodeJcaObject(request));
HttpPost httpPost = new HttpPost();
httpPost.setEntity(new ByteArrayEntity(objectMapper.writeValueAsBytes(tlsCertificateAuthorityRequest)));
if (logger.isInfoEnabled()) {
logger.info("Requesting certificate with dn " + dn + " from " + caHostname + ":" + port);
}
try (CloseableHttpResponse response = client.execute(new HttpHost(caHostname, port, "https"), httpPost)) {
jsonResponseString = IOUtils.toString(new BoundedInputStream(response.getEntity().getContent(), 1024 * 1024), StandardCharsets.UTF_8);
responseCode = response.getStatusLine().getStatusCode();
}
}
if (responseCode != Response.SC_OK) {
throw new IOException(RECEIVED_RESPONSE_CODE + responseCode + " with payload " + jsonResponseString);
}
if (certificates.size() != 1) {
throw new IOException(EXPECTED_ONE_CERTIFICATE);
}
TlsCertificateAuthorityResponse tlsCertificateAuthorityResponse = objectMapper.readValue(jsonResponseString, TlsCertificateAuthorityResponse.class);
if (!tlsCertificateAuthorityResponse.hasHmac()) {
throw new IOException(EXPECTED_RESPONSE_TO_CONTAIN_HMAC);
}
X509Certificate caCertificate = certificates.get(0);
byte[] expectedHmac = TlsHelper.calculateHMac(token, caCertificate.getPublicKey());
if (!MessageDigest.isEqual(expectedHmac, tlsCertificateAuthorityResponse.getHmac())) {
throw new IOException(UNEXPECTED_HMAC_RECEIVED_POSSIBLE_MAN_IN_THE_MIDDLE);
}
if (!tlsCertificateAuthorityResponse.hasCertificate()) {
throw new IOException(EXPECTED_RESPONSE_TO_CONTAIN_CERTIFICATE);
}
X509Certificate x509Certificate = TlsHelper.parseCertificate(tlsCertificateAuthorityResponse.getPemEncodedCertificate());
x509Certificate.verify(caCertificate.getPublicKey());
if (logger.isInfoEnabled()) {
logger.info("Got certificate with dn " + x509Certificate.getSubjectDN());
}
return new X509Certificate[]{x509Certificate, caCertificate};
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e);
}
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.nifi.toolkit.tls.service.dto;
import org.apache.nifi.util.StringUtils;
public class TlsCertificateAuthorityRequest {
private byte[] hmac;
private String csr;
public TlsCertificateAuthorityRequest() {
}
public TlsCertificateAuthorityRequest(byte[] hmac, String csr) {
this.hmac = hmac;
this.csr = csr;
}
public byte[] getHmac() {
return hmac;
}
public void setHmac(byte[] hmac) {
this.hmac = hmac;
}
public boolean hasHmac() {
return hmac != null && hmac.length > 0;
}
public String getCsr() {
return csr;
}
public void setCsr(String csr) {
this.csr = csr;
}
public boolean hasCsr() {
return !StringUtils.isEmpty(csr);
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.nifi.toolkit.tls.service.dto;
import org.apache.nifi.util.StringUtils;
public class TlsCertificateAuthorityResponse {
private byte[] hmac;
private String pemEncodedCertificate;
private String error;
public TlsCertificateAuthorityResponse() {
}
public TlsCertificateAuthorityResponse(byte[] hmac, String pemEncodedCertificate) {
this.hmac = hmac;
this.pemEncodedCertificate = pemEncodedCertificate;
}
public TlsCertificateAuthorityResponse(String error) {
this.error = error;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public byte[] getHmac() {
return hmac;
}
public void setHmac(byte[] hmac) {
this.hmac = hmac;
}
public String getPemEncodedCertificate() {
return pemEncodedCertificate;
}
public void setPemEncodedCertificate(String pemEncodedCertificate) {
this.pemEncodedCertificate = pemEncodedCertificate;
}
public boolean hasCertificate() {
return !StringUtils.isEmpty(pemEncodedCertificate);
}
public boolean hasHmac() {
return hmac != null && hmac.length > 0;
}
}

View File

@ -0,0 +1,126 @@
/*
* 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.nifi.toolkit.tls.service.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.manager.TlsCertificateAuthorityManager;
import org.apache.nifi.toolkit.tls.manager.writer.JsonConfigurationWriter;
import org.apache.nifi.toolkit.tls.service.BaseCertificateAuthorityCommandLine;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
/**
* Starts a Jetty server that will either load an existing CA or create one and use it to sign CSRs
*/
public class TlsCertificateAuthorityService {
private final Logger logger = LoggerFactory.getLogger(TlsCertificateAuthorityService.class);
private final OutputStreamFactory outputStreamFactory;
private Server server;
public TlsCertificateAuthorityService() {
this(FileOutputStream::new);
}
public TlsCertificateAuthorityService(OutputStreamFactory outputStreamFactory) {
this.outputStreamFactory = outputStreamFactory;
}
private static Server createServer(Handler handler, int port, KeyStore keyStore, String keyPassword) throws Exception {
Server server = new Server();
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setIncludeProtocols("TLSv1.2");
sslContextFactory.setKeyStore(keyStore);
sslContextFactory.setKeyManagerPassword(keyPassword);
HttpConfiguration httpsConfig = new HttpConfiguration();
httpsConfig.addCustomizer(new SecureRequestCustomizer());
ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig));
sslConnector.setPort(port);
server.addConnector(sslConnector);
server.setHandler(handler);
return server;
}
public synchronized void start(TlsConfig tlsConfig, String configJson, boolean differentPasswordsForKeyAndKeystore) throws Exception {
if (server != null) {
throw new IllegalStateException("Server already started");
}
ObjectMapper objectMapper = new ObjectMapper();
TlsCertificateAuthorityManager tlsManager;
try {
tlsManager = new TlsCertificateAuthorityManager(tlsConfig);
tlsManager.setDifferentKeyAndKeyStorePassword(differentPasswordsForKeyAndKeystore);
} catch (IOException e) {
logger.error("Unable to open existing keystore, it can be reused by specifiying both " + BaseCertificateAuthorityCommandLine.CONFIG_JSON_ARG + " and " +
BaseCertificateAuthorityCommandLine.USE_CONFIG_JSON_ARG);
throw e;
}
tlsManager.addConfigurationWriter(new JsonConfigurationWriter<>(objectMapper, outputStreamFactory, new File(configJson)));
KeyStore.PrivateKeyEntry privateKeyEntry = tlsManager.getOrGenerateCertificateAuthority();
KeyPair keyPair = new KeyPair(privateKeyEntry.getCertificate().getPublicKey(), privateKeyEntry.getPrivateKey());
Certificate[] certificateChain = privateKeyEntry.getCertificateChain();
if (certificateChain.length != 1) {
throw new IOException("Expected root ca cert to be only certificate in chain");
}
Certificate certificate = certificateChain[0];
X509Certificate caCert;
if (certificate instanceof X509Certificate) {
caCert = (X509Certificate) certificate;
} else {
throw new IOException("Expected " + X509Certificate.class + " as root ca cert");
}
tlsManager.write(outputStreamFactory);
String signingAlgorithm = tlsConfig.getSigningAlgorithm();
int days = tlsConfig.getDays();
server = createServer(new TlsCertificateAuthorityServiceHandler(signingAlgorithm, days, tlsConfig.getToken(), caCert, keyPair, objectMapper), tlsConfig.getPort(), tlsManager.getKeyStore(),
tlsConfig.getKeyPassword());
server.start();
}
public synchronized void shutdown() throws Exception {
if (server == null) {
throw new IllegalStateException("Server already shutdown");
}
server.stop();
server.join();
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.nifi.toolkit.tls.service.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.service.BaseCertificateAuthorityCommandLine;
import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.apache.nifi.util.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class TlsCertificateAuthorityServiceCommandLine extends BaseCertificateAuthorityCommandLine {
public static final String DESCRIPTION = "Acts as a Certificate Authority that can be used by clients to get Certificates";
public static final String NIFI_CA_KEYSTORE = "nifi-ca-" + KEYSTORE;
private final InputStreamFactory inputStreamFactory;
public TlsCertificateAuthorityServiceCommandLine() {
this(FileInputStream::new);
}
public TlsCertificateAuthorityServiceCommandLine(InputStreamFactory inputStreamFactory) {
super(DESCRIPTION);
this.inputStreamFactory = inputStreamFactory;
}
public static void main(String[] args) throws Exception {
TlsHelper.addBouncyCastleProvider();
TlsCertificateAuthorityServiceCommandLine tlsCertificateAuthorityServiceCommandLine = new TlsCertificateAuthorityServiceCommandLine();
try {
tlsCertificateAuthorityServiceCommandLine.parse(args);
} catch (CommandLineParseException e) {
System.exit(e.getExitCode());
}
TlsCertificateAuthorityService tlsCertificateAuthorityService = new TlsCertificateAuthorityService();
tlsCertificateAuthorityService.start(tlsCertificateAuthorityServiceCommandLine.createConfig(), tlsCertificateAuthorityServiceCommandLine.getConfigJson(),
tlsCertificateAuthorityServiceCommandLine.differentPasswordForKeyAndKeystore());
System.out.println("Server Started");
System.out.flush();
}
public TlsConfig createConfig() throws IOException {
if (onlyUseConfigJson()) {
try (InputStream inputStream = inputStreamFactory.create(new File(getConfigJson()))) {
TlsConfig tlsConfig = new ObjectMapper().readValue(inputStream, TlsConfig.class);
tlsConfig.initDefaults();
return tlsConfig;
}
} else {
TlsConfig tlsConfig = new TlsConfig();
tlsConfig.setCaHostname(getCertificateAuthorityHostname());
tlsConfig.setDn(getDn());
tlsConfig.setToken(getToken());
tlsConfig.setPort(getPort());
tlsConfig.setKeyStore(NIFI_CA_KEYSTORE + getKeyStoreType().toLowerCase());
tlsConfig.setKeyStoreType(getKeyStoreType());
tlsConfig.setKeySize(getKeySize());
tlsConfig.setKeyPairAlgorithm(getKeyAlgorithm());
tlsConfig.setSigningAlgorithm(getSigningAlgorithm());
tlsConfig.setDays(getDays());
return tlsConfig;
}
}
@Override
protected String getTokenDescription() {
return "The token to use to prevent MITM (required and must be same as one used by clients)";
}
@Override
protected String getDnDescription() {
return "The dn to use for the CA certificate";
}
@Override
protected String getPortDescription() {
return "The port for the Certificate Authority to listen on";
}
@Override
protected String getDnHostname() {
String dnHostname = getCertificateAuthorityHostname();
if (StringUtils.isEmpty(dnHostname)) {
return "YOUR_CA_HOSTNAME";
}
return dnHostname;
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.nifi.toolkit.tls.service.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.input.BoundedReader;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityRequest;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityResponse;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
/**
* Jetty service handler that validates the hmac of a CSR and issues a certificate if it checks out
*/
public class TlsCertificateAuthorityServiceHandler extends AbstractHandler {
public static final String CSR_FIELD_MUST_BE_SET = "csr field must be set";
public static final String HMAC_FIELD_MUST_BE_SET = "hmac field must be set";
public static final String FORBIDDEN = "forbidden";
private final Logger logger = LoggerFactory.getLogger(TlsCertificateAuthorityServiceHandler.class);
private final String signingAlgorithm;
private final int days;
private final String token;
private final X509Certificate caCert;
private final KeyPair keyPair;
private final ObjectMapper objectMapper;
public TlsCertificateAuthorityServiceHandler(String signingAlgorithm, int days, String token, X509Certificate caCert, KeyPair keyPair, ObjectMapper objectMapper) {
this.signingAlgorithm = signingAlgorithm;
this.days = days;
this.token = token;
this.caCert = caCert;
this.keyPair = keyPair;
this.objectMapper = objectMapper;
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
try {
TlsCertificateAuthorityRequest tlsCertificateAuthorityRequest = objectMapper.readValue(new BoundedReader(request.getReader(), 1024 * 1024), TlsCertificateAuthorityRequest.class);
if (!tlsCertificateAuthorityRequest.hasHmac()) {
writeResponse(objectMapper, request, response, new TlsCertificateAuthorityResponse(HMAC_FIELD_MUST_BE_SET), Response.SC_BAD_REQUEST);
return;
}
if (!tlsCertificateAuthorityRequest.hasCsr()) {
writeResponse(objectMapper, request, response, new TlsCertificateAuthorityResponse(CSR_FIELD_MUST_BE_SET), Response.SC_BAD_REQUEST);
return;
}
JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest = TlsHelper.parseCsr(tlsCertificateAuthorityRequest.getCsr());
byte[] expectedHmac = TlsHelper.calculateHMac(token, jcaPKCS10CertificationRequest.getPublicKey());
if (MessageDigest.isEqual(expectedHmac, tlsCertificateAuthorityRequest.getHmac())) {
X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(jcaPKCS10CertificationRequest.getSubject().toString(),
jcaPKCS10CertificationRequest.getPublicKey(), caCert, keyPair, signingAlgorithm, days);
writeResponse(objectMapper, request, response, new TlsCertificateAuthorityResponse(TlsHelper.calculateHMac(token, caCert.getPublicKey()),
TlsHelper.pemEncodeJcaObject(x509Certificate)), Response.SC_OK);
return;
} else {
writeResponse(objectMapper, request, response, new TlsCertificateAuthorityResponse(FORBIDDEN), Response.SC_FORBIDDEN);
return;
}
} catch (Exception e) {
throw new ServletException("Server error");
} finally {
baseRequest.setHandled(true);
}
}
private void writeResponse(ObjectMapper objectMapper, HttpServletRequest request, HttpServletResponse response, TlsCertificateAuthorityResponse tlsCertificateAuthorityResponse,
int responseCode) throws IOException {
if (logger.isInfoEnabled()) {
logger.info(new StringBuilder("Returning code:").append(responseCode).append(" payload ").append(objectMapper.writeValueAsString(tlsCertificateAuthorityResponse))
.append(" to ").append(request.getRemoteHost()).toString());
}
if (responseCode == Response.SC_OK) {
objectMapper.writeValue(response.getWriter(), tlsCertificateAuthorityResponse);
response.setStatus(responseCode);
} else {
response.setStatus(responseCode);
response.setContentType("application/json");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
objectMapper.writeValue(response.getWriter(), tlsCertificateAuthorityResponse);
}
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.nifi.toolkit.tls.standalone;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.manager.TlsCertificateAuthorityManager;
import org.apache.nifi.toolkit.tls.manager.TlsClientManager;
import org.apache.nifi.toolkit.tls.manager.writer.NifiPropertiesTlsClientConfigWriter;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.List;
public class TlsToolkitStandalone {
public static final String NIFI_KEY = "nifi-key";
public static final String NIFI_CERT = "nifi-cert";
public static final String NIFI_PROPERTIES = "nifi.properties";
private final Logger logger = LoggerFactory.getLogger(TlsToolkitStandalone.class);
private final OutputStreamFactory outputStreamFactory;
public TlsToolkitStandalone() {
this(FileOutputStream::new);
}
public TlsToolkitStandalone(OutputStreamFactory outputStreamFactory) {
this.outputStreamFactory = outputStreamFactory;
}
public void createNifiKeystoresAndTrustStores(File baseDir, TlsConfig tlsConfig, NiFiPropertiesWriterFactory niFiPropertiesWriterFactory, List<String> hostnames, List<String> keyStorePasswords,
List<String> keyPasswords, List<String> trustStorePasswords, String httpsPort) throws GeneralSecurityException, IOException {
String signingAlgorithm = tlsConfig.getSigningAlgorithm();
int days = tlsConfig.getDays();
String keyPairAlgorithm = tlsConfig.getKeyPairAlgorithm();
int keySize = tlsConfig.getKeySize();
TlsCertificateAuthorityManager tlsCertificateAuthorityManager = new TlsCertificateAuthorityManager(tlsConfig);
KeyStore.PrivateKeyEntry privateKeyEntry = tlsCertificateAuthorityManager.getOrGenerateCertificateAuthority();
X509Certificate certificate = (X509Certificate) privateKeyEntry.getCertificateChain()[0];
KeyPair caKeyPair = new KeyPair(certificate.getPublicKey(), privateKeyEntry.getPrivateKey());
if (!baseDir.exists() && !baseDir.mkdirs()) {
throw new IOException(baseDir + " doesn't exist and unable to create it.");
}
if (!baseDir.isDirectory()) {
throw new IOException("Expected directory to output to");
}
if (logger.isInfoEnabled()) {
logger.info("Running standalone certificate generation with output directory " + baseDir + " and hostnames " + hostnames);
}
File nifiCert = new File(baseDir, NIFI_CERT + ".pem");
if (nifiCert.exists()) {
throw new IOException(nifiCert.getAbsolutePath() + " exists already.");
}
File nifiKey = new File(baseDir, NIFI_KEY + ".key");
if (nifiKey.exists()) {
throw new IOException(nifiKey.getAbsolutePath() + " exists already.");
}
for (String hostname : hostnames) {
File hostDirectory = new File(baseDir, hostname);
if (hostDirectory.exists()) {
throw new IOException("Output destination for host " + hostname + " (" + hostDirectory.getAbsolutePath() + ") exists already.");
}
}
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiCert)))) {
pemWriter.writeObject(new JcaMiscPEMGenerator(certificate));
}
try (PemWriter pemWriter = new PemWriter(new OutputStreamWriter(outputStreamFactory.create(nifiKey)))) {
pemWriter.writeObject(new JcaMiscPEMGenerator(caKeyPair));
}
for (int i = 0; i < hostnames.size(); i++) {
String hostname = hostnames.get(i);
File hostDir = new File(baseDir, hostname);
if (!hostDir.mkdirs()) {
throw new IOException("Unable to make directory: " + hostDir.getAbsolutePath());
}
TlsClientConfig tlsClientConfig = new TlsClientConfig(tlsConfig);
tlsClientConfig.setKeyStore(new File(hostDir, "keystore." + tlsClientConfig.getKeyStoreType().toLowerCase()).getAbsolutePath());
tlsClientConfig.setKeyStorePassword(keyStorePasswords.get(i));
tlsClientConfig.setKeyPassword(keyPasswords.get(i));
tlsClientConfig.setTrustStore(new File(hostDir, "truststore." + tlsClientConfig.getTrustStoreType().toLowerCase()).getAbsolutePath());
tlsClientConfig.setTrustStorePassword(trustStorePasswords.get(i));
TlsClientManager tlsClientManager = new TlsClientManager(tlsClientConfig);
KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
tlsClientManager.addPrivateKeyToKeyStore(keyPair, NIFI_KEY, CertificateUtils.generateIssuedCertificate(TlsConfig.calcDefaultDn(hostname),
keyPair.getPublic(), certificate, caKeyPair, signingAlgorithm, days), certificate);
tlsClientManager.setCertificateEntry(NIFI_CERT, certificate);
tlsClientManager.addClientConfigurationWriter(new NifiPropertiesTlsClientConfigWriter(niFiPropertiesWriterFactory, outputStreamFactory, new File(hostDir, "nifi.properties"),
hostname, httpsPort));
tlsClientManager.write(outputStreamFactory);
}
if (logger.isInfoEnabled()) {
logger.info("Successfully generated TLS configuration");
}
}
}

View File

@ -0,0 +1,183 @@
/*
* 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.nifi.toolkit.tls.standalone;
import org.apache.commons.cli.CommandLine;
import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriterFactory;
import org.apache.nifi.toolkit.tls.util.PasswordUtil;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.apache.nifi.util.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class TlsToolkitStandaloneCommandLine extends BaseCommandLine {
public static final String OUTPUT_DIRECTORY_ARG = "outputDirectory";
public static final String NIFI_PROPERTIES_FILE_ARG = "nifiPropertiesFile";
public static final String KEY_STORE_PASSWORD_ARG = "keyStorePassword";
public static final String TRUST_STORE_PASSWORD_ARG = "trustStorePassword";
public static final String KEY_PASSWORD_ARG = "keyPassword";
public static final String HOSTNAMES_ARG = "hostnames";
public static final String HTTPS_PORT_ARG = "httpsPort";
public static final String DEFAULT_OUTPUT_DIRECTORY = new File(".").getPath();
public static final String DESCRIPTION = "Creates certificates and config files for nifi cluster.";
private final PasswordUtil passwordUtil;
private File baseDir;
private List<String> hostnames;
private String httpsPort;
private NiFiPropertiesWriterFactory niFiPropertiesWriterFactory;
private List<String> keyStorePasswords;
private List<String> keyPasswords;
private List<String> trustStorePasswords;
public TlsToolkitStandaloneCommandLine() {
this(new PasswordUtil());
}
protected TlsToolkitStandaloneCommandLine(PasswordUtil passwordUtil) {
super(DESCRIPTION);
this.passwordUtil = passwordUtil;
addOptionWithArg("o", OUTPUT_DIRECTORY_ARG, "The directory to output keystores, truststore, config files.", DEFAULT_OUTPUT_DIRECTORY);
addOptionWithArg("n", HOSTNAMES_ARG, "Comma separated list of hostnames.", TlsConfig.DEFAULT_HOSTNAME);
addOptionWithArg("p", HTTPS_PORT_ARG, "Https port to use.", "");
addOptionWithArg("f", NIFI_PROPERTIES_FILE_ARG, "Base nifi.properties file to update.", "");
addOptionWithArg("S", KEY_STORE_PASSWORD_ARG, "Keystore password to use. Must either be one value or one for each host. (autogenerate if not specified)");
addOptionWithArg("K", KEY_PASSWORD_ARG, "Key password to use. Must either be one value or one for each host. (autogenerate if not specified)");
addOptionWithArg("P", TRUST_STORE_PASSWORD_ARG, "Keystore password to use. Must either be one value or one for each host. (autogenerate if not specified)");
}
public static void main(String[] args) {
TlsHelper.addBouncyCastleProvider();
TlsToolkitStandaloneCommandLine tlsToolkitStandaloneCommandLine = new TlsToolkitStandaloneCommandLine();
try {
tlsToolkitStandaloneCommandLine.parse(args);
} catch (CommandLineParseException e) {
System.exit(e.getExitCode());
}
try {
new TlsToolkitStandalone().createNifiKeystoresAndTrustStores(tlsToolkitStandaloneCommandLine.getBaseDir(), tlsToolkitStandaloneCommandLine.createConfig(),
tlsToolkitStandaloneCommandLine.getNiFiPropertiesWriterFactory(), tlsToolkitStandaloneCommandLine.getHostnames(), tlsToolkitStandaloneCommandLine.getKeyStorePasswords(),
tlsToolkitStandaloneCommandLine.getKeyPasswords(), tlsToolkitStandaloneCommandLine.getTrustStorePasswords(), tlsToolkitStandaloneCommandLine.getHttpsPort());
} catch (Exception e) {
tlsToolkitStandaloneCommandLine.printUsage("Error creating generating tls configuration. (" + e.getMessage() + ")");
System.exit(ExitCode.ERROR_GENERATING_CONFIG.ordinal());
}
System.exit(ExitCode.SUCCESS.ordinal());
}
@Override
protected CommandLine doParse(String... args) throws CommandLineParseException {
CommandLine commandLine = super.doParse(args);
String outputDirectory = commandLine.getOptionValue(OUTPUT_DIRECTORY_ARG, DEFAULT_OUTPUT_DIRECTORY);
baseDir = new File(outputDirectory);
hostnames = Arrays.stream(commandLine.getOptionValue(HOSTNAMES_ARG, TlsConfig.DEFAULT_HOSTNAME).split(",")).map(String::trim).collect(Collectors.toList());
httpsPort = commandLine.getOptionValue(HTTPS_PORT_ARG, "");
int numHosts = hostnames.size();
keyStorePasswords = Collections.unmodifiableList(getPasswords(KEY_STORE_PASSWORD_ARG, commandLine, numHosts));
keyPasswords = Collections.unmodifiableList(getKeyPasswords(commandLine, keyStorePasswords));
trustStorePasswords = Collections.unmodifiableList(getPasswords(TRUST_STORE_PASSWORD_ARG, commandLine, numHosts));
String nifiPropertiesFile = commandLine.getOptionValue(NIFI_PROPERTIES_FILE_ARG, "");
try {
if (StringUtils.isEmpty(nifiPropertiesFile)) {
niFiPropertiesWriterFactory = new NiFiPropertiesWriterFactory();
} else {
niFiPropertiesWriterFactory = new NiFiPropertiesWriterFactory(new FileInputStream(nifiPropertiesFile));
}
} catch (IOException e) {
printUsageAndThrow("Unable to read nifi.properties from " + (StringUtils.isEmpty(nifiPropertiesFile) ? "classpath" : nifiPropertiesFile), ExitCode.ERROR_READING_NIFI_PROPERTIES);
}
return commandLine;
}
private List<String> getPasswords(String arg, CommandLine commandLine, int numHosts) throws CommandLineParseException {
String[] optionValues = commandLine.getOptionValues(arg);
if (optionValues == null) {
return IntStream.range(0, numHosts).mapToObj(operand -> passwordUtil.generatePassword()).collect(Collectors.toList());
}
if (optionValues.length == 1) {
return IntStream.range(0, numHosts).mapToObj(value -> optionValues[0]).collect(Collectors.toList());
} else if (optionValues.length == numHosts) {
return Arrays.stream(optionValues).collect(Collectors.toList());
}
return printUsageAndThrow("Expected either 1 value or " + numHosts + " (the number of hostnames) values for " + arg, ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS);
}
private List<String> getKeyPasswords(CommandLine commandLine, List<String> keyStorePasswords) throws CommandLineParseException {
if (differentPasswordForKeyAndKeystore() || commandLine.hasOption(KEY_PASSWORD_ARG)) {
return getPasswords(KEY_PASSWORD_ARG, commandLine, keyStorePasswords.size());
}
return new ArrayList<>(keyStorePasswords);
}
public File getBaseDir() {
return baseDir;
}
public List<String> getHostnames() {
return hostnames;
}
public String getHttpsPort() {
return httpsPort;
}
public NiFiPropertiesWriterFactory getNiFiPropertiesWriterFactory() {
return niFiPropertiesWriterFactory;
}
public List<String> getKeyStorePasswords() {
return keyStorePasswords;
}
public List<String> getKeyPasswords() {
return keyPasswords;
}
public List<String> getTrustStorePasswords() {
return trustStorePasswords;
}
public TlsConfig createConfig() throws IOException {
TlsConfig tlsConfig = new TlsConfig();
tlsConfig.setCaHostname(getCertificateAuthorityHostname());
tlsConfig.setKeyStore("nifi-ca-" + KEYSTORE + getKeyStoreType().toLowerCase());
tlsConfig.setKeyStoreType(getKeyStoreType());
tlsConfig.setKeySize(getKeySize());
tlsConfig.setKeyPairAlgorithm(getKeyAlgorithm());
tlsConfig.setSigningAlgorithm(getSigningAlgorithm());
tlsConfig.setDays(getDays());
tlsConfig.initDefaults();
return tlsConfig;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.nifi.toolkit.tls.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
public interface InputStreamFactory {
InputStream create(File file) throws FileNotFoundException;
}

View File

@ -0,0 +1,26 @@
/*
* 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.nifi.toolkit.tls.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.OutputStream;
public interface OutputStreamFactory {
OutputStream create(File file) throws FileNotFoundException;
}

View File

@ -0,0 +1,43 @@
/*
* 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.nifi.toolkit.tls.util;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Base64;
public class PasswordUtil {
private final SecureRandom secureRandom;
public PasswordUtil() {
this(new SecureRandom());
}
public PasswordUtil(SecureRandom secureRandom) {
this.secureRandom = secureRandom;
}
public String generatePassword() {
// [see http://stackoverflow.com/questions/41107/how-to-generate-a-random-alpha-numeric-string#answer-41156]
String string = Base64.getEncoder().encodeToString(new BigInteger(256, secureRandom).toByteArray());
while (string.endsWith("=")) {
string = string.substring(0, string.length() - 1);
}
return string;
}
}

View File

@ -0,0 +1,112 @@
/*
* 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.nifi.toolkit.tls.util;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemWriter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class TlsHelper {
private TlsHelper() {
}
public static void addBouncyCastleProvider() {
Security.addProvider(new BouncyCastleProvider());
}
private static KeyPairGenerator createKeyPairGenerator(String algorithm, int keySize) throws NoSuchAlgorithmException {
KeyPairGenerator instance = KeyPairGenerator.getInstance(algorithm);
instance.initialize(keySize);
return instance;
}
public static byte[] calculateHMac(String token, PublicKey publicKey) throws GeneralSecurityException {
SecretKeySpec keySpec = new SecretKeySpec(token.getBytes(StandardCharsets.UTF_8), "RAW");
Mac mac = Mac.getInstance("Hmac-SHA256", BouncyCastleProvider.PROVIDER_NAME);
mac.init(keySpec);
return mac.doFinal(getKeyIdentifier(publicKey));
}
public static byte[] getKeyIdentifier(PublicKey publicKey) throws NoSuchAlgorithmException {
return new JcaX509ExtensionUtils().createSubjectKeyIdentifier(publicKey).getKeyIdentifier();
}
public static String pemEncodeJcaObject(Object object) throws IOException {
StringWriter writer = new StringWriter();
try (PemWriter pemWriter = new PemWriter(writer)) {
pemWriter.writeObject(new JcaMiscPEMGenerator(object));
}
return writer.toString();
}
public static JcaPKCS10CertificationRequest parseCsr(String pemEncodedCsr) throws IOException {
try (PEMParser pemParser = new PEMParser(new StringReader(pemEncodedCsr))) {
Object o = pemParser.readObject();
if (!PKCS10CertificationRequest.class.isInstance(o)) {
throw new IOException("Expecting instance of " + PKCS10CertificationRequest.class + " but got " + o);
}
return new JcaPKCS10CertificationRequest((PKCS10CertificationRequest) o);
}
}
public static X509Certificate parseCertificate(String pemEncodedCertificate) throws IOException, CertificateException {
try (PEMParser pemParser = new PEMParser(new StringReader(pemEncodedCertificate))) {
Object object = pemParser.readObject();
if (!X509CertificateHolder.class.isInstance(object)) {
throw new IOException("Expected " + X509CertificateHolder.class);
}
return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate((X509CertificateHolder) object);
}
}
public static KeyPair generateKeyPair(String algorithm, int keySize) throws NoSuchAlgorithmException {
return createKeyPairGenerator(algorithm, keySize).generateKeyPair();
}
public static JcaPKCS10CertificationRequest generateCertificationRequest(String requestedDn, KeyPair keyPair, String signingAlgorithm) throws OperatorCreationException {
JcaPKCS10CertificationRequestBuilder jcaPKCS10CertificationRequestBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Principal(requestedDn), keyPair.getPublic());
JcaContentSignerBuilder jcaContentSignerBuilder = new JcaContentSignerBuilder(signingAlgorithm);
return new JcaPKCS10CertificationRequest(jcaPKCS10CertificationRequestBuilder.build(jcaContentSignerBuilder.build(keyPair.getPrivate())));
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.nifi.toolkit.tls.properties;
import org.apache.nifi.util.NiFiProperties;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import static org.junit.Assert.assertEquals;
public class NifiPropertiesWriterTest {
private static NiFiPropertiesWriterFactory nifiPropertiesWriterFactory;
private NiFiPropertiesWriter niFiPropertiesWriter;
@BeforeClass
public static void beforeClass() throws IOException {
nifiPropertiesWriterFactory = new NiFiPropertiesWriterFactory();
}
@Before
public void setup() throws IOException {
niFiPropertiesWriter = nifiPropertiesWriterFactory.create();
}
@Test
public void testCanUpdateAllKeys() throws IllegalAccessException, IOException {
String testValue = NifiPropertiesWriterTest.class.getCanonicalName();
// Get all property keys from NiFiProperties and set them to testValue
Set<String> propertyKeys = new HashSet<>();
for (Field field : NiFiProperties.class.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers) && field.getType() == String.class) {
if (!field.getName().toLowerCase().startsWith("default")) {
String fieldName = (String) field.get(null);
propertyKeys.add(fieldName);
niFiPropertiesWriter.setPropertyValue(fieldName, testValue);
}
}
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
niFiPropertiesWriter.writeNiFiProperties(outputStream);
// Verify operation worked
Properties properties = new Properties();
properties.load(new ByteArrayInputStream(outputStream.toByteArray()));
for (String propertyKey : propertyKeys) {
assertEquals(testValue, properties.getProperty(propertyKey));
}
}
}

View File

@ -0,0 +1,261 @@
/*
* 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.nifi.toolkit.tls.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.service.client.TlsCertificateAuthorityClient;
import org.apache.nifi.toolkit.tls.service.server.TlsCertificateAuthorityService;
import org.apache.nifi.toolkit.tls.standalone.TlsToolkitStandalone;
import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
import org.apache.nifi.toolkit.tls.util.OutputStreamFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.or;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class TlsCertificateAuthorityTest {
private File serverConfigFile;
private File clientConfigFile;
private OutputStreamFactory outputStreamFactory;
private InputStreamFactory inputStreamFactory;
private TlsConfig serverConfig;
private TlsClientConfig clientConfig;
private ObjectMapper objectMapper;
private ByteArrayOutputStream serverKeyStoreOutputStream;
private ByteArrayOutputStream clientKeyStoreOutputStream;
private ByteArrayOutputStream clientTrustStoreOutputStream;
private ByteArrayOutputStream serverConfigFileOutputStream;
private ByteArrayOutputStream clientConfigFileOutputStream;
@BeforeClass
public static void beforeClass() {
Security.addProvider(new BouncyCastleProvider());
}
@Before
public void setup() throws FileNotFoundException {
objectMapper = new ObjectMapper();
serverConfigFile = new File("fake.server.config");
clientConfigFile = new File("fake.client.config");
String serverKeyStore = "serverKeyStore";
String clientKeyStore = "clientKeyStore";
String clientTrustStore = "clientTrustStore";
serverKeyStoreOutputStream = new ByteArrayOutputStream();
clientKeyStoreOutputStream = new ByteArrayOutputStream();
clientTrustStoreOutputStream = new ByteArrayOutputStream();
serverConfigFileOutputStream = new ByteArrayOutputStream();
clientConfigFileOutputStream = new ByteArrayOutputStream();
String myTestTokenUseSomethingStronger = "myTestTokenUseSomethingStronger";
int port = availablePort();
serverConfig = new TlsConfig();
serverConfig.setCaHostname("localhost");
serverConfig.setToken(myTestTokenUseSomethingStronger);
serverConfig.setKeyStore(serverKeyStore);
serverConfig.setPort(port);
serverConfig.setDays(5);
serverConfig.setKeySize(2048);
serverConfig.initDefaults();
clientConfig = new TlsClientConfig();
clientConfig.setCaHostname("localhost");
clientConfig.setDn("CN=otherHostname,OU=NIFI");
clientConfig.setKeyStore(clientKeyStore);
clientConfig.setTrustStore(clientTrustStore);
clientConfig.setToken(myTestTokenUseSomethingStronger);
clientConfig.setPort(port);
clientConfig.setKeySize(2048);
clientConfig.initDefaults();
outputStreamFactory = mock(OutputStreamFactory.class);
mockReturnOutputStream(outputStreamFactory, new File(serverKeyStore), serverKeyStoreOutputStream);
mockReturnOutputStream(outputStreamFactory, new File(clientKeyStore), clientKeyStoreOutputStream);
mockReturnOutputStream(outputStreamFactory, new File(clientTrustStore), clientTrustStoreOutputStream);
mockReturnOutputStream(outputStreamFactory, serverConfigFile, serverConfigFileOutputStream);
mockReturnOutputStream(outputStreamFactory, clientConfigFile, clientConfigFileOutputStream);
inputStreamFactory = mock(InputStreamFactory.class);
mockReturnProperties(inputStreamFactory, serverConfigFile, serverConfig);
mockReturnProperties(inputStreamFactory, clientConfigFile, clientConfig);
}
private void mockReturnProperties(InputStreamFactory inputStreamFactory, File file, TlsConfig tlsConfig) throws FileNotFoundException {
when(inputStreamFactory.create(eq(file))).thenAnswer(invocation -> {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
objectMapper.writeValue(byteArrayOutputStream, tlsConfig);
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
});
}
private void mockReturnOutputStream(OutputStreamFactory outputStreamFactory, File file, OutputStream outputStream) throws FileNotFoundException {
when(outputStreamFactory.create(or(eq(file), eq(new File(file.getAbsolutePath()))))).thenReturn(outputStream).thenReturn(null);
}
@Test
public void testClientGetCertDifferentPasswordsForKeyAndKeyStore() throws Exception {
TlsCertificateAuthorityService tlsCertificateAuthorityService = null;
try {
tlsCertificateAuthorityService = new TlsCertificateAuthorityService(outputStreamFactory);
tlsCertificateAuthorityService.start(serverConfig, serverConfigFile.getAbsolutePath(), true);
TlsCertificateAuthorityClient tlsCertificateAuthorityClient = new TlsCertificateAuthorityClient(outputStreamFactory);
tlsCertificateAuthorityClient.generateCertificateAndGetItSigned(clientConfig, null, clientConfigFile.getAbsolutePath(), true);
validate();
} finally {
if (tlsCertificateAuthorityService != null) {
tlsCertificateAuthorityService.shutdown();
}
}
}
@Test
public void testClientGetCertSamePasswordsForKeyAndKeyStore() throws Exception {
TlsCertificateAuthorityService tlsCertificateAuthorityService = null;
try {
tlsCertificateAuthorityService = new TlsCertificateAuthorityService(outputStreamFactory);
tlsCertificateAuthorityService.start(serverConfig, serverConfigFile.getAbsolutePath(), false);
TlsCertificateAuthorityClient tlsCertificateAuthorityClient = new TlsCertificateAuthorityClient(outputStreamFactory);
tlsCertificateAuthorityClient.generateCertificateAndGetItSigned(clientConfig, null, clientConfigFile.getAbsolutePath(), false);
validate();
} finally {
if (tlsCertificateAuthorityService != null) {
tlsCertificateAuthorityService.shutdown();
}
}
}
@Test
public void testTokenMismatch() throws Exception {
serverConfig.setToken("a different token...");
try {
testClientGetCertSamePasswordsForKeyAndKeyStore();
fail("Expected error with mismatching token");
} catch (IOException e) {
assertTrue(e.getMessage().contains("forbidden"));
}
}
private void validate() throws CertificateException, InvalidKeyException, NoSuchAlgorithmException, KeyStoreException, SignatureException,
NoSuchProviderException, UnrecoverableEntryException, IOException {
Certificate caCertificate = validateServerKeyStore();
validateClient(caCertificate);
}
private Certificate validateServerKeyStore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableEntryException,
InvalidKeyException, NoSuchProviderException, SignatureException {
serverConfig = objectMapper.readValue(new ByteArrayInputStream(serverConfigFileOutputStream.toByteArray()), TlsConfig.class);
KeyStore serverKeyStore = KeyStore.getInstance(serverConfig.getKeyStoreType());
serverKeyStore.load(new ByteArrayInputStream(serverKeyStoreOutputStream.toByteArray()), serverConfig.getKeyStorePassword().toCharArray());
KeyStore.Entry serverKeyEntry = serverKeyStore.getEntry(TlsToolkitStandalone.NIFI_KEY, new KeyStore.PasswordProtection(serverConfig.getKeyPassword().toCharArray()));
assertTrue(serverKeyEntry instanceof KeyStore.PrivateKeyEntry);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) serverKeyEntry;
Certificate[] certificateChain = privateKeyEntry.getCertificateChain();
assertEquals(1, certificateChain.length);
Certificate caCertificate = certificateChain[0];
caCertificate.verify(caCertificate.getPublicKey());
assertPrivateAndPublicKeyMatch(privateKeyEntry.getPrivateKey(), caCertificate.getPublicKey());
return caCertificate;
}
private void validateClient(Certificate caCertificate) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException,
UnrecoverableEntryException, InvalidKeyException, NoSuchProviderException, SignatureException {
clientConfig = objectMapper.readValue(new ByteArrayInputStream(clientConfigFileOutputStream.toByteArray()), TlsClientConfig.class);
KeyStore clientKeyStore = KeyStore.getInstance(clientConfig.getKeyStoreType());
clientKeyStore.load(new ByteArrayInputStream(clientKeyStoreOutputStream.toByteArray()), clientConfig.getKeyStorePassword().toCharArray());
KeyStore.Entry clientKeyStoreEntry = clientKeyStore.getEntry(TlsToolkitStandalone.NIFI_KEY, new KeyStore.PasswordProtection(clientConfig.getKeyPassword().toCharArray()));
assertTrue(clientKeyStoreEntry instanceof KeyStore.PrivateKeyEntry);
KeyStore.PrivateKeyEntry clientPrivateKeyEntry = (KeyStore.PrivateKeyEntry) clientKeyStoreEntry;
Certificate[] certificateChain = clientPrivateKeyEntry.getCertificateChain();
assertEquals(2, certificateChain.length);
assertEquals(caCertificate, certificateChain[1]);
certificateChain[0].verify(caCertificate.getPublicKey());
assertPrivateAndPublicKeyMatch(clientPrivateKeyEntry.getPrivateKey(), certificateChain[0].getPublicKey());
KeyStore clientTrustStore = KeyStore.getInstance(clientConfig.getTrustStoreType());
clientTrustStore.load(new ByteArrayInputStream(clientTrustStoreOutputStream.toByteArray()), clientConfig.getTrustStorePassword().toCharArray());
assertEquals(caCertificate, clientTrustStore.getCertificate(TlsToolkitStandalone.NIFI_CERT));
}
private void assertPrivateAndPublicKeyMatch(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = Signature.getInstance(TlsConfig.DEFAULT_SIGNING_ALGORITHM);
signature.initSign(privateKey);
byte[] bytes = "test string".getBytes(StandardCharsets.UTF_8);
signature.update(bytes);
Signature verify = Signature.getInstance(TlsConfig.DEFAULT_SIGNING_ALGORITHM);
verify.initVerify(publicKey);
verify.update(bytes);
verify.verify(signature.sign());
}
/**
* Will determine the available port used by ca server
*/
private int availablePort() {
ServerSocket s = null;
try {
s = new ServerSocket(0);
s.setReuseAddress(true);
return s.getLocalPort();
} catch (Exception e) {
throw new IllegalStateException("Failed to discover available port.", e);
} finally {
try {
s.close();
} catch (IOException e) {
// ignore
}
}
}
}

View File

@ -0,0 +1,148 @@
/*
* 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.nifi.toolkit.tls.service.client;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.InetAddress;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class TlsCertificateAuthorityClientCommandLineTest {
private TlsCertificateAuthorityClientCommandLine tlsCertificateAuthorityClientCommandLine;
private String testToken;
@Before
public void setup() {
tlsCertificateAuthorityClientCommandLine = new TlsCertificateAuthorityClientCommandLine();
testToken = "testToken";
}
@Test
public void testNoToken() {
try {
tlsCertificateAuthorityClientCommandLine.parse(new String[0]);
fail("Expected failure with no token argument");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_TOKEN_ARG_EMPTY.ordinal(), e.getExitCode());
}
}
@Test
public void testDefaults() throws CommandLineParseException, IOException {
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken);
TlsClientConfig clientConfig = tlsCertificateAuthorityClientCommandLine.createClientConfig();
assertEquals(TlsConfig.DEFAULT_HOSTNAME, clientConfig.getCaHostname());
Assert.assertEquals(TlsConfig.calcDefaultDn(InetAddress.getLocalHost().getHostName()), clientConfig.getDn());
assertEquals(TlsCertificateAuthorityClientCommandLine.KEYSTORE + TlsConfig.DEFAULT_KEY_STORE_TYPE.toLowerCase(), clientConfig.getKeyStore());
assertEquals(TlsConfig.DEFAULT_KEY_STORE_TYPE, clientConfig.getKeyStoreType());
assertNull(clientConfig.getKeyStorePassword());
assertNull(clientConfig.getKeyPassword());
assertEquals(TlsCertificateAuthorityClientCommandLine.TRUSTSTORE + TlsConfig.DEFAULT_KEY_STORE_TYPE.toLowerCase(), clientConfig.getTrustStore());
assertEquals(TlsConfig.DEFAULT_KEY_STORE_TYPE, clientConfig.getTrustStoreType());
assertNull(clientConfig.getTrustStorePassword());
assertEquals(TlsConfig.DEFAULT_KEY_SIZE, clientConfig.getKeySize());
assertEquals(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, clientConfig.getKeyPairAlgorithm());
assertEquals(testToken, clientConfig.getToken());
assertEquals(TlsConfig.DEFAULT_PORT, clientConfig.getPort());
assertEquals(TlsCertificateAuthorityClientCommandLine.DEFAULT_CONFIG_JSON, tlsCertificateAuthorityClientCommandLine.getConfigJson());
assertEquals(TlsCertificateAuthorityClientCommandLine.DEFAULT_CERTIFICATE_DIRECTORY, tlsCertificateAuthorityClientCommandLine.getCertificateDirectory());
}
@Test
public void testKeySize() throws CommandLineParseException {
int keySize = 1234;
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-k", Integer.toString(keySize));
assertEquals(keySize, tlsCertificateAuthorityClientCommandLine.getKeySize());
}
@Test
public void testKeyPairAlgorithm() throws CommandLineParseException {
String testAlgorithm = "testAlgorithm";
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-a", testAlgorithm);
assertEquals(testAlgorithm, tlsCertificateAuthorityClientCommandLine.getKeyAlgorithm());
}
@Test
public void testHelp() {
try {
tlsCertificateAuthorityClientCommandLine.parse("-h");
fail("Expected exception");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.HELP.ordinal(), e.getExitCode());
}
}
@Test
public void testCaHostname() throws CommandLineParseException, IOException {
String testCaHostname = "testCaHostname";
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-c", testCaHostname);
assertEquals(testCaHostname, tlsCertificateAuthorityClientCommandLine.createClientConfig().getCaHostname());
}
@Test
public void testDn() throws CommandLineParseException, IOException {
String testDn = "testDn";
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-D", testDn);
assertEquals(testDn, tlsCertificateAuthorityClientCommandLine.createClientConfig().getDn());
}
@Test
public void testPort() throws CommandLineParseException, IOException {
int testPort = 2345;
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-p", Integer.toString(testPort));
assertEquals(testPort, tlsCertificateAuthorityClientCommandLine.createClientConfig().getPort());
}
@Test
public void testKeyStoreType() throws CommandLineParseException, IOException {
String testType = "testType";
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-T", testType);
TlsClientConfig clientConfig = tlsCertificateAuthorityClientCommandLine.createClientConfig();
assertEquals(testType, clientConfig.getKeyStoreType());
assertEquals(testType, clientConfig.getTrustStoreType());
assertEquals(TlsCertificateAuthorityClientCommandLine.KEYSTORE + testType.toLowerCase(), clientConfig.getKeyStore());
assertEquals(TlsCertificateAuthorityClientCommandLine.TRUSTSTORE + testType.toLowerCase(), clientConfig.getTrustStore());
}
@Test
public void testConfigFile() throws CommandLineParseException {
String testPath = "/1/2/3/4";
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-f", testPath);
assertEquals(testPath, tlsCertificateAuthorityClientCommandLine.getConfigJson());
}
@Test
public void testCertificateFile() throws CommandLineParseException {
String testCertificateFile = "testCertificateFile";
tlsCertificateAuthorityClientCommandLine.parse("-t", testToken, "-C", testCertificateFile);
assertEquals(testCertificateFile, tlsCertificateAuthorityClientCommandLine.getCertificateDirectory());
}
}

View File

@ -0,0 +1,236 @@
/*
* 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.nifi.toolkit.tls.service.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpHost;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.configuration.TlsClientConfig;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityRequest;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityResponse;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.eclipse.jetty.server.Response;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class TlsCertificateSigningRequestPerformerTest {
@Mock
Supplier<HttpClientBuilder> httpClientBuilderSupplier;
@Mock
HttpClientBuilder httpClientBuilder;
@Mock
CloseableHttpClient closeableHttpClient;
@Mock
TlsClientConfig tlsClientConfig;
X509Certificate caCertificate;
X509Certificate signedCsr;
ObjectMapper objectMapper;
KeyPair keyPair;
TlsCertificateSigningRequestPerformer tlsCertificateSigningRequestPerformer;
String testToken;
String testCaHostname;
int testPort;
List<X509Certificate> certificates;
TlsCertificateAuthorityResponse tlsCertificateAuthorityResponse;
int statusCode;
private byte[] testHmac;
private String testSignedCsr;
@BeforeClass
public static void before() {
TlsHelper.addBouncyCastleProvider();
}
@Before
public void setup() throws GeneralSecurityException, OperatorCreationException, IOException {
objectMapper = new ObjectMapper();
keyPair = TlsHelper.generateKeyPair(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, TlsConfig.DEFAULT_KEY_SIZE);
testToken = "testToken";
testCaHostname = "testCaHostname";
testPort = 8993;
certificates = new ArrayList<>();
when(tlsClientConfig.getToken()).thenReturn(testToken);
when(tlsClientConfig.getCaHostname()).thenReturn(testCaHostname);
when(tlsClientConfig.getDn()).thenReturn(TlsConfig.calcDefaultDn(testCaHostname));
when(tlsClientConfig.getPort()).thenReturn(testPort);
when(tlsClientConfig.createCertificateSigningRequestPerformer()).thenReturn(tlsCertificateSigningRequestPerformer);
when(tlsClientConfig.getSigningAlgorithm()).thenReturn(TlsConfig.DEFAULT_SIGNING_ALGORITHM);
JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest = TlsHelper.generateCertificationRequest(tlsClientConfig.getDn(), keyPair, TlsConfig.DEFAULT_SIGNING_ALGORITHM);
String testCsrPem = TlsHelper.pemEncodeJcaObject(jcaPKCS10CertificationRequest);
when(httpClientBuilderSupplier.get()).thenReturn(httpClientBuilder);
when(httpClientBuilder.build()).thenAnswer(invocation -> {
Field sslSocketFactory = HttpClientBuilder.class.getDeclaredField("sslSocketFactory");
sslSocketFactory.setAccessible(true);
Object o = sslSocketFactory.get(httpClientBuilder);
Field field = TlsCertificateAuthorityClientSocketFactory.class.getDeclaredField("certificates");
field.setAccessible(true);
((List<X509Certificate>) field.get(o)).addAll(certificates);
return closeableHttpClient;
});
StatusLine statusLine = mock(StatusLine.class);
when(statusLine.getStatusCode()).thenAnswer(i -> statusCode);
when(closeableHttpClient.execute(eq(new HttpHost(testCaHostname, testPort, "https")), any(HttpPost.class))).thenAnswer(invocation -> {
HttpPost httpPost = (HttpPost) invocation.getArguments()[1];
TlsCertificateAuthorityRequest tlsCertificateAuthorityRequest = objectMapper.readValue(httpPost.getEntity().getContent(), TlsCertificateAuthorityRequest.class);
assertEquals(tlsCertificateAuthorityRequest.getCsr(), testCsrPem);
CloseableHttpResponse closeableHttpResponse = mock(CloseableHttpResponse.class);
when(closeableHttpResponse.getEntity()).thenAnswer(i -> {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
objectMapper.writeValue(byteArrayOutputStream, tlsCertificateAuthorityResponse);
return new ByteArrayEntity(byteArrayOutputStream.toByteArray());
});
when(closeableHttpResponse.getStatusLine()).thenReturn(statusLine);
return closeableHttpResponse;
});
KeyPair caKeyPair = TlsHelper.generateKeyPair(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, TlsConfig.DEFAULT_KEY_SIZE);
caCertificate = CertificateUtils.generateSelfSignedX509Certificate(caKeyPair, "CN=fakeCa", TlsConfig.DEFAULT_SIGNING_ALGORITHM, TlsConfig.DEFAULT_DAYS);
testHmac = TlsHelper.calculateHMac(testToken, caCertificate.getPublicKey());
signedCsr = CertificateUtils.generateIssuedCertificate(jcaPKCS10CertificationRequest.getSubject().toString(), jcaPKCS10CertificationRequest.getPublicKey(),
caCertificate, caKeyPair, TlsConfig.DEFAULT_SIGNING_ALGORITHM, TlsConfig.DEFAULT_DAYS);
testSignedCsr = TlsHelper.pemEncodeJcaObject(signedCsr);
tlsCertificateSigningRequestPerformer = new TlsCertificateSigningRequestPerformer(httpClientBuilderSupplier, tlsClientConfig);
}
@Test
public void testOk() throws Exception {
certificates.add(caCertificate);
statusCode = Response.SC_OK;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse(testHmac, testSignedCsr);
tlsCertificateSigningRequestPerformer.perform(keyPair);
}
@Test
public void testBadStatusCode() throws Exception {
statusCode = Response.SC_FORBIDDEN;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse();
try {
tlsCertificateSigningRequestPerformer.perform(keyPair);
fail("Expected IOE");
} catch (IOException e) {
assertTrue(e.getMessage().startsWith(TlsCertificateSigningRequestPerformer.RECEIVED_RESPONSE_CODE + statusCode));
}
}
@Test
public void test0CertSize() throws Exception {
statusCode = Response.SC_OK;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse();
try {
tlsCertificateSigningRequestPerformer.perform(keyPair);
fail("Expected IOE");
} catch (IOException e) {
assertEquals(TlsCertificateSigningRequestPerformer.EXPECTED_ONE_CERTIFICATE, e.getMessage());
}
}
@Test
public void test2CertSize() throws Exception {
certificates.add(caCertificate);
certificates.add(caCertificate);
statusCode = Response.SC_OK;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse();
try {
tlsCertificateSigningRequestPerformer.perform(keyPair);
fail("Expected IOE");
} catch (IOException e) {
assertEquals(TlsCertificateSigningRequestPerformer.EXPECTED_ONE_CERTIFICATE, e.getMessage());
}
}
@Test
public void testNoHmac() throws Exception {
certificates.add(caCertificate);
statusCode = Response.SC_OK;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse(null, testSignedCsr);
try {
tlsCertificateSigningRequestPerformer.perform(keyPair);
fail("Expected IOE");
} catch (IOException e) {
assertEquals(TlsCertificateSigningRequestPerformer.EXPECTED_RESPONSE_TO_CONTAIN_HMAC, e.getMessage());
}
}
@Test
public void testBadHmac() throws Exception {
certificates.add(caCertificate);
statusCode = Response.SC_OK;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse("badHmac".getBytes(StandardCharsets.UTF_8), testSignedCsr);
try {
tlsCertificateSigningRequestPerformer.perform(keyPair);
fail("Expected IOE");
} catch (IOException e) {
assertEquals(TlsCertificateSigningRequestPerformer.UNEXPECTED_HMAC_RECEIVED_POSSIBLE_MAN_IN_THE_MIDDLE, e.getMessage());
}
}
@Test
public void testNoCertificate() throws Exception {
certificates.add(caCertificate);
statusCode = Response.SC_OK;
tlsCertificateAuthorityResponse = new TlsCertificateAuthorityResponse(testHmac, null);
try {
tlsCertificateSigningRequestPerformer.perform(keyPair);
fail("Expected IOE");
} catch (IOException e) {
assertEquals(TlsCertificateSigningRequestPerformer.EXPECTED_RESPONSE_TO_CONTAIN_CERTIFICATE, e.getMessage());
}
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.nifi.toolkit.tls.service.server;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.util.InputStreamFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(MockitoJUnitRunner.class)
public class TlsCertificateAuthorityServiceCommandLineTest {
@Mock
InputStreamFactory inputStreamFactory;
TlsCertificateAuthorityServiceCommandLine tlsCertificateAuthorityServiceCommandLine;
String testToken;
@Before
public void setup() {
tlsCertificateAuthorityServiceCommandLine = new TlsCertificateAuthorityServiceCommandLine(inputStreamFactory);
testToken = "testToken";
}
@Test
public void testDefaults() throws CommandLineParseException, IOException {
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken);
TlsConfig tlsConfig = tlsCertificateAuthorityServiceCommandLine.createConfig();
assertEquals(TlsConfig.DEFAULT_HOSTNAME, tlsConfig.getCaHostname());
assertEquals(testToken, tlsConfig.getToken());
assertEquals(TlsConfig.DEFAULT_PORT, tlsConfig.getPort());
assertEquals(TlsConfig.DEFAULT_KEY_STORE_TYPE, tlsConfig.getKeyStoreType());
assertEquals(TlsCertificateAuthorityServiceCommandLine.NIFI_CA_KEYSTORE + tlsConfig.getKeyStoreType().toLowerCase(), tlsConfig.getKeyStore());
assertNull(tlsConfig.getKeyStorePassword());
assertNull(tlsConfig.getKeyPassword());
assertEquals(TlsConfig.DEFAULT_KEY_SIZE, tlsConfig.getKeySize());
assertEquals(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, tlsConfig.getKeyPairAlgorithm());
assertEquals(TlsConfig.DEFAULT_SIGNING_ALGORITHM, tlsConfig.getSigningAlgorithm());
assertEquals(TlsConfig.DEFAULT_DAYS, tlsConfig.getDays());
}
@Test
public void testCaHostname() throws CommandLineParseException, IOException {
String testCaHostname = "testCaHostname";
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-c", testCaHostname);
assertEquals(testCaHostname, tlsCertificateAuthorityServiceCommandLine.createConfig().getCaHostname());
}
@Test
public void testPort() throws CommandLineParseException, IOException {
int testPort = 4321;
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-p", Integer.toString(testPort));
assertEquals(testPort, tlsCertificateAuthorityServiceCommandLine.createConfig().getPort());
}
@Test
public void testKeyStoreType() throws CommandLineParseException, IOException {
String testKeyStoreType = "testKeyStoreType";
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-T", testKeyStoreType);
TlsConfig tlsConfig = tlsCertificateAuthorityServiceCommandLine.createConfig();
assertEquals(testKeyStoreType, tlsConfig.getKeyStoreType());
assertEquals(TlsCertificateAuthorityServiceCommandLine.NIFI_CA_KEYSTORE + tlsConfig.getKeyStoreType().toLowerCase(), tlsConfig.getKeyStore());
}
@Test
public void testKeySize() throws CommandLineParseException, IOException {
int testKeySize = 8192;
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-k", Integer.toString(testKeySize));
assertEquals(testKeySize, tlsCertificateAuthorityServiceCommandLine.createConfig().getKeySize());
}
@Test
public void testKeyPairAlgorithm() throws CommandLineParseException, IOException {
String testAlgorithm = "testAlgorithm";
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-a", testAlgorithm);
assertEquals(testAlgorithm, tlsCertificateAuthorityServiceCommandLine.createConfig().getKeyPairAlgorithm());
}
@Test
public void testSigningAlgorithm() throws CommandLineParseException, IOException {
String testSigningAlgorithm = "testSigningAlgorithm";
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-s", testSigningAlgorithm);
assertEquals(testSigningAlgorithm, tlsCertificateAuthorityServiceCommandLine.createConfig().getSigningAlgorithm());
}
@Test
public void testDays() throws CommandLineParseException, IOException {
int days = 1234;
tlsCertificateAuthorityServiceCommandLine.parse("-t", testToken, "-d", Integer.toString(days));
assertEquals(days, tlsCertificateAuthorityServiceCommandLine.createConfig().getDays());
}
}

View File

@ -0,0 +1,187 @@
/*
* 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.nifi.toolkit.tls.service.server;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.nifi.security.util.CertificateUtils;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityRequest;
import org.apache.nifi.toolkit.tls.service.dto.TlsCertificateAuthorityResponse;
import org.apache.nifi.toolkit.tls.util.TlsHelper;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.crmf.CRMFException;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.X509Certificate;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class TlsCertificateAuthorityServiceHandlerTest {
X509Certificate caCert;
@Mock
Request baseRequest;
@Mock
HttpServletRequest httpServletRequest;
@Mock
HttpServletResponse httpServletResponse;
JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest;
KeyPair keyPair;
String testToken;
String testPemEncodedCsr;
String testPemEncodedSignedCertificate;
ObjectMapper objectMapper;
TlsCertificateAuthorityServiceHandler tlsCertificateAuthorityServiceHandler;
TlsCertificateAuthorityRequest tlsCertificateAuthorityRequest;
int statusCode;
StringWriter response;
private byte[] testCaHmac;
private byte[] testHmac;
private String requestedDn;
private KeyPair certificateKeyPair;
@BeforeClass
public static void before() {
TlsHelper.addBouncyCastleProvider();
}
@Before
public void setup() throws Exception {
testToken = "testToken";
testPemEncodedSignedCertificate = "testPemEncodedSignedCertificate";
keyPair = TlsHelper.generateKeyPair(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, TlsConfig.DEFAULT_KEY_SIZE);
objectMapper = new ObjectMapper();
when(httpServletRequest.getReader()).thenAnswer(invocation -> {
StringWriter stringWriter = new StringWriter();
objectMapper.writeValue(stringWriter, tlsCertificateAuthorityRequest);
return new BufferedReader(new StringReader(stringWriter.toString()));
});
doAnswer(invocation -> statusCode = (int) invocation.getArguments()[0]).when(httpServletResponse).setStatus(anyInt());
doAnswer(invocation -> {
statusCode = (int) invocation.getArguments()[0];
StringWriter stringWriter = new StringWriter();
stringWriter.write((String) invocation.getArguments()[1]);
response = stringWriter;
return null;
}).when(httpServletResponse).sendError(anyInt(), anyString());
when(httpServletResponse.getWriter()).thenAnswer(invocation -> {
response = new StringWriter();
return new PrintWriter(response);
});
caCert = CertificateUtils.generateSelfSignedX509Certificate(keyPair, "CN=fakeCa", TlsConfig.DEFAULT_SIGNING_ALGORITHM, TlsConfig.DEFAULT_DAYS);
requestedDn = TlsConfig.calcDefaultDn(TlsConfig.DEFAULT_HOSTNAME);
certificateKeyPair = TlsHelper.generateKeyPair(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM, TlsConfig.DEFAULT_KEY_SIZE);
jcaPKCS10CertificationRequest = TlsHelper.generateCertificationRequest(requestedDn, certificateKeyPair, TlsConfig.DEFAULT_SIGNING_ALGORITHM);
testPemEncodedCsr = TlsHelper.pemEncodeJcaObject(jcaPKCS10CertificationRequest);
tlsCertificateAuthorityServiceHandler = new TlsCertificateAuthorityServiceHandler(TlsConfig.DEFAULT_SIGNING_ALGORITHM, TlsConfig.DEFAULT_DAYS, testToken, caCert, keyPair, objectMapper);
testHmac = TlsHelper.calculateHMac(testToken, jcaPKCS10CertificationRequest.getPublicKey());
testCaHmac = TlsHelper.calculateHMac(testToken, caCert.getPublicKey());
}
private TlsCertificateAuthorityResponse getResponse() throws IOException {
return objectMapper.readValue(new StringReader(response.toString()), TlsCertificateAuthorityResponse.class);
}
@Test
public void testSuccess() throws IOException, ServletException, GeneralSecurityException, CRMFException {
tlsCertificateAuthorityRequest = new TlsCertificateAuthorityRequest(testHmac, testPemEncodedCsr);
tlsCertificateAuthorityServiceHandler.handle(null, baseRequest, httpServletRequest, httpServletResponse);
assertEquals(Response.SC_OK, statusCode);
assertArrayEquals(testCaHmac, getResponse().getHmac());
X509Certificate certificate = TlsHelper.parseCertificate(getResponse().getPemEncodedCertificate());
assertEquals(certificateKeyPair.getPublic(), certificate.getPublicKey());
assertEquals(new X500Name(requestedDn), new X500Name(certificate.getSubjectDN().toString()));
certificate.verify(caCert.getPublicKey());
}
@Test
public void testNoCsr() throws IOException, ServletException {
tlsCertificateAuthorityRequest = new TlsCertificateAuthorityRequest(testHmac, null);
tlsCertificateAuthorityServiceHandler.handle(null, baseRequest, httpServletRequest, httpServletResponse);
assertEquals(Response.SC_BAD_REQUEST, statusCode);
assertEquals(TlsCertificateAuthorityServiceHandler.CSR_FIELD_MUST_BE_SET, getResponse().getError());
}
@Test
public void testNoHmac() throws IOException, ServletException {
tlsCertificateAuthorityRequest = new TlsCertificateAuthorityRequest(null, testPemEncodedCsr);
tlsCertificateAuthorityServiceHandler.handle(null, baseRequest, httpServletRequest, httpServletResponse);
assertEquals(Response.SC_BAD_REQUEST, statusCode);
assertEquals(TlsCertificateAuthorityServiceHandler.HMAC_FIELD_MUST_BE_SET, getResponse().getError());
}
@Test
public void testForbidden() throws IOException, ServletException, NoSuchAlgorithmException, CRMFException, NoSuchProviderException, InvalidKeyException {
tlsCertificateAuthorityRequest = new TlsCertificateAuthorityRequest("badHmac".getBytes(StandardCharsets.UTF_8), testPemEncodedCsr);
tlsCertificateAuthorityServiceHandler.handle(null, baseRequest, httpServletRequest, httpServletResponse);
assertEquals(Response.SC_FORBIDDEN, statusCode);
assertEquals(TlsCertificateAuthorityServiceHandler.FORBIDDEN, getResponse().getError());
}
@Test(expected = ServletException.class)
public void testServletException() throws IOException, ServletException {
tlsCertificateAuthorityServiceHandler.handle(null, baseRequest, httpServletRequest, httpServletResponse);
}
@After
public void verifyHandled() {
verify(baseRequest).setHandled(true);
}
}

View File

@ -0,0 +1,325 @@
/*
* 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.nifi.toolkit.tls.standalone;
import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.properties.NiFiPropertiesWriter;
import org.apache.nifi.toolkit.tls.util.PasswordUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.internal.stubbing.defaultanswers.ForwardsInvocations;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
public class TlsToolkitStandaloneCommandLineTest {
private SecureRandom secureRandom;
private TlsToolkitStandaloneCommandLine tlsToolkitStandaloneCommandLine;
@Before
public void setup() {
secureRandom = mock(SecureRandom.class);
doAnswer(new ForwardsInvocations(new Random())).when(secureRandom).nextBytes(any(byte[].class));
tlsToolkitStandaloneCommandLine = new TlsToolkitStandaloneCommandLine(new PasswordUtil(secureRandom));
}
@Test
public void testHelp() {
try {
tlsToolkitStandaloneCommandLine.parse("-h");
fail("Expected usage and help exit");
} catch (CommandLineParseException e) {
Assert.assertEquals(ExitCode.HELP.ordinal(), e.getExitCode());
}
}
@Test
public void testUnknownArg() {
try {
tlsToolkitStandaloneCommandLine.parse("--unknownArg");
fail("Expected error parsing command line");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_PARSING_COMMAND_LINE.ordinal(), e.getExitCode());
}
}
@Test
public void testKeyAlgorithm() throws CommandLineParseException, IOException {
String testKeyAlgorithm = "testKeyAlgorithm";
tlsToolkitStandaloneCommandLine.parse("-a", testKeyAlgorithm);
assertEquals(testKeyAlgorithm, tlsToolkitStandaloneCommandLine.createConfig().getKeyPairAlgorithm());
}
@Test
public void testKeySizeArgNotInteger() {
try {
tlsToolkitStandaloneCommandLine.parse("-k", "badVal");
fail("Expected bad keysize exit code");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_PARSING_INT_ARG.ordinal(), e.getExitCode());
}
}
@Test
public void testKeySize() throws CommandLineParseException, IOException {
int testKeySize = 4096;
tlsToolkitStandaloneCommandLine.parse("-k", Integer.toString(testKeySize));
assertEquals(testKeySize, tlsToolkitStandaloneCommandLine.createConfig().getKeySize());
}
@Test
public void testSigningAlgorithm() throws CommandLineParseException, IOException {
String testSigningAlgorithm = "testSigningAlgorithm";
tlsToolkitStandaloneCommandLine.parse("-s", testSigningAlgorithm);
assertEquals(testSigningAlgorithm, tlsToolkitStandaloneCommandLine.createConfig().getSigningAlgorithm());
}
@Test
public void testDaysNotInteger() {
try {
tlsToolkitStandaloneCommandLine.parse("-d", "badVal");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_PARSING_INT_ARG.ordinal(), e.getExitCode());
}
}
@Test
public void testDays() throws CommandLineParseException, IOException {
int testDays = 29;
tlsToolkitStandaloneCommandLine.parse("-d", Integer.toString(testDays));
assertEquals(testDays, tlsToolkitStandaloneCommandLine.createConfig().getDays());
}
@Test
public void testKeyStoreType() throws CommandLineParseException {
String testKeyStoreType = "testKeyStoreType";
tlsToolkitStandaloneCommandLine.parse("-T", testKeyStoreType);
assertEquals(testKeyStoreType, tlsToolkitStandaloneCommandLine.getKeyStoreType());
}
@Test
public void testOutputDirectory() throws CommandLineParseException {
String testPath = "/fake/path/doesnt/exist";
tlsToolkitStandaloneCommandLine.parse("-o", testPath);
assertEquals(testPath, tlsToolkitStandaloneCommandLine.getBaseDir().getAbsolutePath());
}
@Test
public void testHostnames() throws CommandLineParseException {
String nifi1 = "nifi1";
String nifi2 = "nifi2";
tlsToolkitStandaloneCommandLine.parse("-n", nifi1 + " , " + nifi2);
List<String> hostnames = tlsToolkitStandaloneCommandLine.getHostnames();
assertEquals(2, hostnames.size());
assertEquals(nifi1, hostnames.get(0));
assertEquals(nifi2, hostnames.get(1));
}
@Test
public void testHttpsPort() throws CommandLineParseException {
String testPort = "8998";
tlsToolkitStandaloneCommandLine.parse("-p", testPort);
assertEquals(testPort, tlsToolkitStandaloneCommandLine.getHttpsPort());
}
@Test
public void testNifiPropertiesFile() throws CommandLineParseException, IOException {
tlsToolkitStandaloneCommandLine.parse("-f", TlsToolkitStandaloneTest.TEST_NIFI_PROPERTIES);
assertEquals(TlsToolkitStandaloneTest.FAKE_VALUE, getProperties().get(TlsToolkitStandaloneTest.NIFI_FAKE_PROPERTY));
}
@Test
public void testNifiPropertiesFileDefault() throws CommandLineParseException, IOException {
tlsToolkitStandaloneCommandLine.parse();
assertNull(getProperties().get(TlsToolkitStandaloneTest.NIFI_FAKE_PROPERTY));
}
@Test
public void testBadNifiPropertiesFile() {
try {
tlsToolkitStandaloneCommandLine.parse("-f", "/this/file/should/not/exist.txt");
fail("Expected error when unable to read file");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_READING_NIFI_PROPERTIES.ordinal(), e.getExitCode());
}
}
@Test
public void testNotSameKeyAndKeystorePassword() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse("-g");
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
assertEquals(1, tlsToolkitStandaloneCommandLine.getHostnames().size());
assertEquals(1, keyStorePasswords.size());
assertEquals(1, keyPasswords.size());
assertNotEquals(keyStorePasswords.get(0), keyPasswords.get(0));
}
@Test
public void testSameKeyAndKeystorePassword() throws CommandLineParseException {
tlsToolkitStandaloneCommandLine.parse();
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
assertEquals(1, tlsToolkitStandaloneCommandLine.getHostnames().size());
assertEquals(1, keyStorePasswords.size());
assertEquals(1, keyPasswords.size());
assertEquals(keyStorePasswords.get(0), keyPasswords.get(0));
}
@Test
public void testSameKeyAndKeystorePasswordWithKeystorePasswordSpecified() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-S", testPassword);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
assertEquals(1, keyStorePasswords.size());
assertEquals(testPassword, keyStorePasswords.get(0));
assertEquals(keyStorePasswords, tlsToolkitStandaloneCommandLine.getKeyPasswords());
}
@Test
public void testSameKeyAndKeystorePasswordWithKeyPasswordSpecified() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-K", testPassword);
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
assertNotEquals(tlsToolkitStandaloneCommandLine.getKeyStorePasswords(), keyPasswords);
assertEquals(1, keyPasswords.size());
assertEquals(testPassword, keyPasswords.get(0));
}
@Test
public void testKeyStorePasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-S", testPassword);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
assertEquals(1, keyStorePasswords.size());
assertEquals(testPassword, keyStorePasswords.get(0));
}
@Test
public void testMultipleKeystorePasswordArgs() throws CommandLineParseException {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-S", testPassword1, "-S", testPassword2);
List<String> keyStorePasswords = tlsToolkitStandaloneCommandLine.getKeyStorePasswords();
assertEquals(2, keyStorePasswords.size());
assertEquals(testPassword1, keyStorePasswords.get(0));
assertEquals(testPassword2, keyStorePasswords.get(1));
}
@Test
public void testMultipleKeystorePasswordArgSingleHost() {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
try {
tlsToolkitStandaloneCommandLine.parse("-S", testPassword1, "-S", testPassword2);
fail("Expected error with mismatch keystore password number");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS.ordinal(), e.getExitCode());
}
}
@Test
public void testKeyPasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-K", testPassword);
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
assertEquals(1, keyPasswords.size());
assertEquals(testPassword, keyPasswords.get(0));
}
@Test
public void testMultipleKeyPasswordArgs() throws CommandLineParseException {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-K", testPassword1, "-K", testPassword2);
List<String> keyPasswords = tlsToolkitStandaloneCommandLine.getKeyPasswords();
assertEquals(2, keyPasswords.size());
assertEquals(testPassword1, keyPasswords.get(0));
assertEquals(testPassword2, keyPasswords.get(1));
}
@Test
public void testMultipleKeyPasswordArgSingleHost() {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
try {
tlsToolkitStandaloneCommandLine.parse("-K", testPassword1, "-K", testPassword2);
fail("Expected error with mismatch keystore password number");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS.ordinal(), e.getExitCode());
}
}
@Test
public void testTruststorePasswordArg() throws CommandLineParseException {
String testPassword = "testPassword";
tlsToolkitStandaloneCommandLine.parse("-P", testPassword);
List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.getTrustStorePasswords();
assertEquals(1, trustStorePasswords.size());
assertEquals(testPassword, trustStorePasswords.get(0));
}
@Test
public void testMultipleTruststorePasswordArgs() throws CommandLineParseException {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
tlsToolkitStandaloneCommandLine.parse("-n", "nifi1,nifi2", "-P", testPassword1, "-P", testPassword2);
List<String> trustStorePasswords = tlsToolkitStandaloneCommandLine.getTrustStorePasswords();
assertEquals(2, trustStorePasswords.size());
assertEquals(testPassword1, trustStorePasswords.get(0));
assertEquals(testPassword2, trustStorePasswords.get(1));
}
@Test
public void testMultipleTruststorePasswordArgSingleHost() {
String testPassword1 = "testPassword1";
String testPassword2 = "testPassword2";
try {
tlsToolkitStandaloneCommandLine.parse("-P", testPassword1, "-P", testPassword2);
fail("Expected error with mismatch keystore password number");
} catch (CommandLineParseException e) {
assertEquals(ExitCode.ERROR_INCORRECT_NUMBER_OF_PASSWORDS.ordinal(), e.getExitCode());
}
}
private Properties getProperties() throws IOException {
NiFiPropertiesWriter niFiPropertiesWriter = tlsToolkitStandaloneCommandLine.getNiFiPropertiesWriterFactory().create();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
niFiPropertiesWriter.writeNiFiProperties(byteArrayOutputStream);
Properties properties = new Properties();
properties.load(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
return properties;
}
}

View File

@ -0,0 +1,253 @@
/*
* 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.nifi.toolkit.tls.standalone;
import org.apache.commons.io.FileUtils;
import org.apache.nifi.toolkit.tls.commandLine.BaseCommandLine;
import org.apache.nifi.toolkit.tls.commandLine.ExitCode;
import org.apache.nifi.toolkit.tls.configuration.TlsConfig;
import org.apache.nifi.toolkit.tls.util.TlsHelperTest;
import org.apache.nifi.util.NiFiProperties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.util.Properties;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class TlsToolkitStandaloneTest {
public static final String NIFI_FAKE_PROPERTY = "nifi.fake.property";
public static final String FAKE_VALUE = "fake value";
public static final String TEST_NIFI_PROPERTIES = "src/test/resources/localhost/nifi.properties";
private SecurityManager originalSecurityManager;
private File tempDir;
@Before
public void setup() throws IOException {
tempDir = File.createTempFile("tls-test", UUID.randomUUID().toString());
if (!tempDir.delete()) {
throw new IOException("Couldn't delete " + tempDir);
}
if (!tempDir.mkdirs()) {
throw new IOException("Couldn't make directory " + tempDir);
}
originalSecurityManager = System.getSecurityManager();
// [see http://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit#answer-309427]
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm) {
// Noop
}
@Override
public void checkPermission(Permission perm, Object context) {
// Noop
}
@Override
public void checkExit(int status) {
super.checkExit(status);
throw new ExitException(status);
}
});
}
@After
public void teardown() throws IOException {
System.setSecurityManager(originalSecurityManager);
FileUtils.deleteDirectory(tempDir);
}
@Test
public void testBadParse() {
runAndAssertExitCode(ExitCode.ERROR_PARSING_COMMAND_LINE.ordinal(), "--unknownArgument");
}
@Test
public void testHelp() {
runAndAssertExitCode(ExitCode.HELP.ordinal(), "-h");
runAndAssertExitCode(ExitCode.HELP.ordinal(), "--help");
}
@Test
public void testDirOutput() throws Exception {
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath());
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertNull(nifiProperties.get("nifi.fake.property"));
assertEquals(nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD), nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
}
@Test
public void testDifferentArg() throws Exception {
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-g");
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertNull(nifiProperties.get("nifi.fake.property"));
assertNotEquals(nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD), nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
}
@Test
public void testFileArg() throws Exception {
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-f", TEST_NIFI_PROPERTIES);
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(FAKE_VALUE, nifiProperties.get(NIFI_FAKE_PROPERTY));
}
@Test
public void testHostnamesArgument() throws Exception {
String nifi1 = "nifi1";
String nifi2 = "nifi2";
String nifi3 = "nifi3";
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-n", nifi1 + "," + nifi2 + "," + nifi3);
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
checkHostDirAndReturnNifiProperties(nifi1, x509Certificate);
checkHostDirAndReturnNifiProperties(nifi2, x509Certificate);
checkHostDirAndReturnNifiProperties(nifi3, x509Certificate);
}
@Test
public void testKeyPasswordArg() throws Exception {
String testKey = "testKey";
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-K", testKey);
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(testKey, nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD));
}
@Test
public void testKeyStorePasswordArg() throws Exception {
String testKeyStore = "testKeyStore";
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-S", testKeyStore);
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(testKeyStore, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD));
}
@Test
public void testTrustStorePasswordArg() throws Exception {
String testTrustStore = "testTrustStore";
runAndAssertExitCode(0, "-o", tempDir.getAbsolutePath(), "-P", testTrustStore);
X509Certificate x509Certificate = checkLoadCertPrivateKey(TlsConfig.DEFAULT_KEY_PAIR_ALGORITHM);
Properties nifiProperties = checkHostDirAndReturnNifiProperties(TlsConfig.DEFAULT_HOSTNAME, x509Certificate);
assertEquals(testTrustStore, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD));
}
private X509Certificate checkLoadCertPrivateKey(String algorithm) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, CertificateException {
KeyPair keyPair = TlsHelperTest.loadKeyPair(new File(tempDir, TlsToolkitStandalone.NIFI_KEY + ".key"));
assertEquals(algorithm, keyPair.getPrivate().getAlgorithm());
assertEquals(algorithm, keyPair.getPublic().getAlgorithm());
X509Certificate x509Certificate = TlsHelperTest.loadCertificate(new File(tempDir, TlsToolkitStandalone.NIFI_CERT + ".pem"));
assertEquals(keyPair.getPublic(), x509Certificate.getPublicKey());
return x509Certificate;
}
private Properties checkHostDirAndReturnNifiProperties(String hostname, X509Certificate rootCert) throws Exception {
File hostDir = new File(tempDir, hostname);
Properties nifiProperties = new Properties();
try (InputStream inputStream = new FileInputStream(new File(hostDir, TlsToolkitStandalone.NIFI_PROPERTIES))) {
nifiProperties.load(inputStream);
}
String trustStoreType = nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE);
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
try (InputStream inputStream = new FileInputStream(new File(hostDir, "truststore." + trustStoreType))) {
trustStore.load(inputStream, nifiProperties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray());
}
Certificate certificate = trustStore.getCertificate(TlsToolkitStandalone.NIFI_CERT);
assertEquals(rootCert, certificate);
String keyStoreType = nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE);
String keyStoreFilename = BaseCommandLine.KEYSTORE + keyStoreType;
File keyStoreFile = new File(hostDir, keyStoreFilename);
assertEquals(keyStoreFilename, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE));
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
try (InputStream inputStream = new FileInputStream(keyStoreFile)) {
keyStore.load(inputStream, nifiProperties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray());
}
char[] keyPassword = nifiProperties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD).toCharArray();
KeyStore.Entry entry = keyStore.getEntry(TlsToolkitStandalone.NIFI_KEY, new KeyStore.PasswordProtection(keyPassword));
assertEquals(KeyStore.PrivateKeyEntry.class, entry.getClass());
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) entry;
Certificate[] certificateChain = privateKeyEntry.getCertificateChain();
assertEquals(2, certificateChain.length);
assertEquals(rootCert, certificateChain[1]);
certificateChain[1].verify(rootCert.getPublicKey());
certificateChain[0].verify(rootCert.getPublicKey());
return nifiProperties;
}
private void runAndAssertExitCode(int exitCode, String... args) {
try {
TlsToolkitStandaloneCommandLine.main(args);
fail("Expecting exit code: " + exitCode);
} catch (ExitException e) {
assertEquals(exitCode, e.getExitCode());
}
}
private static class ExitException extends SecurityException {
private final int exitCode;
public ExitException(int exitCode) {
this.exitCode = exitCode;
}
public int getExitCode() {
return exitCode;
}
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.nifi.toolkit.tls.util;
import org.junit.Test;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
public class PasswordUtilTest {
@Test
public void testGeneratePassword() {
SecureRandom secureRandom = mock(SecureRandom.class);
PasswordUtil passwordUtil = new PasswordUtil(secureRandom);
int value = 8675309;
doAnswer(invocation -> {
byte[] bytes = (byte[]) invocation.getArguments()[0];
assertEquals(32, bytes.length);
Arrays.fill(bytes, (byte) 0);
byte[] val = ByteBuffer.allocate(Long.BYTES).putLong(value).array();
System.arraycopy(val, 0, bytes, bytes.length - val.length, val.length);
return null;
}).when(secureRandom).nextBytes(any(byte[].class));
String expected = Base64.getEncoder().encodeToString(BigInteger.valueOf(Integer.valueOf(value).longValue()).toByteArray()).split("=")[0];
String actual = passwordUtil.generatePassword();
assertEquals(expected, actual);
}
}

View File

@ -0,0 +1,163 @@
/*
* 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.nifi.toolkit.tls.util;
import org.apache.nifi.security.util.CertificateUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.OperatorCreationException;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
public class TlsHelperTest {
private int days;
private int keySize;
private String keyPairAlgorithm;
private String signingAlgorithm;
private String keyStoreType;
private SecureRandom secureRandom;
private KeyPairGenerator keyPairGenerator;
public static KeyPair loadKeyPair(Reader reader) throws IOException {
try (PEMParser pemParser = new PEMParser(reader)) {
Object object = pemParser.readObject();
assertEquals(PEMKeyPair.class, object.getClass());
return new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) object);
}
}
public static KeyPair loadKeyPair(File file) throws IOException {
return loadKeyPair(new FileReader(file));
}
public static X509Certificate loadCertificate(Reader reader) throws IOException, CertificateException {
try (PEMParser pemParser = new PEMParser(reader)) {
Object object = pemParser.readObject();
assertEquals(X509CertificateHolder.class, object.getClass());
return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate((X509CertificateHolder) object);
}
}
public static X509Certificate loadCertificate(File file) throws IOException, CertificateException {
return loadCertificate(new FileReader(file));
}
@BeforeClass
public static void beforeClass() {
Security.addProvider(new BouncyCastleProvider());
}
@Before
public void setup() throws NoSuchAlgorithmException {
days = 360;
keySize = 2048;
keyPairAlgorithm = "RSA";
signingAlgorithm = "SHA1WITHRSA";
keyStoreType = KeyStore.getDefaultType();
secureRandom = mock(SecureRandom.class);
keyPairGenerator = KeyPairGenerator.getInstance(keyPairAlgorithm);
keyPairGenerator.initialize(keySize);
}
private Date inFuture(int days) {
return new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(days));
}
@Test
public void testGenerateSelfSignedCert() throws GeneralSecurityException, IOException, OperatorCreationException {
String dn = "CN=testDN,O=testOrg";
X509Certificate x509Certificate = CertificateUtils.generateSelfSignedX509Certificate(TlsHelper.generateKeyPair(keyPairAlgorithm, keySize), dn, signingAlgorithm, days);
Date notAfter = x509Certificate.getNotAfter();
assertTrue(notAfter.after(inFuture(days - 1)));
assertTrue(notAfter.before(inFuture(days + 1)));
Date notBefore = x509Certificate.getNotBefore();
assertTrue(notBefore.after(inFuture(-1)));
assertTrue(notBefore.before(inFuture(1)));
assertEquals(dn, x509Certificate.getIssuerDN().getName());
assertEquals(signingAlgorithm, x509Certificate.getSigAlgName());
assertEquals(keyPairAlgorithm, x509Certificate.getPublicKey().getAlgorithm());
x509Certificate.checkValidity();
}
@Test
public void testIssueCert() throws IOException, CertificateException, NoSuchAlgorithmException, OperatorCreationException, NoSuchProviderException, InvalidKeyException, SignatureException {
X509Certificate issuer = loadCertificate(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("rootCert.crt")));
KeyPair issuerKeyPair = loadKeyPair(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("rootCert.key")));
String dn = "CN=testIssued,O=testOrg";
KeyPair keyPair = TlsHelper.generateKeyPair(keyPairAlgorithm, keySize);
X509Certificate x509Certificate = CertificateUtils.generateIssuedCertificate(dn, keyPair.getPublic(), issuer, issuerKeyPair, signingAlgorithm, days);
assertEquals(dn, x509Certificate.getSubjectDN().toString());
assertEquals(issuer.getSubjectDN().toString(), x509Certificate.getIssuerDN().toString());
assertEquals(keyPair.getPublic(), x509Certificate.getPublicKey());
Date notAfter = x509Certificate.getNotAfter();
assertTrue(notAfter.after(inFuture(days - 1)));
assertTrue(notAfter.before(inFuture(days + 1)));
Date notBefore = x509Certificate.getNotBefore();
assertTrue(notBefore.after(inFuture(-1)));
assertTrue(notBefore.before(inFuture(1)));
assertEquals(signingAlgorithm, x509Certificate.getSigAlgName());
assertEquals(keyPairAlgorithm, x509Certificate.getPublicKey().getAlgorithm());
x509Certificate.verify(issuerKeyPair.getPublic());
}
}

View File

@ -0,0 +1,177 @@
#
# 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.
#
# Core Properties #
nifi.fake.property=fake value
nifi.version=1.0.0-SNAPSHOT
nifi.flow.configuration.file=./conf/flow.xml.gz
nifi.flow.configuration.archive.dir=./conf/archive/
nifi.flowcontroller.autoResumeState=true
nifi.flowcontroller.graceful.shutdown.period=10 sec
nifi.flowservice.writedelay.interval=500 ms
nifi.administrative.yield.duration=30 sec
# If a component has no work to do (is "bored"), how long should we wait before checking again for work?
nifi.bored.yield.duration=10 millis
nifi.authorizer.configuration.file=./conf/authorizers.xml
nifi.login.identity.provider.configuration.file=./conf/login-identity-providers.xml
nifi.templates.directory=./conf/templates
nifi.ui.banner.text=
nifi.ui.autorefresh.interval=30 sec
nifi.nar.library.directory=./lib
nifi.nar.working.directory=./work/nar/
nifi.documentation.working.directory=./work/docs/components
####################
# State Management #
####################
nifi.state.management.configuration.file=./conf/state-management.xml
# The ID of the local state provider
nifi.state.management.provider.local=local-provider
# The ID of the cluster-wide state provider. This will be ignored if NiFi is not clustered but must be populated if running in a cluster.
nifi.state.management.provider.cluster=zk-provider
# Specifies whether or not this instance of NiFi should run an embedded ZooKeeper server
nifi.state.management.embedded.zookeeper.start=false
# Properties file that provides the ZooKeeper properties to use if <nifi.state.management.embedded.zookeeper.start> is set to true
nifi.state.management.embedded.zookeeper.properties=./conf/zookeeper.properties
# H2 Settings
nifi.database.directory=./database_repository
nifi.h2.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE
# FlowFile Repository
nifi.flowfile.repository.implementation=org.apache.nifi.controller.repository.WriteAheadFlowFileRepository
nifi.flowfile.repository.directory=./flowfile_repository
nifi.flowfile.repository.partitions=256
nifi.flowfile.repository.checkpoint.interval=2 mins
nifi.flowfile.repository.always.sync=false
nifi.swap.manager.implementation=org.apache.nifi.controller.FileSystemSwapManager
nifi.queue.swap.threshold=20000
nifi.swap.in.period=5 sec
nifi.swap.in.threads=1
nifi.swap.out.period=5 sec
nifi.swap.out.threads=4
# Content Repository
nifi.content.repository.implementation=org.apache.nifi.controller.repository.FileSystemRepository
nifi.content.claim.max.appendable.size=10 MB
nifi.content.claim.max.flow.files=100
nifi.content.repository.directory.default=./content_repository
nifi.content.repository.archive.max.retention.period=12 hours
nifi.content.repository.archive.max.usage.percentage=50%
nifi.content.repository.archive.enabled=true
nifi.content.repository.always.sync=false
nifi.content.viewer.url=/nifi-content-viewer/
# Provenance Repository Properties
nifi.provenance.repository.implementation=org.apache.nifi.provenance.PersistentProvenanceRepository
# Persistent Provenance Repository Properties
nifi.provenance.repository.directory.default=./provenance_repository
nifi.provenance.repository.max.storage.time=24 hours
nifi.provenance.repository.max.storage.size=1 GB
nifi.provenance.repository.rollover.time=30 secs
nifi.provenance.repository.rollover.size=100 MB
nifi.provenance.repository.query.threads=2
nifi.provenance.repository.index.threads=1
nifi.provenance.repository.compress.on.rollover=true
nifi.provenance.repository.always.sync=false
nifi.provenance.repository.journal.count=16
# Comma-separated list of fields. Fields that are not indexed will not be searchable. Valid fields are:
# EventType, FlowFileUUID, Filename, TransitURI, ProcessorID, AlternateIdentifierURI, Relationship, Details
nifi.provenance.repository.indexed.fields=EventType, FlowFileUUID, Filename, ProcessorID, Relationship
# FlowFile Attributes that should be indexed and made searchable. Some examples to consider are filename, uuid, mime.type
nifi.provenance.repository.indexed.attributes=
# Large values for the shard size will result in more Java heap usage when searching the Provenance Repository
# but should provide better performance
nifi.provenance.repository.index.shard.size=500 MB
# Indicates the maximum length that a FlowFile attribute can be when retrieving a Provenance Event from
# the repository. If the length of any attribute exceeds this value, it will be truncated when the event is retrieved.
nifi.provenance.repository.max.attribute.length=65536
# Volatile Provenance Respository Properties
nifi.provenance.repository.buffer.size=100000
# Component Status Repository
nifi.components.status.repository.implementation=org.apache.nifi.controller.status.history.VolatileComponentStatusRepository
nifi.components.status.repository.buffer.size=1440
nifi.components.status.snapshot.frequency=1 min
# Site to Site properties
nifi.remote.input.host=
nifi.remote.input.secure=false
nifi.remote.input.socket.port=
nifi.remote.input.http.enabled=true
nifi.remote.input.http.transaction.ttl=30 sec
# web properties #
nifi.web.war.directory=./lib
nifi.web.http.host=
nifi.web.http.port=8080
nifi.web.https.host=
nifi.web.https.port=
nifi.web.jetty.working.directory=./work/jetty
nifi.web.jetty.threads=200
# security properties #
nifi.sensitive.props.key=
nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL
nifi.sensitive.props.provider=BC
nifi.security.keystore=./conf/localhost.jks
nifi.security.keystoreType=jks
nifi.security.keystorePasswd=vidmcgplvih3dn2fqjnckns77g
nifi.security.keyPasswd=qgs57rmnot6p8gm97pfjutnu5g
nifi.security.truststore=./conf/truststore.jks
nifi.security.truststoreType=jks
nifi.security.truststorePasswd=t7rmn1fg8np2ck1sduqdd85opv
nifi.security.needClientAuth=
nifi.security.user.authorizer=file-provider
nifi.security.user.login.identity.provider=
nifi.security.ocsp.responder.url=
nifi.security.ocsp.responder.pemEncodedCertificate=
# cluster common properties (all nodes must have same values) #
nifi.cluster.protocol.heartbeat.interval=5 sec
nifi.cluster.protocol.is.secure=false
# cluster node properties (only configure for cluster nodes) #
nifi.cluster.is.node=false
nifi.cluster.node.address=
nifi.cluster.node.protocol.port=
nifi.cluster.node.protocol.threads=10
nifi.cluster.node.event.history.size=25
nifi.cluster.node.connection.timeout=5 sec
nifi.cluster.node.read.timeout=5 sec
nifi.cluster.firewall.file=
# How long a request should be allowed to hold a 'lock' on a component. #
nifi.cluster.request.replication.claim.timeout=15 secs
# zookeeper properties, used for cluster management #
nifi.zookeeper.connect.string=
nifi.zookeeper.connect.timeout=3 secs
nifi.zookeeper.session.timeout=3 secs
nifi.zookeeper.root.node=/nifi
# kerberos #
nifi.kerberos.krb5.file=
nifi.kerberos.service.principal=
nifi.kerberos.keytab.location=
nifi.kerberos.authentication.expiration=12 hours

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWTCCAkGgAwIBAgIGAVXgcI3oMA0GCSqGSIb3DQEBCwUAMC0xFTATBgNVBAMM
DG5pZmkucm9vdC5jYTEUMBIGA1UECwwLYXBhY2hlLm5pZmkwHhcNMTYwNzEyMTg0
ODQwWhcNMTcwNzEyMTg0ODQwWjAtMRUwEwYDVQQDDAxuaWZpLnJvb3QuY2ExFDAS
BgNVBAsMC2FwYWNoZS5uaWZpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAuvjnFOoo5fYaq6vMiP2cmoSj3pzpLjvVPsQGP4OH7vy1f3C6IpWkrXjy/kst
sP116WC4PdnLJ0FF4ogR/4b5XpeFuLXrmsGk3dWQac3zunZ1AOvJKflQqCiOfIG8
9uMknJe0enHq4fhjy4qZrwOmMozGayWa5iRjGfY7mS44luYQvn9dz9G4IIoGVpxJ
rltHDrl0fMcVqY6Kfxb//6jvo74mW0E85hju45JHseEUMHMsZaJAYfKg2dRQryE1
UIW5Gai2Io17HjWIqWUnht5/CvJG3MKo/3XnVuwyVnbnt5bey+N+NVVsMKTig3m7
PGK2R6fycKupIMMVZ60FbsLo7wIDAQABo38wfTAOBgNVHQ8BAf8EBAMCAf4wDAYD
VR0TBAUwAwEB/zAdBgNVHQ4EFgQUxPKyo9LTvN7jTJ1x2jVUe4nNz1swHwYDVR0j
BBgwFoAUxPKyo9LTvN7jTJ1x2jVUe4nNz1swHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQBcMRAlnWDco5WbjTV0uSxmS7Dh
RxYgt7YguqA/tdfcn6hhJZ2ZZm5By6nwP4aGFY45tOv2NsjHgchgfJ6Osl3ZxGmF
+JrXW7mveOwZIfZzM2yFCusgkzrGOAWNL2G+lbXpRCnsTuJL6jUbRE1cFwU+iUYo
E8Xfo6XZVNlzeab4WNerAPGssftV9C+0ya9+5+hFmBhzpGkpn5EVicxLAX6fI4K6
N4ZCBU8DYQilYkE0xgxySSz7Ia1vo8D7Tr30CxoXGsqRLXanW0Jw1wVsLVheTLZw
W3gp2XKOaP/BMFbIFw2iB7REeCao14u9pRAKEodpH+fpUosbFlpd8ysRvFJP
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuvjnFOoo5fYaq6vMiP2cmoSj3pzpLjvVPsQGP4OH7vy1f3C6
IpWkrXjy/kstsP116WC4PdnLJ0FF4ogR/4b5XpeFuLXrmsGk3dWQac3zunZ1AOvJ
KflQqCiOfIG89uMknJe0enHq4fhjy4qZrwOmMozGayWa5iRjGfY7mS44luYQvn9d
z9G4IIoGVpxJrltHDrl0fMcVqY6Kfxb//6jvo74mW0E85hju45JHseEUMHMsZaJA
YfKg2dRQryE1UIW5Gai2Io17HjWIqWUnht5/CvJG3MKo/3XnVuwyVnbnt5bey+N+
NVVsMKTig3m7PGK2R6fycKupIMMVZ60FbsLo7wIDAQABAoIBAAzqSpQQRLj10gvP
tzYzRpEJ3oPvFNBjw6wtQD086bPhcuwVrlXbPl/ZPffA26whJfbz/mYPWFAi2x/1
xECBY1JcZxlGP7sV6zyDlxEn79EOg7CHmQK0PRUQkB8bmyD2ub2zYP0gR1hKnyId
NdZ4Cw/s13vpQhTpqIrPjnhbT/9kUMMxdWrN36/o/TL18VZMD2E3WGMzX+lbKNLs
QvfAGUpbfbPtmUPFezbDoL5Pqkj4KZ4/5l1RfqQiamNijwxxm/oGbDh8AuyxvnoR
yxDc9OWEoepEb72Qdec4LJnaYNXUtI2rg1QAbcXzuRLtv8o0J0gpVdqm2ROyf3tF
aI+Fk6kCgYEA3jZM5Vta7BHBT1HmYGZQOuQHW+WD7CQgLeXCCB4O4xq/7l7iG2hM
3pt65TGae78KHtJ2LciWrKRuNTruBoKax6MFw16ZfSQSFi0Knn+EbcxcmPQ2i/VO
xpdOVAP9LyEKKGTgH2FTE7RkiGt/oK0eJrYHpHMpAs0jRyCIYGPmt10CgYEA12bg
fuMAhiy3kv/IYtl3W7doGFVzu2C59VWxmkp/WvGQyrVUCURp5PNjlb0LIM4DZnKR
/zCxoP0Sf1TuVmORO1Z+Z0NKJoowYyveScTnn+RqKw7ssx9KgGRXXHuWlDcBv5lI
MP5vYs/5J47arZdbjI3Nbirvv8y4kHt1LkrPWLsCgYB0qV9vnqm6fIvXv/DKNYzz
jhoK9hFvnXvDfPeEWXuJYdbYJ7pflz+cM1avE36+bwq6KdZfrQrl8qHlkl26z0DB
hOYWrwi4OYLBX75OfjYhiwQmTTiB/DTycCdwVnPLFXaGxIciEm+STcfmE0H7Mkg9
HJ4giVFDpj1aL0tKB8juBQKBgBED7m33jp4KwEGbdP/h/9y94zow3eCKZoYry8jN
Y7wSYwawRkApKFhOpao5cbyYqYoZONE4zn9SHnjyg5VNbjaKwZd0qFHdDq920qtv
I4Ds8ToPhsErkp4Lzx7eIGn64md5O0urfa8HkL4AOeQGldPHi9fNCn1TNa0sI3iR
rklrAoGBALjnqDQFBuT3io0locXPHw6UbIAbD6Pm8Z1+HgK/hskyX/9bmnJn3oyz
jxziJhpy96RoLu0sxBxo5fjHGc54Qn6Ccivu4oM+4rktfALhLuFUfie8tr6nZlnm
mxRyNqJxD8L5Ttb66TiC9snf98MYAqsKD03fPYUAdpPlIbn+xS44
-----END RSA PRIVATE KEY-----

30
nifi-toolkit/pom.xml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-toolkit</artifactId>
<packaging>pom</packaging>
<modules>
<module>nifi-toolkit-tls</module>
<module>nifi-toolkit-assembly</module>
</modules>
</project>

11
pom.xml
View File

@ -33,6 +33,7 @@ language governing permissions and limitations under the License. -->
<module>nifi-docs</module>
<module>nifi-maven-archetypes</module>
<module>nifi-external</module>
<module>nifi-toolkit</module>
</modules>
<url>http://nifi.apache.org</url>
<organization>
@ -868,6 +869,11 @@ language governing permissions and limitations under the License. -->
<artifactId>nifi-bootstrap</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-toolkit-tls</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-resources</artifactId>
@ -1257,6 +1263,11 @@ language governing permissions and limitations under the License. -->
<artifactId>nifi-hbase-client-service-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-assembly</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>