Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -526,25 +526,62 @@ public Date getLastModificationDateAt(Date evaluationTime)
* @return merged certificate
* @throws IOException if the armored data cannot be processed
* @throws PGPException if a protocol level error occurs
*
* @deprecated use non-static {@link #join(String)} instead.
*/
@Deprecated
public static OpenPGPCertificate join(OpenPGPCertificate certificate, String armored)
throws IOException, PGPException
{
return certificate.join(armored);
}

/**
* Join two copies of the same {@link OpenPGPCertificate}, merging its {@link OpenPGPCertificateComponent components}
* into a single instance.
*
* @param certificate base certificate
* @param other copy of the same certificate, potentially carrying a different set of components
* @return merged certificate
* @throws PGPException if a protocol level error occurs
* @deprecated use non-static {@link #join(OpenPGPCertificate)} instead.
*/
@Deprecated
public static OpenPGPCertificate join(OpenPGPCertificate certificate, OpenPGPCertificate other)
throws PGPException
{
return certificate.join(other);
}

/**
* Join two copies of the same {@link OpenPGPCertificate}, merging its {@link OpenPGPCertificateComponent components}
* into a single instance.
* The ASCII armored {@link String} might contain more than one {@link OpenPGPCertificate}.
* Items that are not a copy of the base certificate are silently ignored.
*
* @param armored ASCII armored {@link String} containing one or more copies of this certificate,
* possibly containing a different set of components
* @return merged certificate
* @throws IOException if the armored data cannot be processed
* @throws PGPException if a protocol level error occurs
*/
public OpenPGPCertificate join(String armored)
throws PGPException, IOException
{
ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes());
InputStream decoderStream = PGPUtil.getDecoderStream(bIn);
BCPGInputStream wrapper = BCPGInputStream.wrap(decoderStream);
PGPObjectFactory objFac = certificate.implementation.pgpObjectFactory(wrapper);
PGPObjectFactory objFac = implementation.pgpObjectFactory(wrapper);

Object next;
while ((next = objFac.nextObject()) != null)
{
if (next instanceof PGPPublicKeyRing)
{
PGPPublicKeyRing publicKeys = (PGPPublicKeyRing)next;
OpenPGPCertificate otherCert = new OpenPGPCertificate(publicKeys, certificate.implementation);
OpenPGPCertificate otherCert = new OpenPGPCertificate((PGPPublicKeyRing) next, implementation);
try
{
return join(certificate, otherCert);
return join(otherCert);
}
catch (IllegalArgumentException e)
{
Expand All @@ -554,7 +591,8 @@ public static OpenPGPCertificate join(OpenPGPCertificate certificate, String arm

else if (next instanceof PGPSecretKeyRing)
{
throw new IllegalArgumentException("Joining with a secret key is not supported.");
throw new IllegalArgumentException("Joining certificate with a secret key is not supported." +
" Try the other way round.");
}

else if (next instanceof PGPSignatureList)
Expand All @@ -564,34 +602,33 @@ else if (next instanceof PGPSignatureList)
// (self-signatures) or by a 3rd party (delegations / delegation revocations)
PGPSignatureList signatures = (PGPSignatureList)next;

PGPPublicKeyRing publicKeys = certificate.getPGPPublicKeyRing();
PGPPublicKeyRing publicKeys = getPGPPublicKeyRing();
PGPPublicKey primaryKey = publicKeys.getPublicKey();
for (Iterator<PGPSignature> it = signatures.iterator(); it.hasNext(); )
{
primaryKey = PGPPublicKey.addCertification(primaryKey, it.next());
}
publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, primaryKey);
return new OpenPGPCertificate(publicKeys, certificate.implementation);
return new OpenPGPCertificate(publicKeys, implementation);
}
}
return null;
return this;
}

/**
* Join two copies of the same {@link OpenPGPCertificate}, merging its {@link OpenPGPCertificateComponent components}
* into a single instance.
*
* @param certificate base certificate
* @param other copy of the same certificate, potentially carrying a different set of components
* @param other copy of this certificate, potentially carrying a different set of components
* @return merged certificate
* @throws PGPException if a protocol level error occurs
*/
public static OpenPGPCertificate join(OpenPGPCertificate certificate, OpenPGPCertificate other)
public OpenPGPCertificate join(OpenPGPCertificate other)
throws PGPException
{
PGPPublicKeyRing joined = PGPPublicKeyRing.join(
certificate.getPGPPublicKeyRing(), other.getPGPPublicKeyRing());
return new OpenPGPCertificate(joined, certificate.implementation);
getPGPPublicKeyRing(), other.getPGPPublicKeyRing());
return new OpenPGPCertificate(joined, implementation);
}

