mirror of https://github.com/apache/nifi.git
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:
parent
f0401e4774
commit
fa4c6ab03c
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<fileSet>
|
||||
<directory>src/main/resources</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<filtered>true</filtered>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>src/main/resources/bin</directory>
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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.
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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 "$@"
|
|
@ -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
|
|
@ -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"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"keyStore" : "serverKeyStore",
|
||||
"keyStoreType" : "jks",
|
||||
"token" : "myTestTokenUseSomethingStronger",
|
||||
"caHostname" : "localhost",
|
||||
"port" : 8443,
|
||||
"days" : 1095,
|
||||
"keySize" : 2048,
|
||||
"keyPairAlgorithm" : "RSA",
|
||||
"signingAlgorithm" : "SHA256WITHRSA"
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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())));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
Binary file not shown.
|
@ -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-----
|
|
@ -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-----
|
|
@ -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
11
pom.xml
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue