ECDH secrets generated by BouncyCastle Java API and by OpenSSL are different - java

I'm trying to make use of elliptic curve crypto. I need two implementations of the same thing, one in Java and one in C. I'm testing them using two key pairs which were generated using the curve secp256k1. When I generate the derived secret in Java I always get a different number from what I get from OpenSSL.
Java code:
/* privateKey and peerPublicKey are generated with the following parameters */
ECParameterSpec paramSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
/* ... */
Provider BC = new BouncyCastleProvider();
KeyAgreement agr = KeyAgreement.getInstance("ECDH", BC);
agr.init(privateKey);
agr.doPhase(peerPublicKey, true);
byte[] secret = agr.generateSecret();
C code
/* pkey and peerkey are generated using EC_KEY_new_by_curve_name(NID_secp256k1) */
/* and than wrapped in an EVP_PKEY */
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
uint8_t *secret = NULL;
size_t secret_len;
EVP_PKEY_derive_init(ctx);
EVP_PKEY_derive_set_peer(ctx, peerkey);
EVP_PKEY_derive(ctx, NULL, &secret_len);
secret = malloc(secret_len);
EVP_PKEY_derive(ctx, secret, &secret_len);
I'm sure that the keys are valid and that they are the same both in C and in Java code, but I don't understand why the derived secret is different. Am I missing something?
Thanks

EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
uint8_t *secret = NULL;
size_t secret_len;
EVP_PKEY_derive_init(ctx);
EVP_PKEY_derive_set_peer(ctx, peerkey);
EVP_PKEY_derive(ctx, NULL, &secret_len);
secret = malloc(secret_len);
EVP_PKEY_derive(ctx, secret, &secret_len);
This code looks like its missing a few steps. For example, EVP_PKEY_paramgen_init is not present.
The OpenSSL wiki has an example at Elliptic Curve Diffie-Hellman. I'm going to copy/paste it below to avoid the link-only answer, but I believe its the work of Matt Caswell.
EVP_PKEY_CTX *pctx, *kctx;
EVP_PKEY_CTX *ctx;
unsigned char *secret;
EVP_PKEY *pkey = NULL, *peerkey, *params = NULL;
/* Create the context for parameter generation */
if(NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) handleErrors();
/* Initialise the parameter generation */
if(1 != EVP_PKEY_paramgen_init(pctx)) handleErrors();
/* We're going to use the ANSI X9.62 Prime 256v1 curve */
if(1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)) handleErrors();
/* Create the parameter object params */
if (!EVP_PKEY_paramgen(pctx, &params)) handleErrors();
/* Create the context for the key generation */
if(NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) handleErrors();
/* Generate the key */
if(1 != EVP_PKEY_keygen_init(kctx)) handleErrors();
if (1 != EVP_PKEY_keygen(kctx, &pkey)) handleErrors();
/* Get the peer's public key, and provide the peer with our public key -
* how this is done will be specific to your circumstances */
peerkey = get_peerkey(pkey);
/* Create the context for the shared secret derivation */
if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) handleErrors();
/* Initialise */
if(1 != EVP_PKEY_derive_init(ctx)) handleErrors();
/* Provide the peer public key */
if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) handleErrors();
/* Determine buffer length for shared secret */
if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) handleErrors();
/* Create the buffer */
if(NULL == (secret = OPENSSL_malloc(*secret_len))) handleErrors();
/* Derive the shared secret */
if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) handleErrors();
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peerkey);
EVP_PKEY_free(pkey);
EVP_PKEY_CTX_free(kctx);
EVP_PKEY_free(params);
EVP_PKEY_CTX_free(pctx);
/* Never use a derived secret directly. Typically it is passed
* through some hash function to produce a key */
return secret;
When I generate the derived secret in Java I always get a different number from what I get from OpenSSL.
Each run of the protocol will produce different results. That's because each party picks a random value for each run of the protocol. That is, the a in g^a is random and different for each run, so the public key A = g^a is different for each run.
If everything is working correctly, you'll never see the parties use the same values, or one party to reuse a past value. Independent executions will never produce the same result. It does not matter if its OpenSSL ↔ OpenSSL, OpenSSL ↔ Java, or Java ↔ Java. They will always produce different results.

Related

How to put a KeyPair into a KeyStore the right way? (programmatically in Java)

Its best to show my problem and explain it after (I am using KeyStore Explorer to peek into my .pfx files):
Basically, I want the result on the right, but I get the result on the left.
To be more precise: The KeyStore should contain the private key and certs chain (KeyPair), in one entry.
I somehow couldn't get it working in java. This is what I tried to get the result on the left:
The problem with the code below is, that its not verifing the certs and adding them as trusted certs.
/**
* Creates and returns a new {#link KeyStore} from the provided information.
* #param keystoreType The {#link KeyStore}s type. Recommended: "pkcs12".
* Check {#link KeyStore#getInstance(String)} for details.
* #param passwordAsCharArray Password to encrypt this {#link KeyStore} and its keys.
* #param certificateChain The certificate chain is simply an array of {#link X509Certificate}s.
* #param privateKey The {#link PrivateKey}.
* #param publicKey The {#link PublicKey}.
* #throws KeyStoreException
*/
public KeyStore buildAndGetKeystore(String keystoreType, char[] passwordAsCharArray,
X509Certificate[] certificateChain, PrivateKey privateKey, PublicKey publicKey)
throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, EmptyStringException, EmptyCharArrayException, EmptyCertificateChainException {
// Check for null parameters
Objects.requireNonNull(keystoreType);
Objects.requireNonNull(passwordAsCharArray);
Objects.requireNonNull(certificateChain);
Objects.requireNonNull(privateKey);
Objects.requireNonNull(publicKey);
// Check for empty parameters
if (keystoreType.isEmpty()) throw new EmptyStringException("Parameter 'keystoreType' should NOT be empty!");
if (passwordAsCharArray.length==0) throw new EmptyCharArrayException("Parameter 'passwordAsCharArray' should NOT be empty!");
if (certificateChain.length==0) throw new EmptyCertificateChainException("Parameter 'certificateChain' should NOT be empty!");
// Initialise a new keystore
KeyStore keystore = KeyStore.getInstance(keystoreType);
keystore.load(null, passwordAsCharArray); // Pass null to tell java this is a new keystore
// Insert certificates
for (int i = 0; i < certificateChain.length; i++) {
keystore.setCertificateEntry(""+i, certificateChain[i]);
}
// Write private key (with password protection) to keystore.
// NOTE: I tried this before and it only writes
// the private key to the .pfx file and ignores the domain chain
//keystore.setKeyEntry("sso-signing-key", privateKey, passwordAsCharArray, certificateChain);
return keystore;
}
Some extra details:
The KeyStore on the right was created like this: First generated the certs at sslforfree.com and then converted them to a PKCS12 KeyStore with https://decoder.link/converter
I created my own certificate chain and used this structure for the certificateChain:
X509Certificate[] certificateChain = new X509Certificate[3];
certificateChain[0] = topCertificate;
certificateChain[1] = middleCertificate;
certificateChain[2] = rootCertificate;
The keystore creation part is as simple like this (I left out the "notNull checks"):
KeyStore keystore = KeyStore.getInstance(keystoreType);
keystore.load(null,null);
keystore.setKeyEntry("use_your_own_alias", privateKey, passwordAsCharArray, certificateChain);
return keystore;
Kindly note that the publicKey is NOT part of the storage process as it is available with
certificateChain[0].getPublicKey()
Checking the keystore (I used a PKCS12 one) in KeyStoreExplorer gives this output:
and the key chain:
You guys won't believe this, but... The problem was the alias name 'priv-key'. Just removed the hyphen '-' and changed the alias name to 'myKey' like in https://stackoverflow.com/a/67147429/13600212 and voila it works...
So many hours lost for such a stupid thing...

How to get Certificate for encrypting mail into Java without private key?

I want to send an encrypted mail in java. BouncyCastle (Release 1.6.4) seems to be popular to do so. In their example "CreateLargeEncryptedMail.java" you find:
/**
* a simple example that creates a single encrypted mail message.
* <p>
* The key store can be created using the class in
* org.bouncycastle.jce.examples.PKCS12Example - the program expects only one
* key to be present in the key file.
* <p>
* Note: while this means that both the private key is available to
* the program, the private key is retrieved from the keystore only for
* the purposes of locating the corresponding public key, in normal circumstances
* you would only be doing this with a certificate available.
*/
public class CreateLargeEncryptedMail
{
public static void main(
String args[])
throws Exception
{
if (args.length != 3)
{
System.err.println("usage: CreateLargeEncryptedMail pkcs12Keystore password inputFile");
System.exit(0);
}
//
// Open the key store
//
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
String keyAlias = ExampleUtils.findKeyAlias(ks, args[0], args[1].toCharArray());
Certificate[] chain = ks.getCertificateChain(keyAlias);
But ks.getCertificateChain() does not work without the private key and usually I do not have the private key of a recipient.
In my tries it returned null. From documentation
Returns the certificate chain associated with the given alias. The certificate chain must have been associated with the alias by a call to setKeyEntry, or by a call to setEntry with a PrivateKeyEntry.
But I do not have the private key.
An other way would be to use CertificateFactory.getInstance("X.509"); is there a way to decrypt smime public key data.
But I only come to
java.security.cert.CertificateParsingException: signed fields invalid
Found stackoverflow to that exception, but the solution uses KeyStore.getCertificate() again.
I have: A certificat suitable for SMIME in Windows trust store. The certificat works in outlook. I can export the certificat to a file.
I want: A java object of type Certificate (X509Certificate) working for SMIME with BounceCastle.
So what kind of file do I have to create with which tool and what to do in Java to get this X509Certificate initialized? Do I need the single certificat or a chain in that file? The certificat is self signed.
BouncyCastle not only supports SMIME encryption but also contains a CertificateFactory which can load a p7b-file I exported from Windows certmgr. For export I chose without private key and with key-chain. That file worked for me using:
import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
...
/**
* Reads the Certificate from the file with filename.
* Works for p7b-files.
* #param filename the name and path of a key-file.
* #return a Certificate
*/
public static Certificate getCertificate(String filename) {
Certificate cert = null;
try (InputStream is = new FileInputStream(filename)) {
CertificateFactory fact = new CertificateFactory();
cert = fact.engineGenerateCertificate(is);
}
catch (Exception ex) {
ex.printStackTrace();
}
return cert;
}

Java to Objective-C RSA Implementation

I'm in trouble in implementing RSA encryption and decryption in Objective-C, I made it in Java very simply and now I tried to translate this java code in objc.
Here is my java code:
public static byte[] encryptRSA(byte[] text, PublicKey key) throws Exception {
byte[] cipherText = null;
// get an RSA cipher object and print the provider
Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plaintext using the public key
cipher.init(Cipher.ENCRYPT_MODE, key);
cipherText = cipher.doFinal(text);
return cipherText;
}
public static byte[] decryptRSA(byte[] text, PrivateKey key) throws Exception {
byte[] dectyptedText = null;
// decrypt the text using the private key
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
and this is how i generate the key pair
String seed = "SOMERANDOMSEED"+Long.toString(System.currentTimeMillis());
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom rand = new SecureRandom(seed.getBytes());
keyGen.initialize(4096,rand);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
now in objC I have writed some code that sems to work, but I don't know hot to generate the rsa they from a seed, like i do in java, and how to import the key that i save in java with this method
//for import
public static byte[] hexStringToByteArray(String s) {
byte[] b = new byte[s.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(s.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
//for export
public static String byteArrayToHexString(byte[] b) {
StringBuilder sb = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
here is my objc code
//this works properly
+(NSString *)decryptRSA:(NSString *)cipherString key:(SecKeyRef) privateKey {
size_t plainBufferSize = SecKeyGetBlockSize(privateKey);
uint8_t *plainBuffer = malloc(plainBufferSize);
NSData *incomingData = [cipherString decodeFromHexidecimal];
uint8_t *cipherBuffer = (uint8_t*)[incomingData bytes];
size_t cipherBufferSize = SecKeyGetBlockSize(privateKey);
SecKeyDecrypt(privateKey,
kSecPaddingOAEPKey,
cipherBuffer,
cipherBufferSize,
plainBuffer,
&plainBufferSize);
NSData *decryptedData = [NSData dataWithBytes:plainBuffer length:plainBufferSize];
NSString *decryptedString = [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding];
return decryptedString;
}
//this works properly
+(NSString *)encryptRSA:(NSString *)plainTextString key:(SecKeyRef)publicKey {
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKey,
kSecPaddingOAEPKey,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return [encryptedData hexadecimalString];
}
//here i generate the key pair
#define kPublicKeyTag "com.apple.sample.publickey"
#define kPrivateKeyTag "com.apple.sample.privatekey"
//i should use these as seed!?!!?
- (void)generateKeyPair:(NSUInteger)keySize {
OSStatus sanityCheck = noErr;
publicKeyRef = NULL;
privateKeyRef = NULL;
// Container dictionaries.
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
// Set the private key dictionary.
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:privateKeyAttr forKey:(id)#kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(id)#kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
}
this is the method i use to export keys in objc, it seems work just like the java method
+ (NSString *)fromPrivateKeyToString: (SecKeyRef) privateKey {
size_t pkeySize = SecKeyGetBlockSize(privateKey);
NSData* pkeyData = [NSData dataWithBytes:privateKey length:pkeySize];
NSString* pkeyString = [pkeyData hexadecimalString];
return pkeyString;
}
Although it is not impossible to create the same key pair from a seed, you need to make sure that both the RNG and the key pair generation are exactly identical. Furthermore, the seed to be put in the generator needs to be used in the same way. Neither the RNG or the key pair generation is usually created with compatibility in mind. Actually, the default "SHA1PRNG" has even changed between versions of Java, and the algorithm is not described.
If you want to use the same private key then it is better to generate it and to transport it to the other runtime. There are multiple ways, but one method is to use a (password) encrypted PKCS#8 or PKCS#12 format. Of course the key or password needs to be kept secret, but that's also the case with your seed value.
For more information, see this Q/A. Don't forget to vote up the question and answer over there, I can use some more points on crypto :).
As I explained in my other answer, it is very tricky to generate the same key pair using the same value of the PRNG. But that does not seem to be what you are after. It seems that you want to use your own seeded PRNG to generate the key pair.
In general, the default SecureRandom in Java is seeded by the operating system. The idea that you can supply your own random number generator is that you may get "better" results using for instance your own entropy pool (e.g. from a a hardware random number generator). Normally the default Java PRNG seeded by the operating system would however provide enough random.
As you are using the SecureRandom class, you supplant the operating system provided seed with your own relatively weakly seeded PRNG. currentTimeMilis certainly does not give you much entropy, and the password seems to be static. This is generally not thought to be enough for generating RSA key pairs.
If you really want to you can add some entropy to the pool instead:
// create runtime default PRNG
SecureRandom rng = new SecureRandom();
// make sure that the rng is seeded by the operating system
rng.nextInt();
// add secret to the pool
rng.setSeed("SOME_SECRET".getBytes(StandardCharsets.UTF_8));
// add time information to the pool
rng.setSeed(System.currentTimeMillis());
// use for e.g. RSA key pair generation
There seems to be no method of injecting your own random number generator in Apple's OS X libraries. As indicated, usually the OS provided random number generator is good enough. If you really want to you can write your additional seeds to /dev/random though.

Compare PublicKey object in java

I have two PublicKey object.I want to compare both for equality or to check which is latest object using java security API or bouncy castle API.How can i achieve this?
You can use equals
if (!key.equals(copyKey)){
System.out.println("not equals!");
}
or check the hashcode of the keys
if (key.hashCode() != copyKey.hashCode())
{
System.out.println("public key hashCode check failed");
}
or compare the hex string of the two public keys
String encodedKey1 = new String(Hex.encode(key1.getEncoded()));
String encodedKey2 = new String(Hex.encode(key2.getEncoded()));
if (!encodedKey1.equals(encodedKey2)){
System.out.println("not equals!");
}
You have a lot of key comparision and check samples at Bouncy Castle Tests, take a look at the org.bouncycastle.jce.provider.test package for some code. BC is not strictly necesary you can do the comparision with the default java security classes.
Normally public keys are compared using some kind of ID. It depends on the protocol how the key ID is calculated. The best method is probably to keep to the PKCS#11 specifications which defines methods of calculating key ID's.
The creation date is not an integral part of the key itself. Either you have to define it elsewhere, or you should use a public key container such as an X509 certificate. Note that you could use the (hex representation of the) key ID to find the creation date in a map.
It's probably best to use a SHA-1 hash over the modulus as ID. The modulus of both the public key and private key are identical and should be different for each key pair. The following code calculates the ID for an RSA public key.
Obviously you can always directly compare the moduli of two keys as well. Key ID's are a bit easier to store though.
public class CreateRSAPublicKeyID {
/**
* Creates a key ID for a given key.
*
* #param key the key
* #return the key ID for the given key
*/
public static byte[] createKeyID(Key key) {
if (key instanceof RSAKey) {
RSAKey rsaKey = (RSAKey) key;
BigInteger modulus = rsaKey.getModulus();
if (modulus.bitLength() % Byte.SIZE != 0) {
throw new IllegalArgumentException("This method currently only works with RSA key sizes that are a multiple of 8 in bits");
}
final byte[] modulusData = i2os(modulus, modulus.bitLength() / Byte.SIZE);
MessageDigest sha1;
try {
sha1 = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-1 message digest should be available in any Java SE runtime", e);
}
return sha1.digest(modulusData);
}
throw new UnsupportedOperationException("Key type not supported");
}
/**
* Integer to octet string (I2OS) creates a fixed size, left padded, big-endian octet string representation for
* a given integer.
*
* #param i the integer
* #param octets the number of octets (bytes)
* #return the octet string representation of i
*/
public static byte[] i2os(BigInteger i, int octets) {
if (i.bitLength() > octets * Byte.SIZE) {
throw new IllegalArgumentException("i does not fit in " + octets + " octets");
}
final byte[] is = i.toByteArray();
if (is.length == octets) {
return is;
}
final byte[] ius = new byte[octets];
if (is.length == octets + 1) {
System.arraycopy(is, 1, ius, 0, octets);
} else {
System.arraycopy(is, 0, ius, octets - is.length, is.length);
}
return ius;
}
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.length; i++) {
sb.append(String.format("%02X", data[i]));
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair pair = kpg.generateKeyPair();
byte[] keyID = createKeyID(pair.getPublic());
System.out.println(toHex(keyID));
}
}
Note that the getModulus() command may not be compatible with some keystores (e.g. those ones that represent HSM tokens or smart cards).
Lookin at Oracle's documentation, I think you can compare PublicKey using its 3 getters : getAlgorithm, getEncoded, getFormat doing this :
oldKey.getAlgorithm().equals(newKey.getAlgorithm()) and so on.

Decrypt gpg in Java without using Java.Runtime

I have a .gpg file and a RSA private key. How can I programatically decrypt it without using operating system? e.g. without using something like Runtime.getRuntime().exec("gpg -decrypt.....");
Libraries I've found all run operating system. Like GnuPG or gnugpg-for-java.
As Skyr mentioned: Bouncy Castle is the way to go.
What do you want to do with this key? If your goal is to en- or decrypt files you might want to take a look at bouncy-gpg (shameless plug: I wrote it).
Using secret keys is actually three steps
Parse the key and put it into a PGPSecretKeyRing
Extract the secret key from the keyring
Decrypt it with the password
1. Parsing the exported key
In any case look here for the part that parses keys:
class ...
private PGPSecretKeyRingCollection secretKeyRings = new PGPSecretKeyRingCollection(EMPTY_LIST);
...
/**
* Add a new secret keyring to the public keyrings.
* .
* Can read the result of "gpg --export" and "gpg --export -a keyid"
* .
* E.g. "gpg --export-secret-key -a keyid":
* addSecretKey("-----BEGIN PGP PRIVATE KEY BLOCK----- ....".getBytes("US-ASCII")
* <p>
* The password is queried via the callback (decryptionSecretKeyPassphraseForSecretKeyId).
*
* #param encodedPrivateKey the key ascii armored or binary
* #throws IOException IO is dangerous
* #throws PGPException E.g. this is nor a valid key
*/
public void addSecretKey(byte[] encodedPrivateKey) throws IOException, PGPException {
if (encodedPrivateKey == null) {
throw new NullPointerException("encodedPrivateKey must not be null");
}
try (
final InputStream raw = new ByteArrayInputStream(encodedPrivateKey);
final InputStream decoded = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(raw)
) {
PGPSecretKeyRing pgpPrivate = new PGPSecretKeyRing(decoded, getKeyFingerPrintCalculator());
this.secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(this.secretKeyRings, pgpPrivate);
}
}
2. Getting the key from the keyring
final PGPSecretKeyRingCollection pgpSec = ...
final PGPSecretKey encryptedKey = pgpSec.getSecretKey(keyID);
3. Decrypting the key
Later you have to decrypt the key using a password like so:
/**
* Decrypt an encrypted PGP secret key.
*
* #param encryptedKey An encrypted key
* #param passphrase The passphrase for the key
* #return the decrypted secret key
* #throws PGPException E.g. wrong passphrase
*/
public static PGPPrivateKey extractPrivateKey(PGPSecretKey encryptedKey, final char[] passphrase) throws PGPException {
LOGGER.debug("Extracting secret key with key ID '0x{}'", Long.toHexString(encryptedKey.getKeyID()));
PGPDigestCalculatorProvider calcProvider = new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build();
PBESecretKeyDecryptor decryptor = new JcePBESecretKeyDecryptorBuilder(
calcProvider).setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(passphrase);
return encryptedKey.extractPrivateKey(decryptor);
}
there are too many examples that I've tried on Bouncy Castle with PGP. The common issue is keyID can't be found in KeyRing.
So, I found #Jens' bouncy-gpg (not sure if he still maintains it.)
Here is his documentation from github.io. It's simple to follow and works!
https://neuhalje.github.io/bouncy-gpg/
The Bouncy Castle library provides (among other features) an OpenPGP implementation. The package org.bouncycastle.openpgp.examples contains several usage examples, one of them showing how to encrypt/decrypt a file using a public/secret key pair (you can have a look at the examples on GrepCode or on the project's Github mirror).

Categories