/**
Expand Down
121 changes: 121 additions & 0 deletions pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package org.bouncycastle.openpgp.api;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.KeyIdentifier;
Expand All @@ -18,9 +22,15 @@
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyValidationException;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.api.exception.KeyPassphraseException;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider;
Expand Down Expand Up @@ -210,6 +220,117 @@ public byte[] getEncoded(PacketFormat packetFormat)
return bOut.toByteArray();
}

/**
* Return a new instance of this key with the updated signatures or subkeys from the given
* ASCII armored stream merged into.
*
* @param armored ASCII armored key, certificate or key signatures
* @return key with merged material
* @throws IOException if the key material cannot be decoded
* @throws PGPException if the key material cannot be decoded
*/
@Override
public OpenPGPKey join(String armored)
throws IOException, PGPException
{
ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes());
InputStream decoderStream = PGPUtil.getDecoderStream(bIn);
BCPGInputStream wrapper = BCPGInputStream.wrap(decoderStream);
PGPObjectFactory objFac = implementation.pgpObjectFactory(wrapper);

OpenPGPCertificate otherCert;

Object next;
while ((next = objFac.nextObject()) != null)
{
if (next instanceof PGPPublicKeyRing)
{
PGPPublicKeyRing publicKeys = (PGPPublicKeyRing)next;
otherCert = new OpenPGPCertificate(publicKeys, implementation);
try
{
return join(otherCert);
}
catch (IllegalArgumentException e)
{
// skip over wrong certificate
}
}

else if (next instanceof PGPSecretKeyRing)
{
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) next;
otherCert = new OpenPGPKey(secretKeys, implementation);
try
{
return join(otherCert);
} catch (IllegalArgumentException e)
{
// skip over wrong certificate
}
}

else if (next instanceof PGPSignatureList)
{
// parse and join delegations / revocations
// those are signatures of type DIRECT_KEY or KEY_REVOCATION issued either by the primary key itself
// (self-signatures) or by a 3rd party (delegations / delegation revocations)
PGPSignatureList signatures = (PGPSignatureList)next;

PGPPublicKeyRing publicKeys = getPGPPublicKeyRing();
PGPPublicKey primaryKey = publicKeys.getPublicKey();
for (Iterator<PGPSignature> it = signatures.iterator(); it.hasNext(); )
{
primaryKey = PGPPublicKey.addCertification(primaryKey, it.next());
}
publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, primaryKey);
PGPSecretKeyRing secretKeys = PGPSecretKeyRing.replacePublicKeys(getPGPKeyRing(), publicKeys);
return new OpenPGPKey(secretKeys, implementation);
}
}
return this;
}

@Override
public OpenPGPKey join(OpenPGPCertificate keyOrCertificate)
throws PGPException
{
if (!getKeyIdentifier().matchesExplicit(keyOrCertificate.getKeyIdentifier()))
{
throw new IllegalArgumentException("Not the same OpenPGP key/certificate: Mismatched primary key.");
}

PGPSecretKeyRing secretKeys = getPGPSecretKeyRing();
if (keyOrCertificate.isSecretKey())
{
OpenPGPKey otherKey = (OpenPGPKey) keyOrCertificate;

// existing secret keys
List<PGPSecretKey> sks = new ArrayList<>();
for (PGPSecretKey sk : getPGPSecretKeyRing())
{
sks.add(sk);
}

// merge new secret keys
for (PGPSecretKey sk : otherKey.getPGPSecretKeyRing())
{
if (getPGPSecretKeyRing().getSecretKey(sk.getKeyIdentifier()) == null)
{
sks.add(sk);
}
}

secretKeys = new PGPSecretKeyRing(sks);
}

// merge the public parts
OpenPGPCertificate joinedCertificate = toCertificate().join(keyOrCertificate);
// join secret and public parts
secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, joinedCertificate.getPGPPublicKeyRing());
return new OpenPGPKey(secretKeys, implementation);
}

/**
* Secret key component of a {@link org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPPrimaryKey} or
* {@link org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPSubkey}.
Expand Down