-
Notifications
You must be signed in to change notification settings - Fork 20
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
Add support for JCA security providers #39
Conversation
@@ -763,6 +771,7 @@ hQEMAyne546XDHBhAQ... | |||
meta.verified | |||
} | |||
|
|||
@Ignore |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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()
).
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
|
There was a problem hiding this 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); |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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()
).
@justinludwig Thanks for the quick response. i have addressed the changes and have updated the version to |
Thanks! I will push out a release for this to maven central in a day or two. |
Thanks |
I pushed JPGPJ version 1.2 with this to maven central. |
jcajce
rather thanbc
BC
and default provider