NIFI-1324:

Changed Maven dependencies for BouncyCastle bcprov and bcpg from jdk16:1.46 to 
jdk15on:1.53 (kept nifi-web-security on jdk16:1.46 because jdk15on:1.53 splits 
OCSP logic into new module bcpkix).
Added individual unit tests for PGP public keyring validation.
Passes all legacy unit tests.
Added TODOs for customizable brick encryption and refactoring shared code.
Cleaned up magic numbers to constants.
Added unit tests for OpenPGPPasswordBasedEncryptor (internal consistency and 
legacy file decrypt).
Began refactoring shared encrypt code from OpenPGP* implementations.
Extracted encrypt utility method from OpenPGPPasswordBasedEncryptor to PGPUtil 
class.
Added test resources (signed and unsigned key-encrypted files).
Added unit tests for OpenPGPKeyBasedEncryptor (internal consistency and 
external file decrypt).
Changed BC dependency for nifi-web-security to bcprov-jdk15on:1.53 and 
bcpkix-jdk15on:1.53.
Updated OCSPValidator to use new BC logic for OCSP validation. This code 
compiles but should be fully audited, as the legacy OCSP validation was not 
completely implemented.
Added skeleton of OCSP validator unit tests with successful keypair and 
certificate generation and signing code.
Added further unit tests for issued certificates.
Annotated unimplemented unit tests with note about Groovy integration.
Refactored Jersey call in OCSPCertificateValidator to internal method.
Added toString() to NiFi local OcspRequest.
Implemented positive & negative unit tests with cache injection for 
valid/revoked OCSP certificate.
Resolved contrib-check issues.
Removed ignored code in unit test.

Signed-off-by: Matt Gilman <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/ffbfffce
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/ffbfffce
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/ffbfffce

Branch: refs/heads/NIFI-259
Commit: ffbfffce6dd381ffdac5704ef2279f1fe5345e89
Parents: 6b54753
Author: Andy LoPresto <[email protected]>
Authored: Tue Dec 22 19:03:09 2015 -0800
Committer: Matt Gilman <[email protected]>
Committed: Tue Jan 12 09:22:51 2016 -0500

----------------------------------------------------------------------
 .../nifi-framework/nifi-framework-core/pom.xml  |   2 +-
 .../nifi-web/nifi-web-security/pom.xml          |   6 +-
 .../x509/ocsp/OcspCertificateValidator.java     | 125 +++++---
 .../web/security/x509/ocsp/OcspRequest.java     |   9 +
 .../x509/ocsp/OcspCertificateValidatorTest.java | 311 +++++++++++++++++++
 .../nifi-standard-processors/pom.xml            |   4 +-
 .../processors/standard/EncryptContent.java     |  20 +-
 .../standard/util/OpenPGPKeyBasedEncryptor.java | 278 +++++++++++------
 .../util/OpenPGPPasswordBasedEncryptor.java     | 103 +++---
 .../nifi/processors/standard/util/PGPUtil.java  |  89 ++++++
 .../processors/standard/TestEncryptContent.java | 147 ++++++++-
 .../util/OpenPGPKeyBasedEncryptorTest.java      | 129 ++++++++
 .../util/OpenPGPPasswordBasedEncryptorTest.java | 123 ++++++++
 .../resources/TestEncryptContent/pubring.gpg    | Bin 0 -> 1250 bytes
 .../resources/TestEncryptContent/secring.gpg    | Bin 0 -> 2628 bytes
 .../resources/TestEncryptContent/text.txt.gpg   | Bin 0 -> 1481 bytes
 .../TestEncryptContent/text.txt.unsigned.gpg    | Bin 0 -> 824 bytes
 pom.xml                                         |  13 +-
 18 files changed, 1127 insertions(+), 232 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
index 783235a..dc694eb 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
@@ -85,7 +85,7 @@
         </dependency>
         <dependency>
             <groupId>org.bouncycastle</groupId>
-            <artifactId>bcprov-jdk16</artifactId>
+            <artifactId>bcprov-jdk15on</artifactId>
         </dependency>
         <dependency>
             <groupId>com.sun.jersey</groupId>

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
index d93e58e..5496f15 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml
@@ -84,7 +84,11 @@
         </dependency>
         <dependency>
             <groupId>org.bouncycastle</groupId>
-            <artifactId>bcprov-jdk16</artifactId>
+            <artifactId>bcprov-jdk15on</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
         </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
index 832a63c..b0762b5 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidator.java
@@ -27,40 +27,51 @@ import com.sun.jersey.api.client.UniformInterfaceException;
 import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.api.client.config.ClientConfig;
 import com.sun.jersey.api.client.config.DefaultClientConfig;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.framework.security.util.SslContextFactory;
+import org.apache.nifi.util.FormatUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.x509.ocsp.OcspStatus.ValidationStatus;
+import org.apache.nifi.web.security.x509.ocsp.OcspStatus.VerificationStatus;
+import org.apache.nifi.web.util.WebUtils;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.ocsp.BasicOCSPResp;
+import org.bouncycastle.cert.ocsp.CertificateID;
+import org.bouncycastle.cert.ocsp.CertificateStatus;
+import org.bouncycastle.cert.ocsp.OCSPException;
+import org.bouncycastle.cert.ocsp.OCSPReq;
+import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
+import org.bouncycastle.cert.ocsp.OCSPResp;
+import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.RevokedStatus;
+import org.bouncycastle.cert.ocsp.SingleResp;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.net.URI;
 import java.security.KeyStore;
-import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-import javax.security.auth.x500.X500Principal;
-import org.apache.nifi.framework.security.util.SslContextFactory;
-import org.apache.nifi.web.security.x509.ocsp.OcspStatus.ValidationStatus;
-import org.apache.nifi.web.security.x509.ocsp.OcspStatus.VerificationStatus;
-import org.apache.nifi.util.FormatUtils;
-import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.web.util.WebUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.bouncycastle.ocsp.BasicOCSPResp;
-import org.bouncycastle.ocsp.CertificateID;
-import org.bouncycastle.ocsp.CertificateStatus;
-import org.bouncycastle.ocsp.OCSPException;
-import org.bouncycastle.ocsp.OCSPReq;
-import org.bouncycastle.ocsp.OCSPReqGenerator;
-import org.bouncycastle.ocsp.OCSPResp;
-import org.bouncycastle.ocsp.OCSPRespStatus;
-import org.bouncycastle.ocsp.RevokedStatus;
-import org.bouncycastle.ocsp.SingleResp;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class OcspCertificateValidator {
 
@@ -89,7 +100,7 @@ public class OcspCertificateValidator {
                 // attempt to parse the specified va url
                 validationAuthorityURI = URI.create(rawValidationAuthorityUrl);
 
-                // connection detials
+                // connection details
                 final ClientConfig config = new DefaultClientConfig();
                 config.getProperties().put(ClientConfig.PROPERTY_READ_TIMEOUT, 
READ_TIMEOUT);
                 
config.getProperties().put(ClientConfig.PROPERTY_CONNECT_TIMEOUT, 
CONNECT_TIMEOUT);
@@ -283,16 +294,24 @@ public class OcspCertificateValidator {
         try {
             // prepare the request
             final BigInteger subjectSerialNumber = 
subjectCertificate.getSerialNumber();
-            final CertificateID certificateId = new 
CertificateID(CertificateID.HASH_SHA1, issuerCertificate, subjectSerialNumber);
+            final DigestCalculatorProvider calculatorProviderBuilder = new 
JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
+            final CertificateID certificateId = new 
CertificateID(calculatorProviderBuilder.get(CertificateID.HASH_SHA1),
+                    new X509CertificateHolder(issuerCertificate.getEncoded()),
+                    subjectSerialNumber);
 
             // generate the request
-            final OCSPReqGenerator requestGenerator = new OCSPReqGenerator();
+            final OCSPReqBuilder requestGenerator = new OCSPReqBuilder();
             requestGenerator.addRequest(certificateId);
-            final OCSPReq ocspRequest = requestGenerator.generate();
+
+            // Create a nonce to avoid replay attack
+            BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
+            Extension ext = new 
Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, new 
DEROctetString(nonce.toByteArray()));
+            requestGenerator.setRequestExtensions(new Extensions(new 
Extension[]{ext}));
+
+            final OCSPReq ocspRequest = requestGenerator.build();
 
             // perform the request
-            final WebResource resource = 
client.resource(validationAuthorityURI);
-            final ClientResponse response = 
resource.header(CONTENT_TYPE_HEADER, 
OCSP_REQUEST_CONTENT_TYPE).post(ClientResponse.class, ocspRequest.getEncoded());
+            final ClientResponse response = getClientResponse(ocspRequest);
 
             // ensure the request was completed successfully
             if (ClientResponse.Status.OK.getStatusCode() != 
response.getStatusInfo().getStatusCode()) {
@@ -305,22 +324,22 @@ public class OcspCertificateValidator {
 
             // verify the response status
             switch (ocspResponse.getStatus()) {
-                case OCSPRespStatus.SUCCESSFUL:
+                case OCSPRespBuilder.SUCCESSFUL:
                     
ocspStatus.setResponseStatus(OcspStatus.ResponseStatus.Successful);
                     break;
-                case OCSPRespStatus.INTERNAL_ERROR:
+                case OCSPRespBuilder.INTERNAL_ERROR:
                     
ocspStatus.setResponseStatus(OcspStatus.ResponseStatus.InternalError);
                     break;
-                case OCSPRespStatus.MALFORMED_REQUEST:
+                case OCSPRespBuilder.MALFORMED_REQUEST:
                     
ocspStatus.setResponseStatus(OcspStatus.ResponseStatus.MalformedRequest);
                     break;
-                case OCSPRespStatus.SIGREQUIRED:
+                case OCSPRespBuilder.SIG_REQUIRED:
                     
ocspStatus.setResponseStatus(OcspStatus.ResponseStatus.SignatureRequired);
                     break;
-                case OCSPRespStatus.TRY_LATER:
+                case OCSPRespBuilder.TRY_LATER:
                     
ocspStatus.setResponseStatus(OcspStatus.ResponseStatus.TryLater);
                     break;
-                case OCSPRespStatus.UNAUTHORIZED:
+                case OCSPRespBuilder.UNAUTHORIZED:
                     
ocspStatus.setResponseStatus(OcspStatus.ResponseStatus.Unauthorized);
                     break;
                 default:
@@ -329,7 +348,7 @@ public class OcspCertificateValidator {
             }
 
             // only proceed if the response was successful
-            if (ocspResponse.getStatus() != OCSPRespStatus.SUCCESSFUL) {
+            if (ocspResponse.getStatus() != OCSPRespBuilder.SUCCESSFUL) {
                 logger.warn(String.format("OCSP request was unsuccessful 
(%s).", ocspStatus.getResponseStatus().toString()));
                 return ocspStatus;
             }
@@ -337,7 +356,7 @@ public class OcspCertificateValidator {
             // ensure the appropriate response object
             final Object ocspResponseObject = ocspResponse.getResponseObject();
             if (ocspResponseObject == null || !(ocspResponseObject instanceof 
BasicOCSPResp)) {
-                logger.warn(String.format("Unexcepted OCSP response object: 
%s", ocspResponseObject));
+                logger.warn(String.format("Unexpected OCSP response object: 
%s", ocspResponseObject));
                 return ocspStatus;
             }
 
@@ -345,9 +364,9 @@ public class OcspCertificateValidator {
             final BasicOCSPResp basicOcspResponse = (BasicOCSPResp) 
ocspResponse.getResponseObject();
 
             // attempt to locate the responder certificate
-            final X509Certificate[] responderCertificates = 
basicOcspResponse.getCerts(null);
+            final X509CertificateHolder[] responderCertificates = 
basicOcspResponse.getCerts();
             if (responderCertificates.length != 1) {
-                logger.warn(String.format("Unexcepted number of OCSP responder 
certificates: %s", responderCertificates.length));
+                logger.warn(String.format("Unexpected number of OCSP responder 
certificates: %s", responderCertificates.length));
                 return ocspStatus;
             }
 
@@ -355,7 +374,7 @@ public class OcspCertificateValidator {
             final X509Certificate trustedResponderCertificate = 
getTrustedResponderCertificate(responderCertificates[0], issuerCertificate);
             if (trustedResponderCertificate != null) {
                 // verify the response
-                if 
(basicOcspResponse.verify(trustedResponderCertificate.getPublicKey(), null)) {
+                if (basicOcspResponse.isSignatureValid(new 
JcaContentVerifierProviderBuilder().setProvider("BC").build(trustedResponderCertificate.getPublicKey())))
 {
                     
ocspStatus.setVerificationStatus(VerificationStatus.Verified);
                 } else {
                     
ocspStatus.setVerificationStatus(VerificationStatus.Unverified);
@@ -383,30 +402,40 @@ public class OcspCertificateValidator {
                     }
                 }
             }
-        } catch (final OCSPException | IOException | UniformInterfaceException 
| ClientHandlerException | NoSuchProviderException e) {
+        } catch (final OCSPException | IOException | UniformInterfaceException 
| ClientHandlerException | OperatorCreationException e) {
             logger.error(e.getMessage(), e);
+        } catch (CertificateException e) {
+            e.printStackTrace();
         }
 
         return ocspStatus;
     }
 
+    private ClientResponse getClientResponse(OCSPReq ocspRequest) throws 
IOException {
+        final WebResource resource = client.resource(validationAuthorityURI);
+        return resource.header(CONTENT_TYPE_HEADER, 
OCSP_REQUEST_CONTENT_TYPE).post(ClientResponse.class, ocspRequest.getEncoded());
+    }
+
     /**
      * Gets the trusted responder certificate. The response contains the 
responder certificate, however we cannot blindly trust it. Instead, we use a 
configured trusted CA. If the responder
      * certificate is a trusted CA, then we can use it. If the responder 
certificate is not directly trusted, we still may be able to trust it if it was 
issued by the same CA that issued the subject
      * certificate. Other various checks may be required (this portion is 
currently not implemented).
      *
-     * @param responderCertificate cert
-     * @param issuerCertificate cert
+     * @param responderCertificateHolder cert
+     * @param issuerCertificate          cert
      * @return cert
      */
-    private X509Certificate getTrustedResponderCertificate(final 
X509Certificate responderCertificate, final X509Certificate issuerCertificate) {
+    private X509Certificate getTrustedResponderCertificate(final 
X509CertificateHolder responderCertificateHolder, final X509Certificate 
issuerCertificate) throws CertificateException {
         // look for the responder's certificate specifically
-        if 
(trustedCAs.containsKey(responderCertificate.getSubjectX500Principal().getName()))
 {
-            return 
trustedCAs.get(responderCertificate.getSubjectX500Principal().getName());
+        final X509Certificate responderCertificate = new 
JcaX509CertificateConverter().setProvider("BC").getCertificate(responderCertificateHolder);
+        final String trustedCAName = 
responderCertificate.getSubjectX500Principal().getName();
+        if (trustedCAs.containsKey(trustedCAName)) {
+            return trustedCAs.get(trustedCAName);
         }
 
         // if the responder certificate was issued by the same CA that issued 
the subject certificate we may be able to use that...
-        if 
(responderCertificate.getIssuerX500Principal().equals(issuerCertificate.getSubjectX500Principal()))
 {
+        final X500Principal issuerCA = 
issuerCertificate.getSubjectX500Principal();
+        if (responderCertificate.getIssuerX500Principal().equals(issuerCA)) {
             // perform a number of verification steps... TODO... from 
sun.security.provider.certpath.OCSPResponse.java... currently incomplete...
 //            try {
 //                // ensure appropriate key usage

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspRequest.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspRequest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspRequest.java
index 5318c13..1f69689 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspRequest.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/x509/ocsp/OcspRequest.java
@@ -66,4 +66,13 @@ public class OcspRequest {
         return true;
     }
 
+    @Override
+    public String toString() {
+        return new StringBuilder("NiFi OCSP Request: ")
+                .append("Subject DN: ").append(subjectCertificate != null ? 
subjectCertificate.getSubjectDN().getName() : "<null>")
+                .append(" issued by ")
+                .append("Issuer DN: ").append(issuerCertificate != null ? 
issuerCertificate.getSubjectDN().getName() : "<null>").toString();
+
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidatorTest.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidatorTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidatorTest.java
new file mode 100644
index 0000000..7d9c542
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/test/java/org/apache/nifi/web/security/x509/ocsp/OcspCertificateValidatorTest.java
@@ -0,0 +1,311 @@
+/*
+ * 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.web.security.x509.ocsp;
+
+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.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+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.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Vector;
+
+public class OcspCertificateValidatorTest {
+    private static final Logger logger = 
LoggerFactory.getLogger(OcspCertificateValidatorTest.class);
+
+    private static final int KEY_SIZE = 2048;
+
+    private static final long YESTERDAY = System.currentTimeMillis() - 24 * 60 
* 60 * 1000;
+    private static final long ONE_YEAR_FROM_NOW = System.currentTimeMillis() + 
365 * 24 * 60 * 60 * 1000;
+    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
+    private static final String PROVIDER = "BC";
+
+    private static final String ISSUER_DN = "CN=NiFi Test 
CA,OU=Security,O=Apache,ST=CA,C=US";
+
+    private static X509Certificate ISSUER_CERTIFICATE;
+
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        Security.addProvider(new BouncyCastleProvider());
+
+//        ISSUER_CERTIFICATE = generateCertificate(ISSUER_DN);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    @After
+    public void tearDown() throws Exception {
+
+    }
+
+    /**
+     * Generates a public/private RSA keypair using the default key size.
+     *
+     * @return the keypair
+     * @throws NoSuchAlgorithmException if the RSA algorithm is not available
+     */
+    private static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        KeyPairGenerator keyPairGenerator = 
KeyPairGenerator.getInstance("RSA");
+        keyPairGenerator.initialize(KEY_SIZE);
+        return keyPairGenerator.generateKeyPair();
+    }
+
+    /**
+     * Generates a signed certificate using an on-demand keypair.
+     *
+     * @param dn the DN
+     * @return the certificate
+     * @throws IOException               if an exception occurs
+     * @throws NoSuchAlgorithmException  if an exception occurs
+     * @throws CertificateException      if an exception occurs
+     * @throws NoSuchProviderException   if an exception occurs
+     * @throws SignatureException        if an exception occurs
+     * @throws InvalidKeyException       if an exception occurs
+     * @throws OperatorCreationException if an exception occurs
+     */
+    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               if an exception occurs
+     * @throws NoSuchAlgorithmException  if an exception occurs
+     * @throws CertificateException      if an exception occurs
+     * @throws NoSuchProviderException   if an exception occurs
+     * @throws SignatureException        if an exception occurs
+     * @throws InvalidKeyException       if an exception occurs
+     * @throws OperatorCreationException if an exception occurs
+     */
+    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);
+    }
+
+    /**
+     * Generates a certificate signed by the issuer key.
+     *
+     * @param dn        the subject DN
+     * @param issuerDn  the issuer DN
+     * @param issuerKey the issuer private key
+     * @return the certificate
+     * @throws IOException               if an exception occurs
+     * @throws NoSuchAlgorithmException  if an exception occurs
+     * @throws CertificateException      if an exception occurs
+     * @throws NoSuchProviderException   if an exception occurs
+     * @throws SignatureException        if an exception occurs
+     * @throws InvalidKeyException       if an exception occurs
+     * @throws OperatorCreationException if an exception occurs
+     */
+    private static X509Certificate generateIssuedCertificate(String dn, String 
issuerDn, PrivateKey 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               if an exception occurs
+     * @throws NoSuchAlgorithmException  if an exception occurs
+     * @throws CertificateException      if an exception occurs
+     * @throws NoSuchProviderException   if an exception occurs
+     * @throws SignatureException        if an exception occurs
+     * @throws InvalidKeyException       if an exception occurs
+     * @throws OperatorCreationException if an exception occurs
+     */
+    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);
+    }
+
+    @Test
+    public void testShouldGenerateCertificate() throws Exception {
+        // Arrange
+        final String testDn = "CN=This is a test";
+
+        // Act
+        X509Certificate certificate = generateCertificate(testDn);
+        logger.info("Generated certificate: \n{}", certificate);
+
+        // Assert
+        assert certificate.getSubjectDN().getName().equals(testDn);
+        assert certificate.getIssuerDN().getName().equals(testDn);
+        certificate.verify(certificate.getPublicKey());
+    }
+
+    @Test
+    public void testShouldGenerateCertificateFromKeyPair() throws Exception {
+        // Arrange
+        final String testDn = "CN=This is a test";
+        final KeyPair keyPair = generateKeyPair();
+
+        // Act
+        X509Certificate certificate = generateCertificate(testDn, keyPair);
+        logger.info("Generated certificate: \n{}", certificate);
+
+        // Assert
+        assert certificate.getPublicKey().equals(keyPair.getPublic());
+        assert certificate.getSubjectDN().getName().equals(testDn);
+        assert certificate.getIssuerDN().getName().equals(testDn);
+        certificate.verify(certificate.getPublicKey());
+    }
+
+    @Test
+    public void testShouldGenerateIssuedCertificate() throws Exception {
+        // Arrange
+        final String testDn = "CN=This is a signed test";
+        final String issuerDn = "CN=Issuer CA";
+        final KeyPair issuerKeyPair = generateKeyPair();
+        final PrivateKey issuerPrivateKey = issuerKeyPair.getPrivate();
+
+        final X509Certificate issuerCertificate = 
generateCertificate(issuerDn, issuerKeyPair);
+        logger.info("Generated issuer certificate: \n{}", issuerCertificate);
+
+        // Act
+        X509Certificate certificate = generateIssuedCertificate(testDn, 
issuerDn, issuerPrivateKey);
+        logger.info("Generated signed certificate: \n{}", certificate);
+
+        // Assert
+        assert 
issuerCertificate.getPublicKey().equals(issuerKeyPair.getPublic());
+        assert certificate.getSubjectX500Principal().getName().equals(testDn);
+        assert certificate.getIssuerX500Principal().getName().equals(issuerDn);
+        certificate.verify(issuerCertificate.getPublicKey());
+
+        try {
+            certificate.verify(certificate.getPublicKey());
+            Assert.fail("Should have thrown exception");
+        } catch (Exception e) {
+            assert e instanceof SignatureException;
+            assert e.getMessage().contains("certificate does not verify with 
supplied key");
+        }
+    }
+
+    @Ignore("To be implemented with Groovy test")
+    @Test
+    public void testShouldValidateCertificate() throws Exception {
+
+    }
+
+    @Ignore("To be implemented with Groovy test")
+    @Test
+    public void testShouldNotValidateEmptyCertificate() throws Exception {
+
+    }
+
+    @Ignore("To be implemented with Groovy test")
+    @Test
+    public void testShouldNotValidateInvalidCertificate() throws Exception {
+
+    }
+
+    @Ignore("To be implemented with Groovy test")
+    @Test
+    public void testValidateShouldHandleUnsignedResponse() throws Exception {
+
+    }
+
+    @Ignore("To be implemented with Groovy test")
+    @Test
+    public void testValidateShouldHandleResponseWithIncorrectNonce() throws 
Exception {
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
index e189027..5ec2cf5 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
@@ -74,11 +74,11 @@ language governing permissions and limitations under the 
License. -->
         </dependency>
         <dependency>
             <groupId>org.bouncycastle</groupId>
-            <artifactId>bcprov-jdk16</artifactId>
+            <artifactId>bcprov-jdk15on</artifactId>
         </dependency>
         <dependency>
             <groupId>org.bouncycastle</groupId>
-            <artifactId>bcpg-jdk16</artifactId>
+            <artifactId>bcpg-jdk15on</artifactId>
         </dependency>
         <dependency>
             <groupId>commons-codec</groupId>

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java
index 50397c8..8cad3cb 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java
@@ -16,16 +16,6 @@
  */
 package org.apache.nifi.processors.standard;
 
-import java.security.Security;
-import java.text.Normalizer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.annotation.behavior.EventDriven;
 import org.apache.nifi.annotation.behavior.InputRequirement;
@@ -56,6 +46,16 @@ import org.apache.nifi.security.util.KeyDerivationFunction;
 import org.apache.nifi.util.StopWatch;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
+import java.security.Security;
+import java.text.Normalizer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
 @EventDriven
 @SideEffectFree
 @SupportsBatching

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPKeyBasedEncryptor.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPKeyBasedEncryptor.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPKeyBasedEncryptor.java
index 1931c72..b97e416 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPKeyBasedEncryptor.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPKeyBasedEncryptor.java
@@ -16,17 +16,6 @@
  */
 package org.apache.nifi.processors.standard.util;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.zip.Deflater;
-
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.io.StreamCallback;
 import org.apache.nifi.processors.standard.EncryptContent;
@@ -41,6 +30,7 @@ import org.bouncycastle.openpgp.PGPException;
 import org.bouncycastle.openpgp.PGPLiteralData;
 import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
 import org.bouncycastle.openpgp.PGPObjectFactory;
+import org.bouncycastle.openpgp.PGPOnePassSignatureList;
 import org.bouncycastle.openpgp.PGPPrivateKey;
 import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
@@ -50,18 +40,41 @@ import org.bouncycastle.openpgp.PGPSecretKey;
 import org.bouncycastle.openpgp.PGPSecretKeyRing;
 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
 import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
+import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.zip.Deflater;
+
+import static org.apache.nifi.processors.standard.util.PGPUtil.BLOCK_SIZE;
+import static org.apache.nifi.processors.standard.util.PGPUtil.BUFFER_SIZE;
 
 public class OpenPGPKeyBasedEncryptor implements Encryptor {
+    private static final Logger logger = 
LoggerFactory.getLogger(OpenPGPPasswordBasedEncryptor.class);
 
     private String algorithm;
     private String provider;
+    // TODO: This can hold either the secret or public keyring path
     private String keyring;
     private String userId;
     private char[] passphrase;
     private String filename;
 
-    public static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
-
     public OpenPGPKeyBasedEncryptor(final String algorithm, final String 
provider, final String keyring, final String userId, final char[] passphrase, 
final String filename) {
         this.algorithm = algorithm;
         this.provider = provider;
@@ -81,75 +94,137 @@ public class OpenPGPKeyBasedEncryptor implements Encryptor 
{
         return new OpenPGPDecryptCallback(provider, keyring, passphrase);
     }
 
-    /*
-     * Validate secret keyring passphrase
+    /**
+     * Returns true if the passphrase is valid.
+     * <p>
+     * This is used in the EncryptContent custom validation to check if the 
passphrase can extract a private key from the secret key ring. After BC was 
upgraded from 1.46 to 1.53, the API changed so this is performed differently 
but the functionality is equivalent.
+     *
+     * @param provider the provider name
+     * @param secretKeyringFile the file path to the keyring
+     * @param passphrase        the passphrase
+     * @return true if the passphrase can successfully extract any private key
+     * @throws IOException             if there is a problem reading the 
keyring file
+     * @throws PGPException            if there is a problem 
parsing/extracting the private key
+     * @throws NoSuchProviderException if the provider is not available
      */
     public static boolean validateKeyring(String provider, String 
secretKeyringFile, char[] passphrase) throws IOException, PGPException, 
NoSuchProviderException {
-        try (InputStream fin = 
Files.newInputStream(Paths.get(secretKeyringFile)); InputStream pin = 
PGPUtil.getDecoderStream(fin)) {
-            PGPSecretKeyRingCollection pgpsec = new 
PGPSecretKeyRingCollection(pin);
-            Iterator ringit = pgpsec.getKeyRings();
-            while (ringit.hasNext()) {
-                PGPSecretKeyRing secretkeyring = (PGPSecretKeyRing) 
ringit.next();
-                PGPSecretKey secretkey = secretkeyring.getSecretKey();
-                secretkey.extractPrivateKey(passphrase, provider);
-                return true;
-            }
+        try {
+            getDecryptedPrivateKey(provider, secretKeyringFile, passphrase);
+            return true;
+        } catch (Exception e) {
+            // If this point is reached, no private key could be extracted 
with the given passphrase
             return false;
         }
+    }
+
+    private static PGPPrivateKey getDecryptedPrivateKey(String provider, 
String secretKeyringFile, char[] passphrase) throws IOException, PGPException {
+        // TODO: Verify that key IDs cannot be 0
+        return getDecryptedPrivateKey(provider, secretKeyringFile, 0L, 
passphrase);
+    }
+
+    private static PGPPrivateKey getDecryptedPrivateKey(String provider, 
String secretKeyringFile, long keyId, char[] passphrase) throws IOException, 
PGPException {
+        // TODO: Reevaluate the mechanism for executing this task as 
performance can suffer here and only a specific key needs to be validated
+
+        // Read in from the secret keyring file
+        try (FileInputStream keyInputStream = new 
FileInputStream(secretKeyringFile)) {
+
+            // Form the SecretKeyRing collection (1.53 way with fingerprint 
calculator)
+            PGPSecretKeyRingCollection pgpSecretKeyRingCollection = new 
PGPSecretKeyRingCollection(keyInputStream, new BcKeyFingerprintCalculator());
+
+            // The decryptor is identical for all keys
+            final PBESecretKeyDecryptor decryptor = new 
JcePBESecretKeyDecryptorBuilder().setProvider(provider).build(passphrase);
+
+            // Iterate over all secret keyrings
+            Iterator<PGPSecretKeyRing> keyringIterator = 
pgpSecretKeyRingCollection.getKeyRings();
+            PGPSecretKeyRing keyRing;
+            PGPSecretKey secretKey;
+
+            while (keyringIterator.hasNext()) {
+                keyRing = keyringIterator.next();
+
+                // If keyId exists, get a specific secret key; else, iterate 
over all
+                if (keyId != 0) {
+                    secretKey = keyRing.getSecretKey(keyId);
+                    try {
+                        return secretKey.extractPrivateKey(decryptor);
+                    } catch (Exception e) {
+                        throw new PGPException("No private key available using 
passphrase", e);
+                    }
+                } else {
+                    Iterator<PGPSecretKey> keyIterator = 
keyRing.getSecretKeys();
+
+                    while (keyIterator.hasNext()) {
+                        secretKey = keyIterator.next();
+                        try {
+                            return secretKey.extractPrivateKey(decryptor);
+                        } catch (Exception e) {
+                            // TODO: Log (expected) failures?
+                        }
+                    }
+                }
+            }
+        }
 
+        // If this point is reached, no private key could be extracted with 
the given passphrase
+        throw new PGPException("No private key available using passphrase");
     }
 
     /*
      * Get the public key for a specific user id from a keyring.
      */
     @SuppressWarnings("rawtypes")
-    public static PGPPublicKey getPublicKey(String userId, String 
publicKeyring) throws IOException, PGPException {
-        PGPPublicKey pubkey = null;
-        try (InputStream fin = Files.newInputStream(Paths.get(publicKeyring)); 
InputStream pin = PGPUtil.getDecoderStream(fin)) {
-            PGPPublicKeyRingCollection pgppub = new 
PGPPublicKeyRingCollection(pin);
-
-            Iterator ringit = pgppub.getKeyRings();
-            while (ringit.hasNext()) {
-                PGPPublicKeyRing kring = (PGPPublicKeyRing) ringit.next();
-
-                Iterator keyit = kring.getPublicKeys();
-                while (keyit.hasNext()) {
-                    pubkey = (PGPPublicKey) keyit.next();
-                    boolean userIdMatch = false;
-
-                    Iterator userit = pubkey.getUserIDs();
-                    while (userit.hasNext()) {
-                        String id = userit.next().toString();
-                        if (id.contains(userId)) {
-                            userIdMatch = true;
-                            break;
+    public static PGPPublicKey getPublicKey(String userId, String 
publicKeyringFile) throws IOException, PGPException {
+        // TODO: Reevaluate the mechanism for executing this task as 
performance can suffer here and only a specific key needs to be validated
+
+        // Read in from the public keyring file
+        try (FileInputStream keyInputStream = new 
FileInputStream(publicKeyringFile)) {
+
+            // Form the PublicKeyRing collection (1.53 way with fingerprint 
calculator)
+            PGPPublicKeyRingCollection pgpPublicKeyRingCollection = new 
PGPPublicKeyRingCollection(keyInputStream, new BcKeyFingerprintCalculator());
+
+            // Iterate over all public keyrings
+            Iterator<PGPPublicKeyRing> iter = 
pgpPublicKeyRingCollection.getKeyRings();
+            PGPPublicKeyRing keyRing;
+            while (iter.hasNext()) {
+                keyRing = iter.next();
+
+                // Iterate over each public key in this keyring
+                Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
+                while (keyIter.hasNext()) {
+                    PGPPublicKey publicKey = keyIter.next();
+
+                    // Iterate over each userId attached to the public key
+                    Iterator userIdIterator = publicKey.getUserIDs();
+                    while (userIdIterator.hasNext()) {
+                        String id = (String) userIdIterator.next();
+                        if (userId.equalsIgnoreCase(id)) {
+                            return publicKey;
                         }
                     }
-                    if (pubkey.isEncryptionKey() && userIdMatch) {
-                        return pubkey;
-                    }
                 }
             }
         }
-        return null;
+
+        // If this point is reached, no public key could be extracted with the 
given userId
+        throw new PGPException("Could not find a public key with the given 
userId");
     }
 
     private static class OpenPGPDecryptCallback implements StreamCallback {
 
         private String provider;
-        private String secretKeyring;
+        private String secretKeyringFile;
         private char[] passphrase;
 
-        OpenPGPDecryptCallback(final String provider, final String keyring, 
final char[] passphrase) {
+        OpenPGPDecryptCallback(final String provider, final String 
secretKeyringFile, final char[] passphrase) {
             this.provider = provider;
-            this.secretKeyring = keyring;
+            this.secretKeyringFile = secretKeyringFile;
             this.passphrase = passphrase;
         }
 
         @Override
         public void process(InputStream in, OutputStream out) throws 
IOException {
             try (InputStream pgpin = PGPUtil.getDecoderStream(in)) {
-                PGPObjectFactory pgpFactory = new PGPObjectFactory(pgpin);
+                PGPObjectFactory pgpFactory = new PGPObjectFactory(pgpin, new 
BcKeyFingerprintCalculator());
 
                 Object obj = pgpFactory.nextObject();
                 if (!(obj instanceof PGPEncryptedDataList)) {
@@ -160,19 +235,11 @@ public class OpenPGPKeyBasedEncryptor implements 
Encryptor {
                 }
                 PGPEncryptedDataList encList = (PGPEncryptedDataList) obj;
 
-                PGPSecretKeyRingCollection pgpSecretKeyring;
-                try (InputStream secretKeyringIS = 
Files.newInputStream(Paths.get(secretKeyring)); InputStream pgpIS = 
PGPUtil.getDecoderStream(secretKeyringIS)) {
-                    // open secret keyring file
-                    pgpSecretKeyring = new PGPSecretKeyRingCollection(pgpIS);
-                } catch (Exception e) {
-                    throw new ProcessException("Invalid secret keyring - " + 
e.getMessage());
-                }
-
                 try {
                     PGPPrivateKey privateKey = null;
                     PGPPublicKeyEncryptedData encData = null;
 
-                    // find the secret key in the encrypted data
+                    // Find the secret key in the encrypted data
                     Iterator it = encList.getEncryptedDataObjects();
                     while (privateKey == null && it.hasNext()) {
                         obj = it.next();
@@ -180,32 +247,60 @@ public class OpenPGPKeyBasedEncryptor implements 
Encryptor {
                             throw new ProcessException("Invalid OpenPGP data");
                         }
                         encData = (PGPPublicKeyEncryptedData) obj;
-                        PGPSecretKey secretkey = 
pgpSecretKeyring.getSecretKey(encData.getKeyID());
-                        if (secretkey != null) {
-                            privateKey = 
secretkey.extractPrivateKey(passphrase, provider);
+
+                        // Check each encrypted data object to see if it 
contains the key ID for the secret key -> private key
+                        try {
+                            privateKey = getDecryptedPrivateKey(provider, 
secretKeyringFile, encData.getKeyID(), passphrase);
+                        } catch (PGPException e) {
+                            // TODO: Log (expected) exception?
                         }
                     }
                     if (privateKey == null) {
                         throw new ProcessException("Secret keyring does not 
contain the key required to decrypt");
                     }
 
-                    try (InputStream clearData = 
encData.getDataStream(privateKey, provider)) {
-                        PGPObjectFactory clearFactory = new 
PGPObjectFactory(clearData);
+                    // Read in the encrypted data stream and decrypt it
+                    final PublicKeyDataDecryptorFactory dataDecryptor = new 
JcePublicKeyDataDecryptorFactoryBuilder().setProvider(provider).build(privateKey);
+                    try (InputStream clear = 
encData.getDataStream(dataDecryptor)) {
+                        // Create a plain object factory
+                        JcaPGPObjectFactory plainFact = new 
JcaPGPObjectFactory(clear);
+
+                        Object message = plainFact.nextObject();
+
+                        // Check the message type and act accordingly
+
+                        // If compressed, decompress
+                        if (message instanceof PGPCompressedData) {
+                            PGPCompressedData cData = (PGPCompressedData) 
message;
+                            JcaPGPObjectFactory pgpFact = new 
JcaPGPObjectFactory(cData.getDataStream());
+
+                            message = pgpFact.nextObject();
+                        }
+
+                        // If the message is literal data, read it and process 
to the out stream
+                        if (message instanceof PGPLiteralData) {
+                            PGPLiteralData literalData = (PGPLiteralData) 
message;
 
-                        obj = clearFactory.nextObject();
-                        if (obj instanceof PGPCompressedData) {
-                            PGPCompressedData compData = (PGPCompressedData) 
obj;
-                            clearFactory = new 
PGPObjectFactory(compData.getDataStream());
-                            obj = clearFactory.nextObject();
+                            try (InputStream lis = 
literalData.getInputStream()) {
+                                final byte[] buffer = new byte[BLOCK_SIZE];
+                                int len;
+                                while ((len = lis.read(buffer)) >= 0) {
+                                    out.write(buffer, 0, len);
+                                }
+                            }
+                        } else if (message instanceof PGPOnePassSignatureList) 
{
+                            // TODO: This is legacy code but should verify 
signature list here
+                            throw new PGPException("encrypted message contains 
a signed message - not literal data.");
+                        } else {
+                            throw new PGPException("message is not a simple 
encrypted file - type unknown.");
                         }
-                        PGPLiteralData literal = (PGPLiteralData) obj;
 
-                        try (InputStream lis = literal.getInputStream()) {
-                            final byte[] buffer = new byte[4096];
-                            int len;
-                            while ((len = lis.read(buffer)) >= 0) {
-                                out.write(buffer, 0, len);
+                        if (encData.isIntegrityProtected()) {
+                            if (!encData.verify()) {
+                                throw new PGPException("Failed message 
integrity check");
                             }
+                        } else {
+                            logger.warn("No message integrity check");
                         }
                     }
                 } catch (Exception e) {
@@ -235,6 +330,8 @@ public class OpenPGPKeyBasedEncryptor implements Encryptor {
         @Override
         public void process(InputStream in, OutputStream out) throws 
IOException {
             PGPPublicKey publicKey;
+            final boolean isArmored = 
EncryptContent.isPGPArmoredAlgorithm(algorithm);
+
             try {
                 publicKey = getPublicKey(userId, publicKeyring);
             } catch (Exception e) {
@@ -242,35 +339,35 @@ public class OpenPGPKeyBasedEncryptor implements 
Encryptor {
             }
 
             try {
-                SecureRandom secureRandom = 
SecureRandom.getInstance(SECURE_RANDOM_ALGORITHM);
-
                 OutputStream output = out;
-                if (EncryptContent.isPGPArmoredAlgorithm(algorithm)) {
+                if (isArmored) {
                     output = new ArmoredOutputStream(out);
                 }
 
                 try {
-                    PGPEncryptedDataGenerator encGenerator = new 
PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, false, secureRandom, 
provider);
-                    encGenerator.addMethod(publicKey);
-                    try (OutputStream encOut = encGenerator.open(output, new 
byte[65536])) {
+                    // TODO: Refactor internal symmetric encryption algorithm 
to be customizable
+                    PGPEncryptedDataGenerator encryptedDataGenerator = new 
PGPEncryptedDataGenerator(
+                            new 
JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_128).setWithIntegrityPacket(true).setSecureRandom(new
 SecureRandom()).setProvider(provider));
 
-                        PGPCompressedDataGenerator compData = new 
PGPCompressedDataGenerator(PGPCompressedData.ZIP, Deflater.BEST_SPEED);
-                        try (OutputStream compOut = compData.open(encOut, new 
byte[65536])) {
+                    encryptedDataGenerator.addMethod(new 
JcePublicKeyKeyEncryptionMethodGenerator(publicKey).setProvider(provider));
 
-                            PGPLiteralDataGenerator literal = new 
PGPLiteralDataGenerator();
-                            try (OutputStream literalOut = 
literal.open(compOut, PGPLiteralData.BINARY, filename, new Date(), new 
byte[65536])) {
+                    // TODO: Refactor shared encryption code to utility
+                    try (OutputStream encryptedOut = 
encryptedDataGenerator.open(output, new byte[BUFFER_SIZE])) {
+                        PGPCompressedDataGenerator compressedDataGenerator = 
new PGPCompressedDataGenerator(PGPCompressedData.ZIP, Deflater.BEST_SPEED);
+                        try (OutputStream compressedOut = 
compressedDataGenerator.open(encryptedOut, new byte[BUFFER_SIZE])) {
+                            PGPLiteralDataGenerator literalDataGenerator = new 
PGPLiteralDataGenerator();
+                            try (OutputStream literalOut = 
literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, filename, new 
Date(), new byte[BUFFER_SIZE])) {
 
-                                final byte[] buffer = new byte[4096];
+                                final byte[] buffer = new byte[BLOCK_SIZE];
                                 int len;
                                 while ((len = in.read(buffer)) >= 0) {
                                     literalOut.write(buffer, 0, len);
                                 }
-
                             }
                         }
                     }
                 } finally {
-                    if (EncryptContent.isPGPArmoredAlgorithm(algorithm)) {
+                    if (isArmored) {
                         output.close();
                     }
                 }
@@ -278,6 +375,5 @@ public class OpenPGPKeyBasedEncryptor implements Encryptor {
                 throw new ProcessException(e.getMessage());
             }
         }
-
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPPasswordBasedEncryptor.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPPasswordBasedEncryptor.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPPasswordBasedEncryptor.java
index e0c398c..1ffe9e4 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPPasswordBasedEncryptor.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/OpenPGPPasswordBasedEncryptor.java
@@ -16,38 +16,39 @@
  */
 package org.apache.nifi.processors.standard.util;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.SecureRandom;
-import java.util.Date;
-import java.util.zip.Deflater;
-
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.io.StreamCallback;
-import org.apache.nifi.processors.standard.EncryptContent;
 import org.apache.nifi.processors.standard.EncryptContent.Encryptor;
-import org.bouncycastle.bcpg.ArmoredOutputStream;
 import org.bouncycastle.openpgp.PGPCompressedData;
-import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
 import org.bouncycastle.openpgp.PGPEncryptedData;
-import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
 import org.bouncycastle.openpgp.PGPEncryptedDataList;
+import org.bouncycastle.openpgp.PGPException;
 import org.bouncycastle.openpgp.PGPLiteralData;
-import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
-import org.bouncycastle.openpgp.PGPObjectFactory;
 import org.bouncycastle.openpgp.PGPPBEEncryptedData;
-import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
+import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
+import 
org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.bouncycastle.openpgp.PGPUtil.getDecoderStream;
 
 public class OpenPGPPasswordBasedEncryptor implements Encryptor {
+    private static final Logger logger = 
LoggerFactory.getLogger(OpenPGPPasswordBasedEncryptor.class);
 
     private String algorithm;
     private String provider;
     private char[] password;
     private String filename;
 
-    public static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
-
     public OpenPGPPasswordBasedEncryptor(final String algorithm, final String 
provider, final char[] passphrase, final String filename) {
         this.algorithm = algorithm;
         this.provider = provider;
@@ -77,8 +78,8 @@ public class OpenPGPPasswordBasedEncryptor implements 
Encryptor {
 
         @Override
         public void process(InputStream in, OutputStream out) throws 
IOException {
-            InputStream pgpin = PGPUtil.getDecoderStream(in);
-            PGPObjectFactory pgpFactory = new PGPObjectFactory(pgpin);
+            InputStream pgpin = getDecoderStream(in);
+            JcaPGPObjectFactory pgpFactory = new JcaPGPObjectFactory(pgpin);
 
             Object obj = pgpFactory.nextObject();
             if (!(obj instanceof PGPEncryptedDataList)) {
@@ -93,31 +94,41 @@ public class OpenPGPPasswordBasedEncryptor implements 
Encryptor {
             if (!(obj instanceof PGPPBEEncryptedData)) {
                 throw new ProcessException("Invalid OpenPGP data");
             }
-            PGPPBEEncryptedData encData = (PGPPBEEncryptedData) obj;
+            PGPPBEEncryptedData encryptedData = (PGPPBEEncryptedData) obj;
 
             try {
-                InputStream clearData = encData.getDataStream(password, 
provider);
-                PGPObjectFactory clearFactory = new 
PGPObjectFactory(clearData);
+                final PGPDigestCalculatorProvider digestCalculatorProvider = 
new JcaPGPDigestCalculatorProviderBuilder().setProvider(provider).build();
+                final PBEDataDecryptorFactory decryptorFactory = new 
JcePBEDataDecryptorFactoryBuilder(digestCalculatorProvider).setProvider(provider).build(password);
+                InputStream clear = 
encryptedData.getDataStream(decryptorFactory);
 
-                obj = clearFactory.nextObject();
+                JcaPGPObjectFactory pgpObjectFactory = new 
JcaPGPObjectFactory(clear);
+
+                obj = pgpObjectFactory.nextObject();
                 if (obj instanceof PGPCompressedData) {
-                    PGPCompressedData compData = (PGPCompressedData) obj;
-                    clearFactory = new 
PGPObjectFactory(compData.getDataStream());
-                    obj = clearFactory.nextObject();
+                    PGPCompressedData compressedData = (PGPCompressedData) obj;
+                    pgpObjectFactory = new 
JcaPGPObjectFactory(compressedData.getDataStream());
+                    obj = pgpObjectFactory.nextObject();
                 }
-                PGPLiteralData literal = (PGPLiteralData) obj;
 
-                InputStream lis = literal.getInputStream();
-                final byte[] buffer = new byte[4096];
+                PGPLiteralData literalData = (PGPLiteralData) obj;
+                InputStream plainIn = literalData.getInputStream();
+                final byte[] buffer = new 
byte[org.apache.nifi.processors.standard.util.PGPUtil.BLOCK_SIZE];
                 int len;
-                while ((len = lis.read(buffer)) >= 0) {
+                while ((len = plainIn.read(buffer)) >= 0) {
                     out.write(buffer, 0, len);
                 }
+
+                if (encryptedData.isIntegrityProtected()) {
+                    if (!encryptedData.verify()) {
+                        throw new PGPException("Integrity check failed");
+                    }
+                } else {
+                    logger.warn("No message integrity check");
+                }
             } catch (Exception e) {
                 throw new ProcessException(e.getMessage());
             }
         }
-
     }
 
     private static class OpenPGPEncryptCallback implements StreamCallback {
@@ -137,39 +148,11 @@ public class OpenPGPPasswordBasedEncryptor implements 
Encryptor {
         @Override
         public void process(InputStream in, OutputStream out) throws 
IOException {
             try {
-                SecureRandom secureRandom = 
SecureRandom.getInstance(SECURE_RANDOM_ALGORITHM);
-
-                OutputStream output = out;
-                if (EncryptContent.isPGPArmoredAlgorithm(algorithm)) {
-                    output = new ArmoredOutputStream(out);
-                }
-
-                PGPEncryptedDataGenerator encGenerator = new 
PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, false,
-                        secureRandom, provider);
-                encGenerator.addMethod(password);
-                OutputStream encOut = encGenerator.open(output, new 
byte[65536]);
-
-                PGPCompressedDataGenerator compData = new 
PGPCompressedDataGenerator(PGPCompressedData.ZIP, Deflater.BEST_SPEED);
-                OutputStream compOut = compData.open(encOut, new byte[65536]);
-
-                PGPLiteralDataGenerator literal = new 
PGPLiteralDataGenerator();
-                OutputStream literalOut = literal.open(compOut, 
PGPLiteralData.BINARY, filename, new Date(), new byte[65536]);
-
-                final byte[] buffer = new byte[4096];
-                int len;
-                while ((len = in.read(buffer)) >= 0) {
-                    literalOut.write(buffer, 0, len);
-                }
-
-                literalOut.close();
-                compOut.close();
-                encOut.close();
-                output.close();
+                PGPKeyEncryptionMethodGenerator encryptionMethodGenerator = 
new JcePBEKeyEncryptionMethodGenerator(password).setProvider(provider);
+                org.apache.nifi.processors.standard.util.PGPUtil.encrypt(in, 
out, algorithm, provider, PGPEncryptedData.AES_128, filename, 
encryptionMethodGenerator);
             } catch (Exception e) {
                 throw new ProcessException(e.getMessage());
             }
-
         }
-
     }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PGPUtil.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PGPUtil.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PGPUtil.java
new file mode 100644
index 0000000..b1ee11f
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PGPUtil.java
@@ -0,0 +1,89 @@
+/*
+ * 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.processors.standard.util;
+
+import org.apache.nifi.processors.standard.EncryptContent;
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.openpgp.PGPCompressedData;
+import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
+import org.bouncycastle.openpgp.PGPEncryptedData;
+import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPLiteralData;
+import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
+import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
+import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Date;
+import java.util.zip.Deflater;
+
+/**
+ * This class contains static utility methods to assist with common PGP 
operations.
+ */
+public class PGPUtil {
+    private static final Logger logger = 
LoggerFactory.getLogger(PGPUtil.class);
+
+    public static final int BUFFER_SIZE = 65536;
+    public static final int BLOCK_SIZE = 4096;
+
+    public static void encrypt(InputStream in, OutputStream out, String 
algorithm, String provider, int cipher, String filename, 
PGPKeyEncryptionMethodGenerator encryptionMethodGenerator) throws IOException, 
PGPException {
+        final boolean isArmored = 
EncryptContent.isPGPArmoredAlgorithm(algorithm);
+        OutputStream output = out;
+        if (isArmored) {
+            output = new ArmoredOutputStream(out);
+        }
+
+        // Default value, do not allow null encryption
+        if (cipher == PGPEncryptedData.NULL) {
+            logger.warn("Null encryption not allowed; defaulting to AES-128");
+            cipher = PGPEncryptedData.AES_128;
+        }
+
+        try {
+            // TODO: Can probably hardcode provider to BC and remove one 
method parameter
+            PGPEncryptedDataGenerator encryptedDataGenerator = new 
PGPEncryptedDataGenerator(
+                    new 
JcePGPDataEncryptorBuilder(cipher).setWithIntegrityPacket(true).setSecureRandom(new
 SecureRandom()).setProvider(provider));
+
+            encryptedDataGenerator.addMethod(encryptionMethodGenerator);
+
+            try (OutputStream encryptedOut = 
encryptedDataGenerator.open(output, new byte[BUFFER_SIZE])) {
+                PGPCompressedDataGenerator compressedDataGenerator = new 
PGPCompressedDataGenerator(PGPCompressedData.ZIP, Deflater.BEST_SPEED);
+                try (OutputStream compressedOut = 
compressedDataGenerator.open(encryptedOut, new byte[BUFFER_SIZE])) {
+                    PGPLiteralDataGenerator literalDataGenerator = new 
PGPLiteralDataGenerator();
+                    try (OutputStream literalOut = 
literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, filename, new 
Date(), new byte[BUFFER_SIZE])) {
+
+                        final byte[] buffer = new byte[BLOCK_SIZE];
+                        int len;
+                        while ((len = in.read(buffer)) >= 0) {
+                            literalOut.write(buffer, 0, len);
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (isArmored) {
+                output.close();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ffbfffce/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
index ee21a50..3be2976 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
@@ -16,12 +16,6 @@
  */
 package org.apache.nifi.processors.standard;
 
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.security.Security;
-import java.util.Collection;
-
 import org.apache.commons.codec.binary.Hex;
 import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.processors.standard.util.PasswordBasedEncryptor;
@@ -39,6 +33,12 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.security.Security;
+import java.util.Collection;
+
 public class TestEncryptContent {
 
     private static final Logger logger = 
LoggerFactory.getLogger(TestEncryptContent.class);
@@ -204,6 +204,128 @@ public class TestEncryptContent {
     }
 
     @Test
+    public void testShouldValidatePGPPublicKeyringRequiresUserId() {
+        // Arrange
+        final TestRunner runner = 
TestRunners.newTestRunner(EncryptContent.class);
+        Collection<ValidationResult> results;
+        MockProcessContext pc;
+
+        runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE);
+        runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, 
EncryptionMethod.PGP.name());
+        runner.setProperty(EncryptContent.PUBLIC_KEYRING, 
"src/test/resources/TestEncryptContent/pubring.gpg");
+        runner.enqueue(new byte[0]);
+        pc = (MockProcessContext) runner.getProcessContext();
+
+        // Act
+        results = pc.validate();
+
+        // Assert
+        Assert.assertEquals(1, results.size());
+        ValidationResult vr = (ValidationResult) results.toArray()[0];
+        String expectedResult = " encryption without a " + 
EncryptContent.PASSWORD.getDisplayName() + " requires both "
+                + EncryptContent.PUBLIC_KEYRING.getDisplayName() + " and "
+                + EncryptContent.PUBLIC_KEY_USERID.getDisplayName();
+        String message = "'" + vr.toString() + "' contains '" + expectedResult 
+ "'";
+        Assert.assertTrue(message, vr.toString().contains(expectedResult));
+    }
+
+    @Test
+    public void testShouldValidatePGPPublicKeyringExists() {
+        // Arrange
+        final TestRunner runner = 
TestRunners.newTestRunner(EncryptContent.class);
+        Collection<ValidationResult> results;
+        MockProcessContext pc;
+
+        runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE);
+        runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, 
EncryptionMethod.PGP.name());
+        runner.setProperty(EncryptContent.PUBLIC_KEYRING, 
"src/test/resources/TestEncryptContent/pubring.gpg.missing");
+        runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID");
+        runner.enqueue(new byte[0]);
+        pc = (MockProcessContext) runner.getProcessContext();
+
+        // Act
+        results = pc.validate();
+
+        // Assert
+        Assert.assertEquals(1, results.size());
+        ValidationResult vr = (ValidationResult) results.toArray()[0];
+        String expectedResult = " (No such file or directory)";
+        String message = "'" + vr.toString() + "' contains '" + expectedResult 
+ "'";
+        Assert.assertTrue(message, vr.toString().contains(expectedResult));
+    }
+
+    @Test
+    public void testShouldValidatePGPPublicKeyringIsProperFormat() {
+        // Arrange
+        final TestRunner runner = 
TestRunners.newTestRunner(EncryptContent.class);
+        Collection<ValidationResult> results;
+        MockProcessContext pc;
+
+        runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE);
+        runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, 
EncryptionMethod.PGP.name());
+        runner.setProperty(EncryptContent.PUBLIC_KEYRING, 
"src/test/resources/TestEncryptContent/text.txt");
+        runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID");
+        runner.enqueue(new byte[0]);
+        pc = (MockProcessContext) runner.getProcessContext();
+
+        // Act
+        results = pc.validate();
+
+        // Assert
+        Assert.assertEquals(1, results.size());
+        ValidationResult vr = (ValidationResult) results.toArray()[0];
+        String expectedResult = " java.io.IOException: invalid header 
encountered";
+        String message = "'" + vr.toString() + "' contains '" + expectedResult 
+ "'";
+        Assert.assertTrue(message, vr.toString().contains(expectedResult));
+    }
+
+    @Test
+    public void testShouldValidatePGPPublicKeyringContainsUserId() {
+        // Arrange
+        final TestRunner runner = 
TestRunners.newTestRunner(EncryptContent.class);
+        Collection<ValidationResult> results;
+        MockProcessContext pc;
+
+        runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE);
+        runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, 
EncryptionMethod.PGP.name());
+        runner.setProperty(EncryptContent.PUBLIC_KEYRING, 
"src/test/resources/TestEncryptContent/pubring.gpg");
+        runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID");
+        runner.enqueue(new byte[0]);
+        pc = (MockProcessContext) runner.getProcessContext();
+
+        // Act
+        results = pc.validate();
+
+        // Assert
+        Assert.assertEquals(1, results.size());
+        ValidationResult vr = (ValidationResult) results.toArray()[0];
+        String expectedResult = "PGPException: Could not find a public key 
with the given userId";
+        String message = "'" + vr.toString() + "' contains '" + expectedResult 
+ "'";
+        Assert.assertTrue(message, vr.toString().contains(expectedResult));
+    }
+
+    @Test
+    public void testShouldExtractPGPPublicKeyFromKeyring() {
+        // Arrange
+        final TestRunner runner = 
TestRunners.newTestRunner(EncryptContent.class);
+        Collection<ValidationResult> results;
+        MockProcessContext pc;
+
+        runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE);
+        runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, 
EncryptionMethod.PGP.name());
+        runner.setProperty(EncryptContent.PUBLIC_KEYRING, 
"src/test/resources/TestEncryptContent/pubring.gpg");
+        runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "NiFi PGP Test 
Key (Short test key for NiFi PGP unit tests) 
<[email protected]>");
+        runner.enqueue(new byte[0]);
+        pc = (MockProcessContext) runner.getProcessContext();
+
+        // Act
+        results = pc.validate();
+
+        // Assert
+        Assert.assertEquals(0, results.size());
+    }
+
+    @Test
     public void testValidation() {
         final TestRunner runner = 
TestRunners.newTestRunner(EncryptContent.class);
         Collection<ValidationResult> results;
@@ -249,20 +371,15 @@ public class TestEncryptContent {
                             + 
EncryptContent.PUBLIC_KEY_USERID.getDisplayName()));
         }
 
-        runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID");
-        runner.enqueue(new byte[0]);
-        pc = (MockProcessContext) runner.getProcessContext();
-        results = pc.validate();
-        Assert.assertEquals(1, results.size());
-        for (final ValidationResult vr : results) {
-            Assert.assertTrue(vr.toString().contains("does not contain user id 
USERID"));
-        }
+        // Legacy tests moved to individual tests to comply with new library
+
+        // TODO: Move secring tests out to individual as well
 
         runner.removeProperty(EncryptContent.PUBLIC_KEYRING);
         runner.removeProperty(EncryptContent.PUBLIC_KEY_USERID);
 
         runner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE);
-        runner.setProperty(EncryptContent.PRIVATE_KEYRING, 
"src/test/resources/TestEncryptContent/text.txt");
+        runner.setProperty(EncryptContent.PRIVATE_KEYRING, 
"src/test/resources/TestEncryptContent/secring.gpg");
         runner.enqueue(new byte[0]);
         pc = (MockProcessContext) runner.getProcessContext();
         results = pc.validate();

Reply via email to