org.apache.camel.converter.crypto.PGPDataFormatUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of camel-crypto Show documentation
Show all versions of camel-crypto Show documentation
Camel Cryptographic Support
/**
* 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.camel.converter.crypto;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ResourceHelper;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.DSA;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.ECDSA;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.ELGAMAL_GENERAL;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.RSA_GENERAL;
import static org.bouncycastle.bcpg.PublicKeyAlgorithmTags.RSA_SIGN;
public final class PGPDataFormatUtil {
private static final Logger LOG = LoggerFactory.getLogger(PGPDataFormatUtil.class);
private PGPDataFormatUtil() {
}
@Deprecated
public static PGPPublicKey findPublicKey(CamelContext context, String filename, String userid, boolean forEncryption)
throws IOException, PGPException, NoSuchProviderException {
return findPublicKey(context, filename, null, userid, forEncryption);
}
@Deprecated
public static PGPPublicKey findPublicKey(CamelContext context, String filename, byte[] keyRing, String userid, boolean forEncryption)
throws IOException, PGPException, NoSuchProviderException {
InputStream is = determineKeyRingInputStream(context, filename, keyRing, forEncryption);
try {
List result = findPublicKeys(is, Collections.singletonList(userid), forEncryption);
if (result.isEmpty()) {
return null;
} else {
return result.get(0);
}
} finally {
IOHelper.close(is);
}
}
public static List findPublicKeys(CamelContext context, String filename, byte[] keyRing, List userids,
boolean forEncryption) throws IOException, PGPException, NoSuchProviderException {
InputStream is = determineKeyRingInputStream(context, filename, keyRing, forEncryption);
try {
return findPublicKeys(is, userids, forEncryption);
} finally {
IOHelper.close(is);
}
}
@Deprecated
public static PGPPublicKey findPublicKeyWithKeyId(CamelContext context, String filename, byte[] keyRing, long keyid,
boolean forEncryption) throws IOException, PGPException, NoSuchProviderException {
InputStream is = determineKeyRingInputStream(context, filename, keyRing, forEncryption);
PGPPublicKey pubKey;
try {
pubKey = findPublicKeyWithKeyId(is, keyid);
} finally {
IOHelper.close(is);
}
return pubKey;
}
public static PGPPublicKeyRingCollection getPublicKeyRingCollection(CamelContext context, String filename, byte[] keyRing, boolean forEncryption) throws IOException, PGPException {
InputStream is = determineKeyRingInputStream(context, filename, keyRing, forEncryption);
try {
return new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(is), new BcKeyFingerprintCalculator());
} finally {
IOHelper.close(is);
}
}
public static PGPPrivateKey findPrivateKeyWithKeyId(CamelContext context, String filename, byte[] secretKeyRing, long keyid,
String passphrase, PGPPassphraseAccessor passpraseAccessor, String provider) throws IOException, PGPException,
NoSuchProviderException {
InputStream is = determineKeyRingInputStream(context, filename, secretKeyRing, true);
try {
return findPrivateKeyWithKeyId(is, keyid, passphrase, passpraseAccessor, provider);
} finally {
IOHelper.close(is);
}
}
private static PGPPrivateKey findPrivateKeyWithKeyId(InputStream keyringInput, long keyid, String passphrase,
PGPPassphraseAccessor passphraseAccessor, String provider) throws IOException, PGPException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyringInput),
new BcKeyFingerprintCalculator());
return findPrivateKeyWithkeyId(keyid, passphrase, passphraseAccessor, provider, pgpSec);
}
public static PGPPrivateKey findPrivateKeyWithkeyId(long keyid, String passphrase, PGPPassphraseAccessor passphraseAccessor,
String provider, PGPSecretKeyRingCollection pgpSec) throws PGPException {
for (Iterator> i = pgpSec.getKeyRings(); i.hasNext();) {
Object data = i.next();
if (data instanceof PGPSecretKeyRing) {
PGPSecretKeyRing keyring = (PGPSecretKeyRing) data;
PGPSecretKey secKey = keyring.getSecretKey(keyid);
if (secKey != null) {
if (passphrase == null && passphraseAccessor != null) {
// get passphrase from accessor // only primary/master key has user IDS
@SuppressWarnings("unchecked")
Iterator userIDs = keyring.getSecretKey().getUserIDs();
while (passphrase == null && userIDs.hasNext()) {
passphrase = passphraseAccessor.getPassphrase(userIDs.next());
}
}
if (passphrase != null) {
PGPPrivateKey privateKey = secKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(provider)
.build(passphrase.toCharArray()));
if (privateKey != null) {
return privateKey;
}
}
}
}
}
return null;
}
private static InputStream determineKeyRingInputStream(CamelContext context, String filename, byte[] keyRing, boolean forEncryption)
throws IOException {
if (filename != null && keyRing != null) {
String encryptionOrSignature;
if (forEncryption) {
encryptionOrSignature = "encryption";
} else {
encryptionOrSignature = "signature";
}
throw new IllegalStateException(String.format("Either specify %s file name or key ring byte array. You can not specify both.",
encryptionOrSignature));
}
InputStream is;
if (keyRing != null) {
is = new ByteArrayInputStream(keyRing);
} else {
is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, filename);
}
return is;
}
private static PGPPublicKey findPublicKeyWithKeyId(InputStream input, long keyid) throws IOException, PGPException,
NoSuchProviderException {
PGPPublicKeyRingCollection pgpSec =
new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input),
new BcKeyFingerprintCalculator());
return pgpSec.getPublicKey(keyid);
}
private static List findPublicKeys(InputStream input, List userids, boolean forEncryption) throws IOException,
PGPException, NoSuchProviderException {
PGPPublicKeyRingCollection pgpSec = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input),
new BcKeyFingerprintCalculator());
return findPublicKeys(userids, forEncryption, pgpSec);
}
public static List findPublicKeys(List useridParts, boolean forEncryption, PGPPublicKeyRingCollection pgpPublicKeyringCollection) {
List result = new ArrayList(useridParts.size());
for (Iterator keyRingIter = pgpPublicKeyringCollection.getKeyRings(); keyRingIter.hasNext();) {
PGPPublicKeyRing keyRing = keyRingIter.next();
PGPPublicKey primaryKey = keyRing.getPublicKey();
String[] foundKeyUserIdForUserIdPart = findFirstKeyUserIdContainingOneOfTheParts(useridParts, primaryKey);
if (foundKeyUserIdForUserIdPart == null) {
LOG.debug("No User ID found in primary key with key ID {} containing one of the parts {}", primaryKey.getKeyID(),
useridParts);
continue;
}
LOG.debug("User ID {} found in primary key with key ID {} containing one of the parts {}", new Object[] {
foundKeyUserIdForUserIdPart[0], primaryKey.getKeyID(), useridParts });
// add adequate keys to the result
for (Iterator keyIter = keyRing.getPublicKeys(); keyIter.hasNext();) {
PGPPublicKey key = keyIter.next();
if (forEncryption) {
if (isEncryptionKey(key)) {
LOG.debug("Public encryption key with key user ID {} and key ID {} added to the encryption keys",
foundKeyUserIdForUserIdPart[0], Long.toString(key.getKeyID()));
result.add(key);
}
} else if (!forEncryption && isSignatureKey(key)) {
// not used!
result.add(key);
LOG.debug("Public key with key user ID {} and key ID {} added to the signing keys", foundKeyUserIdForUserIdPart[0],
Long.toString(key.getKeyID()));
}
}
}
return result;
}
private static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
return false;
}
//check keyflags
Boolean hasEncryptionKeyFlags = hasOneOfExpectedKeyFlags(key, new int[] {KeyFlags.ENCRYPT_COMMS, KeyFlags.ENCRYPT_STORAGE });
if (hasEncryptionKeyFlags != null && !hasEncryptionKeyFlags) {
LOG.debug(
"Public key with key key ID {} found for specified user ID. But this key will not be used for the encryption, because its key flags are not encryption key flags.",
Long.toString(key.getKeyID()));
return false;
} else {
// also without keyflags (hasEncryptionKeyFlags = null), true is returned!
return true;
}
}
// Within a public keyring, the master / primary key has the user ID(s); the subkeys don't
// have user IDs associated directly to them, but the subkeys are implicitly associated with
// the user IDs of the master / primary key. The master / primary key is the first key in
// the keyring, and the rest of the keys are subkeys.
// http://bouncy-castle.1462172.n4.nabble.com/How-to-find-PGP-subkeys-td1465289.html
private static String[] findFirstKeyUserIdContainingOneOfTheParts(List useridParts, PGPPublicKey primaryKey) {
String[] foundKeyUserIdForUserIdPart = null;
for (@SuppressWarnings("unchecked")
Iterator iterator = primaryKey.getUserIDs(); iterator.hasNext();) {
String keyUserId = iterator.next();
for (String userIdPart : useridParts) {
if (keyUserId.contains(userIdPart)) {
foundKeyUserIdForUserIdPart = new String[] {keyUserId, userIdPart };
}
}
}
return foundKeyUserIdForUserIdPart;
}
private static boolean isSignatureKey(PGPPublicKey key) {
int algorithm = key.getAlgorithm();
return algorithm == RSA_GENERAL || algorithm == RSA_SIGN || algorithm == DSA || algorithm == ECDSA || algorithm == ELGAMAL_GENERAL;
}
@Deprecated
public static PGPPrivateKey findPrivateKey(CamelContext context, String keychainFilename, InputStream encryptedInput, String passphrase)
throws IOException, PGPException, NoSuchProviderException {
return findPrivateKey(context, keychainFilename, null, encryptedInput, passphrase, "BC");
}
@Deprecated
public static PGPPrivateKey findPrivateKey(CamelContext context, String keychainFilename, byte[] secKeyRing,
InputStream encryptedInput, String passphrase, String provider) throws IOException, PGPException, NoSuchProviderException {
return findPrivateKey(context, keychainFilename, secKeyRing, encryptedInput, passphrase, null, provider);
}
@Deprecated
public static PGPPrivateKey findPrivateKey(CamelContext context, String keychainFilename, byte[] secKeyRing,
InputStream encryptedInput, String passphrase, PGPPassphraseAccessor passphraseAccessor, String provider) throws IOException,
PGPException, NoSuchProviderException {
InputStream keyChainInputStream = determineKeyRingInputStream(context, keychainFilename, secKeyRing, true);
PGPPrivateKey privKey = null;
try {
privKey = findPrivateKey(keyChainInputStream, encryptedInput, passphrase, passphraseAccessor, provider);
} finally {
IOHelper.close(keyChainInputStream);
}
return privKey;
}
@Deprecated
private static PGPPrivateKey findPrivateKey(InputStream keyringInput, InputStream encryptedInput, String passphrase,
PGPPassphraseAccessor passphraseAccessor, String provider) throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec =
new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyringInput),
new BcKeyFingerprintCalculator());
PGPObjectFactory factory = new PGPObjectFactory(PGPUtil.getDecoderStream(encryptedInput),
new BcKeyFingerprintCalculator());
PGPEncryptedDataList enc;
Object o = factory.nextObject();
if (o == null) {
throw new PGPException("Provided input is not encrypted.");
}
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) factory.nextObject();
}
encryptedInput.reset(); // nextObject() method reads from the InputStream, so rewind it!
Iterator> encryptedDataObjects = enc.getEncryptedDataObjects();
PGPPrivateKey privateKey = null;
PGPPublicKeyEncryptedData encryptedData = null;
while (privateKey == null && encryptedDataObjects.hasNext()) {
encryptedData = (PGPPublicKeyEncryptedData) encryptedDataObjects.next();
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(encryptedData.getKeyID());
if (pgpSecKey != null) {
if (passphrase == null && passphraseAccessor != null) {
// get passphrase from accessor
@SuppressWarnings("unchecked")
Iterator userIDs = pgpSecKey.getUserIDs();
while (passphrase == null && userIDs.hasNext()) {
passphrase = passphraseAccessor.getPassphrase(userIDs.next());
}
}
privateKey = pgpSecKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(provider).build(
passphrase.toCharArray()));
}
}
if (privateKey == null && pgpSec.size() > 0 && encryptedData != null) {
throw new PGPException("Provided input is encrypted with unknown pair of keys.");
}
return privateKey;
}
@Deprecated
public static PGPSecretKey findSecretKey(CamelContext context, String keychainFilename, String passphrase) throws IOException,
PGPException, NoSuchProviderException {
return findSecretKey(context, keychainFilename, null, passphrase, "BC");
}
@Deprecated
public static PGPSecretKey findSecretKey(CamelContext context, String keychainFilename, byte[] secKeyRing, String passphrase,
String userId, String provider) throws IOException, PGPException, NoSuchProviderException {
InputStream keyChainInputStream = determineKeyRingInputStream(context, keychainFilename, secKeyRing, false);
try {
List secKeys = findSecretKeysWithPrivateKeyAndUserId(keyChainInputStream,
Collections.singletonMap(userId, passphrase), provider);
if (!secKeys.isEmpty()) {
return secKeys.get(0).getSecretKey();
}
return null;
} finally {
IOHelper.close(keyChainInputStream);
}
}
public static List findSecretKeysWithPrivateKeyAndUserId(CamelContext context,
String keychainFilename, byte[] secKeyRing, Map sigKeyUserId2Password, String provider) throws IOException,
PGPException, NoSuchProviderException {
InputStream keyChainInputStream = determineKeyRingInputStream(context, keychainFilename, secKeyRing, false);
try {
return findSecretKeysWithPrivateKeyAndUserId(keyChainInputStream, sigKeyUserId2Password, provider);
} finally {
IOHelper.close(keyChainInputStream);
}
}
@Deprecated
public static PGPSecretKey findSecretKey(CamelContext context, String keychainFilename, byte[] secKeyRing, String passphrase,
String provider) throws IOException, PGPException, NoSuchProviderException {
return findSecretKey(context, keychainFilename, secKeyRing, passphrase, null, provider);
}
private static List findSecretKeysWithPrivateKeyAndUserId(InputStream keyringInput,
Map sigKeyUserId2Password, String provider) throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec =
new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyringInput),
new BcKeyFingerprintCalculator());
return findSecretKeysWithPrivateKeyAndUserId(sigKeyUserId2Password, provider, pgpSec);
}
public static List findSecretKeysWithPrivateKeyAndUserId(Map sigKeyUserId2Password,
String provider, PGPSecretKeyRingCollection pgpSec) throws PGPException {
List result = new ArrayList(sigKeyUserId2Password.size());
for (Iterator> i = pgpSec.getKeyRings(); i.hasNext();) {
Object data = i.next();
if (data instanceof PGPSecretKeyRing) {
PGPSecretKeyRing keyring = (PGPSecretKeyRing) data;
PGPSecretKey primaryKey = keyring.getSecretKey();
List useridParts = new ArrayList(sigKeyUserId2Password.keySet());
String[] foundKeyUserIdForUserIdPart = findFirstKeyUserIdContainingOneOfTheParts(useridParts, primaryKey.getPublicKey());
if (foundKeyUserIdForUserIdPart == null) {
LOG.debug("No User ID found in primary key with key ID {} containing one of the parts {}", primaryKey.getKeyID(),
useridParts);
continue;
}
LOG.debug("User ID {} found in primary key with key ID {} containing one of the parts {}", new Object[] {
foundKeyUserIdForUserIdPart[0], primaryKey.getKeyID(), useridParts });
// add all signing keys
for (Iterator iterKey = keyring.getSecretKeys(); iterKey.hasNext();) {
PGPSecretKey secKey = iterKey.next();
if (isSigningKey(secKey)) {
PGPPrivateKey privateKey = secKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(provider)
.build(sigKeyUserId2Password.get(foundKeyUserIdForUserIdPart[1]).toCharArray()));
if (privateKey != null) {
result.add(new PGPSecretKeyAndPrivateKeyAndUserId(secKey, privateKey, foundKeyUserIdForUserIdPart[0]));
LOG.debug("Private key with user ID {} and key ID {} added to the signing keys",
foundKeyUserIdForUserIdPart[0], Long.toString(privateKey.getKeyID()));
}
}
}
}
}
return result;
}
private static boolean isSigningKey(PGPSecretKey secKey) {
if (!secKey.isSigningKey()) {
return false;
}
Boolean hasSigningKeyFlag = hasOneOfExpectedKeyFlags(secKey.getPublicKey(), new int[] {KeyFlags.SIGN_DATA });
if (hasSigningKeyFlag != null && !hasSigningKeyFlag) {
// not a signing key --> ignore
LOG.debug(
"Secret key with key ID {} found for specified user ID part. But this key will not be used for signing because of its key flags.",
Long.toString(secKey.getKeyID()));
return false;
} else {
// also if there are not any keyflags (hasSigningKeyFlag=null), true is returned!
return true;
}
}
/**
* Checks whether one of the signatures of the key has one of the expected
* key flags
*
* @param key
* @return {@link Boolean#TRUE} if key has one of the expected flag,
* null
if the key does not have any key flags,
* {@link Boolean#FALSE} if the key has none of the expected flags
*/
private static Boolean hasOneOfExpectedKeyFlags(PGPPublicKey key, int[] expectedKeyFlags) {
boolean containsKeyFlags = false;
for (@SuppressWarnings("unchecked")
Iterator itsig = key.getSignatures(); itsig.hasNext();) {
PGPSignature sig = itsig.next();
PGPSignatureSubpacketVector subPacks = sig.getHashedSubPackets();
if (subPacks != null) {
int keyFlag = subPacks.getKeyFlags();
if (keyFlag > 0 && !containsKeyFlags) {
containsKeyFlags = true;
}
for (int expectdKeyFlag : expectedKeyFlags) {
int result = keyFlag & expectdKeyFlag;
if (result == expectdKeyFlag) {
return Boolean.TRUE;
}
}
}
}
if (containsKeyFlags) {
return Boolean.FALSE;
}
return null; // no key flag
}
/**
* Determines a public key from the keyring collection which has a certain
* key ID and which has a User ID which contains at least one of the User ID
* parts.
*
* @param keyId
* key ID
* @param userIdParts
* user ID parts, can be empty, than no filter on the User ID is
* executed
* @param publicKeyringCollection
* keyring collection
* @return public key or null
if no fitting key is found
* @throws PGPException
*/
@SuppressWarnings("unchecked")
public static PGPPublicKey getPublicKeyWithKeyIdAndUserID(long keyId, List userIdParts, PGPPublicKeyRingCollection publicKeyringCollection)
throws PGPException {
PGPPublicKeyRing publicKeyring = publicKeyringCollection.getPublicKeyRing(keyId);
if (publicKeyring == null) {
LOG.debug("No public key found for key ID {}.", Long.toString(keyId));
return null;
}
// publicKey can be a subkey the user IDs must therefore be provided by the primary/master key
if (isAllowedKey(userIdParts, publicKeyring.getPublicKey().getUserIDs())) {
return publicKeyring.getPublicKey(keyId);
} else {
return null;
}
}
private static boolean isAllowedKey(List allowedUserIds, Iterator verifyingPublicKeyUserIds) {
if (allowedUserIds == null || allowedUserIds.isEmpty()) {
// no restrictions specified
return true;
}
String keyUserId = null;
for (; verifyingPublicKeyUserIds.hasNext();) {
keyUserId = verifyingPublicKeyUserIds.next();
for (String userid : allowedUserIds) {
if (keyUserId != null && keyUserId.contains(userid)) {
LOG.debug(
"Public key with user ID {} fulfills the User ID restriction.",
keyUserId, allowedUserIds);
return true;
}
}
}
LOG.warn(
"Public key with User ID {} does not fulfill the User ID restriction.",
keyUserId, allowedUserIds);
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy