org.bouncycastle.openpgp.PGPSignature Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcpg-jdk14 Show documentation
Show all versions of bcpg-jdk14 Show documentation
The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.4. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.
The newest version!
package org.bouncycastle.openpgp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.HashUtils;
import org.bouncycastle.bcpg.KeyIdentifier;
import org.bouncycastle.bcpg.MPInteger;
import org.bouncycastle.bcpg.Packet;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.SignaturePacket;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.TrustPacket;
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.math.ec.rfc8032.Ed25519;
import org.bouncycastle.math.ec.rfc8032.Ed448;
import org.bouncycastle.openpgp.operator.PGPContentVerifier;
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilder;
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Strings;
/**
* A PGP signature object.
*/
public class PGPSignature
extends PGPDefaultSignatureGenerator
{
/**
* The signature is made over some binary data.
* No preprocessing is applied.
*
* This signature type is used to create data signatures.
*
* @see
* RFC9580 - Binary Signature of a Document
*/
public static final int BINARY_DOCUMENT = 0x00;
/**
* The signature is made over text data.
* In a preprocessing step, the text data is canonicalized (line endings may be altered).
*
* This signature type is used to create data signatures.
*
* @see
* RFC9580 - Text Signature of a Canonical Document
*/
public static final int CANONICAL_TEXT_DOCUMENT = 0x01;
/**
* The signature is made only over its own signature subpackets.
*
* @see
* RFC9580 - Standalone Signature
*/
public static final int STAND_ALONE = 0x02;
/**
* Generic certification over a user-id or user-attribute.
* The issuer of a generic certification does not make any claims as to what extent they checked
* the authenticity of the identity claim.
*
* This signature type is used to bind user information to primary keys, or to certify the identity claim
* of a third party.
*
* @see
* RFC9580 - Generic Certification Signature of a User ID and Public Key Packet
*/
public static final int DEFAULT_CERTIFICATION = 0x10;
/**
* Persona certification over a user-id or user-attribute.
* The issuer of a persona certification did explicitly not check the authenticity of the identity claim.
*
* This signature type is used to bind user information to primary keys, or to certify the identity claim
* of a third party.
*
* @see
* RFC9580 - Persona Certification Signature of a User ID and Public Key Packet
*/
public static final int NO_CERTIFICATION = 0x11;
/**
* Casual certification over a user-id or user-attribute.
* The issuer of a casual certification did some casual verification to check the authenticity of the
* identity claim.
*
* This signature type is used to bind user information to primary keys, or to certify the identity claim
* of a third party.
*
* @see
* RFC9580 - Casual Certification of a User ID an Public Key Packet
*/
public static final int CASUAL_CERTIFICATION = 0x12;
/**
* Positive certification over a user-id or user-attribute.
* The issuer of a positive certification did extensive effort to check the authenticity of the identity claim.
*
* This signature type is used to bind user information to primary keys, or to certify the identity claim
* of a third party.
*
* @see
* RFC9580 - Positive Certification Signature of a User ID and Public Key Packet
*/
public static final int POSITIVE_CERTIFICATION = 0x13;
/**
* Subkey Binding Signature to bind a subkey to a primary key.
* This signature type is used to bind a subkey to the primary key of a certificate.
*
* @see
* RFC9580 - Subkey Binding Signature
*/
public static final int SUBKEY_BINDING = 0x18;
/**
* Primary-Key Binding Signature to bind a signing-capable subkey to a primary key.
* This (back-) signature is used as an embedded signature in a {@link #SUBKEY_BINDING} signature and acts as
* a claim by the subkey, stating that it is in fact a subkey of the primary key.
*
* @see
* RFC9580 - Primary Key Binding Signature
*/
public static final int PRIMARYKEY_BINDING = 0x19;
/**
* The signature is made directly over a primary key.
* If issued as a self-signature, its contents apply to the whole certificate, meaning this signature
* is appropriate to set algorithm preferences which also apply to its subkeys.
* Issued as a signature over a third-party certificate, it can be used to mark said certificate as a CA.
*
* @see
* RFC9580 - Direct Key Signature
*/
public static final int DIRECT_KEY = 0x1f;
/**
* The signature is used to revoke a primary key (and in turn the whole certificate with all its subkeys).
*
* @see
* RFC9580 - Key Revocation Signature
*/
public static final int KEY_REVOCATION = 0x20;
/**
* The signature is used to revoke the binding of a particular subkey.
*
* @see
* RFC9580 - Subkey Revocation Signature
*/
public static final int SUBKEY_REVOCATION = 0x28;
/**
* The signature is used to revoke a user-id certification signature
* ({@link #DEFAULT_CERTIFICATION}, {@link #NO_CERTIFICATION}, {@link #CASUAL_CERTIFICATION},
* {@link #POSITIVE_CERTIFICATION}) or {@link #DIRECT_KEY} signature.
* Issued as a self-signature, it can be used to revoke an identity claim.
* Issued over a third-party certificate, it revokes the attestation of the third-party's claim.
*
* @see
* RFC9580 - Certification Revocation Signature
*/
public static final int CERTIFICATION_REVOCATION = 0x30;
/**
* The signature is only meaningful for the timestamp contained in it.
*
* @see
* RFC9580 - Timestamp Signature
*/
public static final int TIMESTAMP = 0x40;
/**
* This signature is issued over another signature and can act as an attestation of that signature.
* This concept can be used to "approve" third-party certifications over the own key, allowing
* third-party certifications to be published on key-servers that usually strip such signatures
* to prevent certificate flooding.
*
* @see
* RFC9580 - Third-Party Confirmation Signature/a>
*/
public static final int THIRD_PARTY_CONFIRMATION = 0x50;
private final SignaturePacket sigPck;
private final TrustPacket trustPck;
private volatile PGPContentVerifier verifier;
private static SignaturePacket cast(Packet packet)
throws IOException
{
if (!(packet instanceof SignaturePacket))
{
throw new IOException("unexpected packet in stream: " + packet);
}
return (SignaturePacket)packet;
}
/**
* Parse a {@link PGPSignature} from an OpenPGP packet input stream.
* @param pIn packet input stream
* @throws IOException
* @throws PGPException
*/
public PGPSignature(
BCPGInputStream pIn)
throws IOException, PGPException
{
this(cast(pIn.readPacket()));
}
PGPSignature(
PGPSignature signature)
{
super(signature.getVersion());
sigPck = signature.sigPck;
sigType = signature.sigType;
trustPck = signature.trustPck;
}
PGPSignature(
SignaturePacket sigPacket)
{
this(sigPacket, null);
}
PGPSignature(
SignaturePacket sigPacket,
TrustPacket trustPacket)
{
super(sigPacket.getVersion());
this.sigPck = sigPacket;
this.sigType = sigPck.getSignatureType();
this.trustPck = trustPacket;
}
/**
* Return the OpenPGP version number for this signature.
*
* @return signature version number.
*/
public int getVersion()
{
return sigPck.getVersion();
}
/**
* Return the key algorithm associated with this signature.
*
* @return signature key algorithm.
*/
public int getKeyAlgorithm()
{
return sigPck.getKeyAlgorithm();
}
/**
* Return the hash algorithm associated with this signature.
*
* @return signature hash algorithm.
*/
public int getHashAlgorithm()
{
return sigPck.getHashAlgorithm();
}
/**
* Return the digest prefix of the signature.
*
* @return digest prefix
*/
public byte[] getDigestPrefix()
{
return sigPck.getFingerPrint();
}
/**
* Return true if this signature represents a certification.
*
* @return true if this signature represents a certification, false otherwise.
*/
public boolean isCertification()
{
return isCertification(getSignatureType());
}
/**
* Initialize the signature for verification.
*
* @param verifierBuilderProvider provide the implementation for signature verification
* @param pubKey issuer public key
* @throws PGPException
*/
public void init(PGPContentVerifierBuilderProvider verifierBuilderProvider, PGPPublicKey pubKey)
throws PGPException
{
if (sigType == 0xFF)
{
throw new PGPException("Illegal signature type 0xFF provided.");
}
if (getVersion() == SignaturePacket.VERSION_6 && pubKey.getVersion() != PublicKeyPacket.VERSION_6)
{
throw new PGPException("MUST NOT verify v6 signature with non-v6 key.");
}
if (getVersion() == SignaturePacket.VERSION_4 && pubKey.getVersion() != PublicKeyPacket.VERSION_4)
{
throw new PGPException("MUST NOT verify v4 signature with non-v4 key.");
}
PGPContentVerifierBuilder verifierBuilder = createVerifierProvider(verifierBuilderProvider);
init(verifierBuilder.build(pubKey));
}
PGPContentVerifierBuilder createVerifierProvider(PGPContentVerifierBuilderProvider verifierBuilderProvider)
throws PGPException
{
return verifierBuilderProvider.get(sigPck.getKeyAlgorithm(), sigPck.getHashAlgorithm());
}
void init(PGPContentVerifier verifier)
throws PGPException
{
this.verifier = verifier;
this.lastb = 0;
this.sigOut = verifier.getOutputStream();
checkSaltSize();
updateWithSalt();
}
private void checkSaltSize()
throws PGPException
{
if (getVersion() != SignaturePacket.VERSION_6)
{
return;
}
int expectedSaltSize = HashUtils.getV6SignatureSaltSizeInBytes(getHashAlgorithm());
if (expectedSaltSize != sigPck.getSalt().length)
{
throw new PGPException("RFC9580 defines the salt size for " + PGPUtil.getDigestName(getHashAlgorithm()) +
" as " + expectedSaltSize + " octets, but signature has " + sigPck.getSalt().length + " octets.");
}
}
private void updateWithSalt()
throws PGPException
{
if (getVersion() == SignaturePacket.VERSION_6)
{
try
{
sigOut.write(sigPck.getSalt());
}
catch (IOException e)
{
throw new PGPException("Could not update with salt.", e);
}
}
}
/**
* Finish the verification and return true if the signature is "correct".
* Note: The fact that this method returned true
does not yet mean that the signature is valid.
* A correct signature may very well be expired, the issuer key may be revoked, etc.
* All these constraints are not checked by this method.
* @return true if the signature is correct
* @throws PGPException
*/
public boolean verify()
throws PGPException
{
try
{
sigOut.write(this.getSignatureTrailer());
sigOut.close();
}
catch (IOException e)
{
throw new PGPException(e.getMessage(), e);
}
return verifier.verify(this.getSignature());
}
/**
* Verify the signature as certifying the passed in public key as associated
* with the passed in user attributes.
*
* @param userAttributes user attributes the key was stored under
* @param key the key to be verified.
* @return true if the signature matches, false otherwise.
* @throws PGPException
*/
public boolean verifyCertification(
PGPUserAttributeSubpacketVector userAttributes,
PGPPublicKey key)
throws PGPException
{
if (verifier == null)
{
throw new PGPException("PGPSignature not initialised - call init().");
}
if (!PGPSignature.isCertification(sigType)
&& PGPSignature.CERTIFICATION_REVOCATION != sigType)
{
throw new PGPException("signature is neither a certification signature nor a certification revocation.");
}
return doVerifyCertification(userAttributes, key);
}
boolean doVerifyCertification(
PGPUserAttributeSubpacketVector userAttributes,
PGPPublicKey key)
throws PGPException
{
updateWithPublicKey(key);
getAttributesHash(userAttributes);
addTrailer();
return verifier.verify(this.getSignature());
}
/**
* Verify the signature as certifying the passed in public key as associated
* with the passed in id.
*
* @param id id the key was stored under
* @param key the key to be verified.
* @return true if the signature matches, false otherwise.
* @throws PGPException
*/
public boolean verifyCertification(
String id,
PGPPublicKey key)
throws PGPException
{
return verifyCertification(Strings.toUTF8ByteArray(id), key);
}
/**
* Verify the signature as certifying the passed in public key as associated
* with the passed in rawID.
*
* @param rawID id the key was stored under in its raw byte form.
* @param key the key to be verified.
* @return true if the signature matches, false otherwise.
* @throws PGPException
*/
public boolean verifyCertification(
byte[] rawID,
PGPPublicKey key)
throws PGPException
{
if (verifier == null)
{
throw new PGPException("PGPSignature not initialised - call init().");
}
if (!PGPSignature.isCertification(sigType)
&& PGPSignature.CERTIFICATION_REVOCATION != sigType)
{
throw new PGPException("signature is neither a certification signature nor a certification revocation.");
}
return doVerifyCertification(rawID, key);
}
boolean doVerifyCertification(byte[] rawID, PGPPublicKey key)
throws PGPException
{
updateWithPublicKey(key);
//
// hash in the rawID
//
updateWithIdData(0xb4, rawID);
addTrailer();
return verifier.verify(this.getSignature());
}
/**
* Verify a certification for the passed in key against the passed in
* master key.
*
* @param masterKey the key we are verifying against.
* @param pubKey the key we are verifying.
* @return true if the certification is valid, false otherwise.
* @throws PGPException
*/
public boolean verifyCertification(
PGPPublicKey masterKey,
PGPPublicKey pubKey)
throws PGPException
{
if (verifier == null)
{
throw new PGPException("PGPSignature not initialised - call init().");
}
if (PGPSignature.SUBKEY_BINDING != sigType
&& PGPSignature.PRIMARYKEY_BINDING != sigType
&& PGPSignature.SUBKEY_REVOCATION != sigType)
{
throw new PGPException("signature is not a key binding signature.");
}
return doVerifyCertification(masterKey, pubKey);
}
boolean doVerifyCertification(
PGPPublicKey masterKey,
PGPPublicKey pubKey)
throws PGPException
{
updateWithPublicKey(masterKey);
updateWithPublicKey(pubKey);
addTrailer();
return verifier.verify(this.getSignature());
}
private void addTrailer()
{
try
{
sigOut.write(sigPck.getSignatureTrailer());
sigOut.close();
}
catch (IOException e)
{
throw new PGPRuntimeOperationException(e.getMessage(), e);
}
}
/**
* Verify a key certification, such as a revocation, for the passed in key.
*
* @param pubKey the key we are checking.
* @return true if the certification is valid, false otherwise.
* @throws PGPException
*/
public boolean verifyCertification(
PGPPublicKey pubKey)
throws PGPException
{
if (verifier == null)
{
throw new PGPException("PGPSignature not initialised - call init().");
}
if (this.getSignatureType() != KEY_REVOCATION
&& this.getSignatureType() != DIRECT_KEY)
{
throw new PGPException("signature is not a key signature");
}
return doVerifyCertification(pubKey);
}
boolean doVerifyCertification(
PGPPublicKey pubKey)
throws PGPException
{
updateWithPublicKey(pubKey);
addTrailer();
return verifier.verify(this.getSignature());
}
/**
* Return the type id of the signature.
* @see
* RFC9580 - Signature Types
*
* @return type id
*/
public int getSignatureType()
{
return sigPck.getSignatureType();
}
/**
* Return the id of the key that created the signature.
* Note: Since signatures of version 4 or later encode the issuer information inside a
* signature subpacket ({@link IssuerKeyID} or {@link IssuerFingerprint}), there is not
* a single source of truth for the key-id.
* To match any suitable issuer keys, use {@link #getKeyIdentifiers()} instead.
*
* @return keyID of the signatures corresponding key.
*/
public long getKeyID()
{
return sigPck.getKeyID();
}
/**
* Create a list of {@link KeyIdentifier} objects, for all {@link IssuerFingerprint}
* and {@link IssuerKeyID} signature subpackets found in either the hashed or unhashed areas
* of the signature.
*
* @return all detectable {@link KeyIdentifier KeyIdentifiers}
*/
public List getKeyIdentifiers()
{
List identifiers = new ArrayList ();
identifiers.addAll(getHashedKeyIdentifiers());
identifiers.addAll(getUnhashedKeyIdentifiers());
return identifiers;
}
/**
* Return a list of all {@link KeyIdentifier KeyIdentifiers} that could be derived from
* any {@link IssuerFingerprint} or {@link IssuerKeyID} subpackets of the hashed signature
* subpacket area.
*
* @return hashed key identifiers
*/
public List getHashedKeyIdentifiers()
{
return extractKeyIdentifiers(sigPck.getHashedSubPackets());
}
/**
* Return a list of all {@link KeyIdentifier KeyIdentifiers} that could be derived from
* any {@link IssuerFingerprint} or {@link IssuerKeyID} subpackets of the unhashed signature
* subpacket area.
*
* @return unhashed key identifiers
*/
public List getUnhashedKeyIdentifiers()
{
return extractKeyIdentifiers(sigPck.getUnhashedSubPackets());
}
private List extractKeyIdentifiers(SignatureSubpacket[] subpackets)
{
List identifiers = new ArrayList ();
for (int idx = 0; idx != subpackets.length; idx++)
{
SignatureSubpacket s = subpackets[idx];
if (s instanceof IssuerFingerprint)
{
IssuerFingerprint issuer = (IssuerFingerprint) s;
identifiers.add(new KeyIdentifier(issuer.getFingerprint()));
}
if (s instanceof IssuerKeyID)
{
IssuerKeyID issuer = (IssuerKeyID) s;
identifiers.add(new KeyIdentifier(issuer.getKeyID()));
}
}
return identifiers;
}
/**
* Return the creation time of the signature.
*
* @return the signature creation time.
*/
public Date getCreationTime()
{
return new Date(sigPck.getCreationTime());
}
public byte[] getSignatureTrailer()
{
return sigPck.getSignatureTrailer();
}
/**
* Return true if the signature has either hashed or unhashed subpackets.
*
* @return true if either hashed or unhashed subpackets are present, false otherwise.
*/
public boolean hasSubpackets()
{
return sigPck.getHashedSubPackets() != null || sigPck.getUnhashedSubPackets() != null;
}
/**
* Return the hashed subpackets of the signature.
* Hashed signature subpackets are covered by the signature.
*
* @return hashed signature subpackets
*/
public PGPSignatureSubpacketVector getHashedSubPackets()
{
return createSubpacketVector(sigPck.getHashedSubPackets());
}
/**
* Return the unhashed subpackets of the signature.
* As unhashed signature subpackets are NOT covered by the signature, an attacker might inject false
* information after the fact, therefore only "self-authenticating" information from this area can
* be trusted.
* Self-authenticating information are for example the {@link org.bouncycastle.bcpg.sig.IssuerKeyID}
* or {@link org.bouncycastle.bcpg.sig.IssuerFingerprint}, whose authenticity can be confirmed by
* verifying the signature using the declared key.
*
* @return unhashed signature subpackets
*/
public PGPSignatureSubpacketVector getUnhashedSubPackets()
{
return createSubpacketVector(sigPck.getUnhashedSubPackets());
}
private PGPSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks)
{
if (pcks != null)
{
return new PGPSignatureSubpacketVector(pcks);
}
return null;
}
/**
* Return the salt of a v6 signature.
* @return salt
*/
byte[] getSalt()
{
return sigPck.getSalt();
}
/**
* Return the cryptographic raw signature contained in the OpenPGP signature packet.
* The value is dependent on the signing algorithm.
* @return cryptographic signature
* @throws PGPException
*/
public byte[] getSignature()
throws PGPException
{
MPInteger[] sigValues = sigPck.getSignature();
byte[] signature;
if (sigValues != null)
{
if (sigValues.length == 1) // an RSA signature
{
signature = BigIntegers.asUnsignedByteArray(sigValues[0].getValue());
}
else if (getKeyAlgorithm() == PublicKeyAlgorithmTags.EDDSA_LEGACY)
{
byte[] a = BigIntegers.asUnsignedByteArray(sigValues[0].getValue());
byte[] b = BigIntegers.asUnsignedByteArray(sigValues[1].getValue());
if (a.length + b.length > Ed25519.SIGNATURE_SIZE)
{
signature = new byte[Ed448.SIGNATURE_SIZE];
System.arraycopy(a, 0, signature, Ed448.PUBLIC_KEY_SIZE - a.length, a.length);
System.arraycopy(b, 0, signature, Ed448.SIGNATURE_SIZE - b.length, b.length);
}
else
{
signature = new byte[Ed25519.SIGNATURE_SIZE];
System.arraycopy(a, 0, signature, Ed25519.PUBLIC_KEY_SIZE - a.length, a.length);
System.arraycopy(b, 0, signature, Ed25519.SIGNATURE_SIZE - b.length, b.length);
}
}
else
{
try
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(sigValues[0].getValue()));
v.add(new ASN1Integer(sigValues[1].getValue()));
signature = new DERSequence(v).getEncoded();
}
catch (IOException e)
{
throw new PGPException("exception encoding DSA sig.", e);
}
}
}
else
{
signature = sigPck.getSignatureBytes();
}
return signature;
}
/**
* Return the OpenPGP packet encoding of the signature.
* @return OpenPGP packet encoding
* @throws IOException
*/
public byte[] getEncoded()
throws IOException
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
this.encode(bOut);
return bOut.toByteArray();
}
/**
* Return an encoding of the signature, with trust packets stripped out if forTransfer is true.
*
* @param forTransfer if the purpose of encoding is to send key to other users.
* @return a encoded byte array representing the key.
* @throws IOException in case of encoding error.
*/
public byte[] getEncoded(boolean forTransfer)
throws IOException
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
this.encode(bOut, forTransfer);
return bOut.toByteArray();
}
/**
* Encode the signature to an OpenPGP packet stream.
* This method does not strip out any trust packets.
* @param outStream packet stream
* @throws IOException
*/
public void encode(
OutputStream outStream)
throws IOException
{
encode(outStream, false);
}
/**
* Encode the signature to outStream, with trust packets stripped out if forTransfer is true.
*
* @param outStream stream to write the key encoding to.
* @param forTransfer if the purpose of encoding is to send key to other users.
* @throws IOException in case of encoding error.
*/
public void encode(
OutputStream outStream,
boolean forTransfer)
throws IOException
{
// Exportable signatures MUST NOT be exported if forTransfer==true
if (forTransfer && (!getHashedSubPackets().isExportable() || !getUnhashedSubPackets().isExportable()))
{
return;
}
BCPGOutputStream out = BCPGOutputStream.wrap(outStream);
out.writePacket(sigPck);
if (!forTransfer && trustPck != null)
{
out.writePacket(trustPck);
}
}
/**
* Return true if the passed in signature type represents a certification, false if the signature type is not.
*
* @param signatureType
* @return true if signatureType is a certification, false otherwise.
*/
public static boolean isCertification(int signatureType)
{
return PGPSignature.DEFAULT_CERTIFICATION == signatureType
|| PGPSignature.NO_CERTIFICATION == signatureType
|| PGPSignature.CASUAL_CERTIFICATION == signatureType
|| PGPSignature.POSITIVE_CERTIFICATION == signatureType;
}
/**
* Return true, if the cryptographic signature encoding of the two signatures match.
* @param sig1 first signature
* @param sig2 second signature
* @return true if both signatures contain the same cryptographic signature
*/
public static boolean isSignatureEncodingEqual(PGPSignature sig1, PGPSignature sig2)
{
return Arrays.areEqual(sig1.sigPck.getSignatureBytes(), sig2.sigPck.getSignatureBytes());
}
/**
* Join two copies of the same signature.
* As an entity might append additional information to an existing signatures unhashed subpacket area
* (e.g. an embedded {@link #THIRD_PARTY_CONFIRMATION} signature), an implementation might want to
* join an existing instance of a signature with an updated copy, e.g. retrieved from a key server.
* This method merges both signature instances by joining unhashed subpackets.
* @param sig1 first signature
* @param sig2 second signature
* @return merged signature
* @throws PGPException
*/
public static PGPSignature join(PGPSignature sig1, PGPSignature sig2)
throws PGPException
{
if (!isSignatureEncodingEqual(sig1, sig2))
{
throw new IllegalArgumentException("These are different signatures.");
}
// merge unhashed subpackets
SignatureSubpacket[] sig1Unhashed = sig1.getUnhashedSubPackets().packets;
SignatureSubpacket[] sig2Unhashed = sig2.getUnhashedSubPackets().packets;
List merged = new ArrayList (java.util.Arrays.asList(sig1Unhashed));
for (int i = 0; i != sig2Unhashed.length; i++)
{
SignatureSubpacket subpacket = sig2Unhashed[i];
boolean found = false;
for (int j = 0; j != sig1Unhashed.length; j++)
{
SignatureSubpacket existing = sig1Unhashed[j];
if (subpacket.equals(existing))
{
found = true;
break;
}
}
if (!found)
{
merged.add(subpacket);
}
}
SignatureSubpacket[] unhashed = (SignatureSubpacket[])merged.toArray(new SignatureSubpacket[0]);
return new PGPSignature(
new SignaturePacket(
sig1.getSignatureType(),
sig1.getKeyID(),
sig1.getKeyAlgorithm(),
sig1.getHashAlgorithm(),
sig1.getHashedSubPackets().packets,
unhashed,
sig1.getDigestPrefix(),
sig1.sigPck.getSignature()
)
);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy