Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for JCA security providers #39

Merged
merged 9 commits into from
Oct 5, 2021

Conversation

smirandamedallia
Copy link
Contributor

  • Update the various classes to use jcajce rather than bc
  • Add support to set the security provider at a library level
  • Use BC and default provider

@@ -763,6 +771,7 @@ hQEMAyne546XDHBhAQ...
meta.verified
}

@Ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't make this work when I run this with ./gradlew build.
It gives me the following error:

org.bouncycastle.openpgp.PGPException: exception constructing public key
	at org.c02e.jpgpj.Encryptor.encrypt(Encryptor.java:1130)
	at org.c02e.jpgpj.Encryptor.prepareCiphertextOutputStream(Encryptor.java:974)
	at org.c02e.jpgpj.Encryptor.encrypt(Encryptor.java:857)
	at org.c02e.jpgpj.Encryptor.encrypt(Encryptor.java:826)
	at org.c02e.jpgpj.EncryptorSpec.encrypt and sign with no usage flags(EncryptorSpec.groovy:781)
Caused by: java.security.spec.InvalidParameterSpecException: Not a supported curve: java.security.spec.ECGenParameterSpec@5b0e7c05
	at java.base/sun.security.util.ECParameters.engineInit(ECParameters.java:130)
	at java.base/java.security.AlgorithmParameters.init(AlgorithmParameters.java:294)
	... 5 more

Could you please take a look

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an artifact of some peculiarities with groovy compilation and class loading (it fails here and not elsewhere because the JDK's default JCA implementation can handle the other test public keys, but not this test key using the P-256 curve). I believe right now it fails only when this class is recompiled and run in the same process (and not when compiled and run separately); and it will go away if a BouncyCastleProvider instance is set directly on the JcaContextHelper.securityProvider property (instead of indirectly via the JCA apparatus of Security.addProvider() / Security.getProvider()).

@smirandamedallia
Copy link
Contributor Author

I have upgraded the version number since BC provider must be present in the list of providers or the Provider must be set before performing PGP operations

Security.addProvider(new BouncyCastleProvider())

Copy link
Owner

@justinludwig justinludwig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for going ahead and doing this refactoring! It looks good to me -- I mainly have a few small suggestions (noted inline).

The one big suggestion I have is that instead of requiring JPGPJ users to register the BouncyCastleProvider with JCA (ie by calling Security.addProvider(new BouncyCastleProvider())), the JcaContextHelper class should itself try to initialize its securityProvider property with a BouncyCastleProvider instance directly. That way, existing users won't have to make any changes to their code, and can avoid messing around with JCA if they don't want to.

So that users can also use the alternative FIPS implementation if they do want to, we can do this initialization via reflection (see my suggestion for this inline) -- it'll be kind of ugly, but will make it easy for users who just use the regular Bouncy Castle implementation to do so without any JCA fiddling -- while still avoiding a big fireball that stymies users who do want to use the FIPS implementation instead.

@@ -1248,7 +1248,11 @@ protected PublicKeyKeyEncryptionMethodGenerator buildPublicKeyEncryptor(Key key,
}

PGPPublicKey publicKey = key.getEncryption().getPublicKey();
return new BcPublicKeyKeyEncryptionMethodGenerator(publicKey);
JcePublicKeyKeyEncryptionMethodGenerator generator = new JcePublicKeyKeyEncryptionMethodGenerator(publicKey);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull the creation of this generator (and the setting of the security provider on it) into a new factory method of the new JcaContextProvider class, like you did elsewhere for other JCA-aware objects.

new BcPGPDigestCalculatorProvider().get(kdAlgorithm.ordinal()),
workFactor);
PGPDigestCalculatorProvider digestCalculatorProvider = JCAContextHelper.getPGPDigestCalculatorProvider();
JcePBEKeyEncryptionMethodGenerator jcePBEKeyEncryptionMethodGenerator = new JcePBEKeyEncryptionMethodGenerator(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull this into a new factory method of the JcaContextProvider class, as well.

@@ -1313,7 +1322,11 @@ protected PGPSignatureGenerator buildSigner(Key key, FileMetadata meta)
* Builds a PGPContentSignerBuilder for the specified algorithms.
*/
protected PGPContentSignerBuilder buildSignerBuilder(int keyAlgorithm, int hashAlgorithm) {
return new BcPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm);
JcaPGPContentSignerBuilder jcaPGPContentSignerBuilder = new JcaPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull this into a new factory method of the JcaContextProvider class, as well.

* consisting of {@link java.security.Provider provider}
* Note: The following class is not thread safe, the security provider should not be changed during PGP operations
*/
public class JCAContextHelper {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's name this class JcaContextHelper instead of JCAContextHelper, to match the casing convention of other class names with initialisms in this project, like EncryptedAsciiArmorHeadersCallback etc.

* Note: The following class is not thread safe, the security provider should not be changed during PGP operations
*/
public class JCAContextHelper {
private static Provider securityProvider = Security.getProvider("BC");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the convenience of users who don't otherwise need to fiddle with JCA, let's try to automatically set the securityProvider property to a BouncyCastleProvider instance by default -- and fail as gracefully as possible if it's not available. Something like this:

    private static Provider securityProvider;
    static {
        try {
           securityProvider = (Provider) Class.forName(
                "org.bouncycastle.jce.provider.BouncyCastleProvider"
            ).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            LoggerFactory.getLogger(JCAContextHelper.class.getName()).warn(
                "not using BC security provider");
        }
    }

try {
return JCAContextHelper.getJcaKeyBoxBuilder().build(result.stream).getKeyBlobs().iterator();
} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of wrapping this with a RuntimeException, wrap this with a PGPException (so users can catch a PGPException in their own code if that want to handle anything PGP-related going wrong when they use JPGPJ).

try {
return JCAContextHelper.getJcePBESecretKeyDecryptorBuilder().build(chars);
} catch (PGPException e) {
throw new RuntimeException(e);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of wrapping the PGPException with a RuntimeException, let's just change the signature of this buildDecryptor method to throws PGPException.

class EncryptorSpec extends Specification {
def cipherOut = new ByteArrayOutputStream()
def plainOut = new ByteArrayOutputStream()

def setupSpec() {
Security.addProvider(new BouncyCastleProvider())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won't need this (here, and same for the other test specs) if we change the JcaProviderHelper to automatically use the BouncyCastleProvider when available.

@@ -763,6 +771,7 @@ hQEMAyne546XDHBhAQ...
meta.verified
}

@Ignore
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an artifact of some peculiarities with groovy compilation and class loading (it fails here and not elsewhere because the JDK's default JCA implementation can handle the other test public keys, but not this test key using the P-256 curve). I believe right now it fails only when this class is recompiled and run in the same process (and not when compiled and run separately); and it will go away if a BouncyCastleProvider instance is set directly on the JcaContextHelper.securityProvider property (instead of indirectly via the JCA apparatus of Security.addProvider() / Security.getProvider()).

@smirandamedallia
Copy link
Contributor Author

@justinludwig Thanks for the quick response. i have addressed the changes and have updated the version to 1.2 since it is non-breaking change

@justinludwig justinludwig merged commit ca31412 into justinludwig:master Oct 5, 2021
@justinludwig
Copy link
Owner

Thanks! I will push out a release for this to maven central in a day or two.

@smirandamedallia
Copy link
Contributor Author

Thanks

@justinludwig
Copy link
Owner

I pushed JPGPJ version 1.2 with this to maven central.

@justinludwig justinludwig mentioned this pull request Oct 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants