All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.openpgp.PGPPublicKey Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.5. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.

There is a newer version: 1.46
Show newest version
package org.bouncycastle.openpgp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.bouncycastle.bcpg.BCPGKey;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.ContainedPacket;
import org.bouncycastle.bcpg.DSAPublicBCPGKey;
import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
import org.bouncycastle.bcpg.MPInteger;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyPacket;
import org.bouncycastle.bcpg.RSAPublicBCPGKey;
import org.bouncycastle.bcpg.TrustPacket;
import org.bouncycastle.bcpg.UserAttributePacket;
import org.bouncycastle.bcpg.UserIDPacket;
import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
import org.bouncycastle.jce.spec.ElGamalParameterSpec;
import org.bouncycastle.jce.spec.ElGamalPublicKeySpec;
import org.bouncycastle.util.Arrays;

/**
 * general class to handle a PGP public key object.
 */
public class PGPPublicKey
    implements PublicKeyAlgorithmTags
{
    private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] { PGPSignature.POSITIVE_CERTIFICATION, PGPSignature.CASUAL_CERTIFICATION, PGPSignature.NO_CERTIFICATION, PGPSignature.DEFAULT_CERTIFICATION };
    
    PublicKeyPacket publicPk;
    TrustPacket     trustPk;
    List            keySigs = new ArrayList();
    List            ids = new ArrayList();
    List            idTrusts = new ArrayList();
    List            idSigs = new ArrayList();
    
    List            subSigs = null;

    private long    keyID;
    private byte[]  fingerprint;
    private int     keyStrength;
    
    private void init()
        throws IOException
    {
        BCPGKey                key = publicPk.getKey();
        
        if (publicPk.getVersion() <= 3)
        {
            RSAPublicBCPGKey    rK = (RSAPublicBCPGKey)key;
            
            this.keyID = rK.getModulus().longValue();
            
            try
            {
                MessageDigest   digest = MessageDigest.getInstance("MD5");
            
                byte[]  bytes = new MPInteger(rK.getModulus()).getEncoded();
                digest.update(bytes, 2, bytes.length - 2);
            
                bytes = new MPInteger(rK.getPublicExponent()).getEncoded();
                digest.update(bytes, 2, bytes.length - 2);
            
                this.fingerprint = digest.digest();
            }
            catch (NoSuchAlgorithmException e)
            {
                throw new IOException("can't find MD5");
            }

            this.keyStrength = rK.getModulus().bitLength();
        }
        else
        {
            byte[]             kBytes = publicPk.getEncodedContents();

            try
            {
                MessageDigest   digest = MessageDigest.getInstance("SHA1");
            
                digest.update((byte)0x99);
                digest.update((byte)(kBytes.length >> 8));
                digest.update((byte)kBytes.length);
                digest.update(kBytes);
                
                this.fingerprint = digest.digest();
            }
            catch (NoSuchAlgorithmException e)
            {
                throw new IOException("can't find SHA1");
            }
            
            this.keyID = ((long)(fingerprint[fingerprint.length - 8] & 0xff) << 56)
                            | ((long)(fingerprint[fingerprint.length - 7] & 0xff) << 48)
                            | ((long)(fingerprint[fingerprint.length - 6] & 0xff) << 40)
                            | ((long)(fingerprint[fingerprint.length - 5] & 0xff) << 32)
                            | ((long)(fingerprint[fingerprint.length - 4] & 0xff) << 24)
                            | ((long)(fingerprint[fingerprint.length - 3] & 0xff) << 16)
                            | ((long)(fingerprint[fingerprint.length - 2] & 0xff) << 8)
                            | ((fingerprint[fingerprint.length - 1] & 0xff));
            
            if (key instanceof RSAPublicBCPGKey)
            {
                this.keyStrength = ((RSAPublicBCPGKey)key).getModulus().bitLength();
            }
            else if (key instanceof DSAPublicBCPGKey)
            {
                this.keyStrength = ((DSAPublicBCPGKey)key).getP().bitLength();
            }
            else if (key instanceof ElGamalPublicBCPGKey)
            {
                this.keyStrength = ((ElGamalPublicBCPGKey)key).getP().bitLength();
            }
        }
    }
    
    /**
     * Create a PGPPublicKey from the passed in JCA one.
     * 

* Note: the time passed in affects the value of the key's keyID, so you probably only want * to do this once for a JCA key, or make sure you keep track of the time you used. * * @param algorithm asymmetric algorithm type representing the public key. * @param pubKey actual public key to associate. * @param time date of creation. * @param provider provider to use for underlying digest calculations. * @throws PGPException on key creation problem. * @throws NoSuchProviderException if the specified provider is required and cannot be found. */ public PGPPublicKey( int algorithm, PublicKey pubKey, Date time, String provider) throws PGPException, NoSuchProviderException { this(algorithm, pubKey, time); } public PGPPublicKey( int algorithm, PublicKey pubKey, Date time) throws PGPException { BCPGKey bcpgKey; if (pubKey instanceof RSAPublicKey) { RSAPublicKey rK = (RSAPublicKey)pubKey; bcpgKey = new RSAPublicBCPGKey(rK.getModulus(), rK.getPublicExponent()); } else if (pubKey instanceof DSAPublicKey) { DSAPublicKey dK = (DSAPublicKey)pubKey; DSAParams dP = dK.getParams(); bcpgKey = new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP.getG(), dK.getY()); } else if (pubKey instanceof ElGamalPublicKey) { ElGamalPublicKey eK = (ElGamalPublicKey)pubKey; ElGamalParameterSpec eS = eK.getParameters(); bcpgKey = new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK.getY()); } else { throw new PGPException("unknown key class"); } this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey); this.ids = new ArrayList(); this.idSigs = new ArrayList(); try { init(); } catch (IOException e) { throw new PGPException("exception calculating keyID", e); } } /* * Constructor for a sub-key. */ PGPPublicKey( PublicKeyPacket publicPk, TrustPacket trustPk, List sigs) throws IOException { this.publicPk = publicPk; this.trustPk = trustPk; this.subSigs = sigs; init(); } PGPPublicKey( PGPPublicKey key, TrustPacket trust, List subSigs) { this.publicPk = key.publicPk; this.trustPk = trust; this.subSigs = subSigs; this.fingerprint = key.fingerprint; this.keyID = key.keyID; this.keyStrength = key.keyStrength; } /** * Copy constructor. * @param pubKey the public key to copy. */ PGPPublicKey( PGPPublicKey pubKey) { this.publicPk = pubKey.publicPk; this.keySigs = new ArrayList(pubKey.keySigs); this.ids = new ArrayList(pubKey.ids); this.idTrusts = new ArrayList(pubKey.idTrusts); this.idSigs = new ArrayList(pubKey.idSigs.size()); for (int i = 0; i != pubKey.idSigs.size(); i++) { this.idSigs.add(new ArrayList((ArrayList)pubKey.idSigs.get(i))); } if (pubKey.subSigs != null) { this.subSigs = new ArrayList(pubKey.subSigs.size()); for (int i = 0; i != pubKey.subSigs.size(); i++) { this.subSigs.add(pubKey.subSigs.get(i)); } } this.fingerprint = pubKey.fingerprint; this.keyID = pubKey.keyID; this.keyStrength = pubKey.keyStrength; } PGPPublicKey( PublicKeyPacket publicPk, TrustPacket trustPk, List keySigs, List ids, List idTrusts, List idSigs) throws IOException { this.publicPk = publicPk; this.trustPk = trustPk; this.keySigs = keySigs; this.ids = ids; this.idTrusts = idTrusts; this.idSigs = idSigs; init(); } PGPPublicKey( PublicKeyPacket publicPk, List ids, List idSigs) throws IOException { this.publicPk = publicPk; this.ids = ids; this.idSigs = idSigs; init(); } /** * @return the version of this key. */ public int getVersion() { return publicPk.getVersion(); } /** * @return creation time of key. */ public Date getCreationTime() { return publicPk.getTime(); } /** * @return number of valid days from creation time - zero means no * expiry. */ public int getValidDays() { if (publicPk.getVersion() > 3) { return (int)(this.getValidSeconds() / (24 * 60 * 60)); } else { return publicPk.getValidDays(); } } /** * Return the trust data associated with the public key, if present. * @return a byte array with trust data, null otherwise. */ public byte[] getTrustData() { if (trustPk == null) { return null; } return Arrays.clone(trustPk.getLevelAndTrustAmount()); } /** * @return number of valid seconds from creation time - zero means no * expiry. */ public long getValidSeconds() { if (publicPk.getVersion() > 3) { if (this.isMasterKey()) { for (int i = 0; i != MASTER_KEY_CERTIFICATION_TYPES.length; i++) { long seconds = getExpirationTimeFromSig(true, MASTER_KEY_CERTIFICATION_TYPES[i]); if (seconds >= 0) { return seconds; } } } else { long seconds = getExpirationTimeFromSig(false, PGPSignature.SUBKEY_BINDING); if (seconds >= 0) { return seconds; } } return 0; } else { return (long)publicPk.getValidDays() * 24 * 60 * 60; } } private long getExpirationTimeFromSig( boolean selfSigned, int signatureType) { Iterator signatures = this.getSignaturesOfType(signatureType); if (signatures.hasNext()) { PGPSignature sig = (PGPSignature)signatures.next(); if (!selfSigned || sig.getKeyID() == this.getKeyID()) { PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); if (hashed != null) { return hashed.getKeyExpirationTime(); } return 0; } } return -1; } /** * Return the keyID associated with the public key. * * @return long */ public long getKeyID() { return keyID; } /** * Return the fingerprint of the key. * * @return key fingerprint. */ public byte[] getFingerprint() { byte[] tmp = new byte[fingerprint.length]; System.arraycopy(fingerprint, 0, tmp, 0, tmp.length); return tmp; } /** * Return true if this key has an algorithm type that makes it suitable to use for encryption. *

* Note: with version 4 keys KeyFlags subpackets should also be considered when present for * determining the preferred use of the key. * * @return true if the key algorithm is suitable for encryption. */ public boolean isEncryptionKey() { int algorithm = publicPk.getAlgorithm(); return ((algorithm == RSA_GENERAL) || (algorithm == RSA_ENCRYPT) || (algorithm == ELGAMAL_ENCRYPT) || (algorithm == ELGAMAL_GENERAL)); } /** * Return true if this is a master key. * @return true if a master key. */ public boolean isMasterKey() { return (subSigs == null); } /** * Return the algorithm code associated with the public key. * * @return int */ public int getAlgorithm() { return publicPk.getAlgorithm(); } /** * Return the strength of the key in bits. * * @return bit strenght of key. */ public int getBitStrength() { return keyStrength; } /** * Return the public key contained in the object. * * @param provider provider to construct the key for. * @return a JCE/JCA public key. * @throws PGPException if the key algorithm is not recognised. * @throws NoSuchProviderException if the provider cannot be found. */ public PublicKey getKey( String provider) throws PGPException, NoSuchProviderException { return getKey(PGPUtil.getProvider(provider)); } public PublicKey getKey( Provider provider) throws PGPException { KeyFactory fact; try { switch (publicPk.getAlgorithm()) { case RSA_ENCRYPT: case RSA_GENERAL: case RSA_SIGN: RSAPublicBCPGKey rsaK = (RSAPublicBCPGKey)publicPk.getKey(); RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsaK.getModulus(), rsaK.getPublicExponent()); fact = KeyFactory.getInstance("RSA", provider); return fact.generatePublic(rsaSpec); case DSA: DSAPublicBCPGKey dsaK = (DSAPublicBCPGKey)publicPk.getKey(); DSAPublicKeySpec dsaSpec = new DSAPublicKeySpec(dsaK.getY(), dsaK.getP(), dsaK.getQ(), dsaK.getG()); fact = KeyFactory.getInstance("DSA", provider); return fact.generatePublic(dsaSpec); case ELGAMAL_ENCRYPT: case ELGAMAL_GENERAL: ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey)publicPk.getKey(); ElGamalPublicKeySpec elSpec = new ElGamalPublicKeySpec(elK.getY(), new ElGamalParameterSpec(elK.getP(), elK.getG())); fact = KeyFactory.getInstance("ElGamal", provider); return fact.generatePublic(elSpec); default: throw new PGPException("unknown public key algorithm encountered"); } } catch (PGPException e) { throw e; } catch (Exception e) { throw new PGPException("exception constructing public key", e); } } /** * Return any userIDs associated with the key. * * @return an iterator of Strings. */ public Iterator getUserIDs() { List temp = new ArrayList(); for (int i = 0; i != ids.size(); i++) { if (ids.get(i) instanceof String) { temp.add(ids.get(i)); } } return temp.iterator(); } /** * Return any user attribute vectors associated with the key. * * @return an iterator of PGPUserAttributeSubpacketVector objects. */ public Iterator getUserAttributes() { List temp = new ArrayList(); for (int i = 0; i != ids.size(); i++) { if (ids.get(i) instanceof PGPUserAttributeSubpacketVector) { temp.add(ids.get(i)); } } return temp.iterator(); } /** * Return any signatures associated with the passed in id. * * @param id the id to be matched. * @return an iterator of PGPSignature objects. */ public Iterator getSignaturesForID( String id) { for (int i = 0; i != ids.size(); i++) { if (id.equals(ids.get(i))) { return ((ArrayList)idSigs.get(i)).iterator(); } } return null; } /** * Return an iterator of signatures associated with the passed in user attributes. * * @param userAttributes the vector of user attributes to be matched. * @return an iterator of PGPSignature objects. */ public Iterator getSignaturesForUserAttribute( PGPUserAttributeSubpacketVector userAttributes) { for (int i = 0; i != ids.size(); i++) { if (userAttributes.equals(ids.get(i))) { return ((ArrayList)idSigs.get(i)).iterator(); } } return null; } /** * Return signatures of the passed in type that are on this key. * * @param signatureType the type of the signature to be returned. * @return an iterator (possibly empty) of signatures of the given type. */ public Iterator getSignaturesOfType( int signatureType) { List l = new ArrayList(); Iterator it = this.getSignatures(); while (it.hasNext()) { PGPSignature sig = (PGPSignature)it.next(); if (sig.getSignatureType() == signatureType) { l.add(sig); } } return l.iterator(); } /** * Return all signatures/certifications associated with this key. * * @return an iterator (possibly empty) with all signatures/certifications. */ public Iterator getSignatures() { if (subSigs == null) { List sigs = new ArrayList(); sigs.addAll(keySigs); for (int i = 0; i != idSigs.size(); i++) { sigs.addAll((Collection)idSigs.get(i)); } return sigs.iterator(); } else { return subSigs.iterator(); } } public byte[] getEncoded() throws IOException { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); this.encode(bOut); return bOut.toByteArray(); } public void encode( OutputStream outStream) throws IOException { BCPGOutputStream out; if (outStream instanceof BCPGOutputStream) { out = (BCPGOutputStream)outStream; } else { out = new BCPGOutputStream(outStream); } out.writePacket(publicPk); if (trustPk != null) { out.writePacket(trustPk); } if (subSigs == null) // not a sub-key { for (int i = 0; i != keySigs.size(); i++) { ((PGPSignature)keySigs.get(i)).encode(out); } for (int i = 0; i != ids.size(); i++) { if (ids.get(i) instanceof String) { String id = (String)ids.get(i); out.writePacket(new UserIDPacket(id)); } else { PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)ids.get(i); out.writePacket(new UserAttributePacket(v.toSubpacketArray())); } if (idTrusts.get(i) != null) { out.writePacket((ContainedPacket)idTrusts.get(i)); } List sigs = (List)idSigs.get(i); for (int j = 0; j != sigs.size(); j++) { ((PGPSignature)sigs.get(j)).encode(out); } } } else { for (int j = 0; j != subSigs.size(); j++) { ((PGPSignature)subSigs.get(j)).encode(out); } } } /** * Check whether this (sub)key has a revocation signature on it. * * @return boolean indicating whether this (sub)key has been revoked. */ public boolean isRevoked() { int ns = 0; boolean revoked = false; if (this.isMasterKey()) // Master key { while (!revoked && (ns < keySigs.size())) { if (((PGPSignature)keySigs.get(ns++)).getSignatureType() == PGPSignature.KEY_REVOCATION) { revoked = true; } } } else // Sub-key { while (!revoked && (ns < subSigs.size())) { if (((PGPSignature)subSigs.get(ns++)).getSignatureType() == PGPSignature.SUBKEY_REVOCATION) { revoked = true; } } } return revoked; } /** * Add a certification for an id to the given public key. * * @param key the key the certification is to be added to. * @param id the id the certification is associated with. * @param certification the new certification. * @return the re-certified key. */ public static PGPPublicKey addCertification( PGPPublicKey key, String id, PGPSignature certification) { return addCert(key, id, certification); } /** * Add a certification for the given UserAttributeSubpackets to the given public key. * * @param key the key the certification is to be added to. * @param userAttributes the attributes the certification is associated with. * @param certification the new certification. * @return the re-certified key. */ public static PGPPublicKey addCertification( PGPPublicKey key, PGPUserAttributeSubpacketVector userAttributes, PGPSignature certification) { return addCert(key, userAttributes, certification); } private static PGPPublicKey addCert( PGPPublicKey key, Object id, PGPSignature certification) { PGPPublicKey returnKey = new PGPPublicKey(key); List sigList = null; for (int i = 0; i != returnKey.ids.size(); i++) { if (id.equals(returnKey.ids.get(i))) { sigList = (List)returnKey.idSigs.get(i); } } if (sigList != null) { sigList.add(certification); } else { sigList = new ArrayList(); sigList.add(certification); returnKey.ids.add(id); returnKey.idTrusts.add(null); returnKey.idSigs.add(sigList); } return returnKey; } /** * Remove any certifications associated with a given user attribute subpacket * on a key. * * @param key the key the certifications are to be removed from. * @param userAttributes the attributes to be removed. * @return the re-certified key, null if the user attribute subpacket was not found on the key. */ public static PGPPublicKey removeCertification( PGPPublicKey key, PGPUserAttributeSubpacketVector userAttributes) { return removeCert(key, userAttributes); } /** * Remove any certifications associated with a given id on a key. * * @param key the key the certifications are to be removed from. * @param id the id that is to be removed. * @return the re-certified key, null if the id was not found on the key. */ public static PGPPublicKey removeCertification( PGPPublicKey key, String id) { return removeCert(key, id); } private static PGPPublicKey removeCert( PGPPublicKey key, Object id) { PGPPublicKey returnKey = new PGPPublicKey(key); boolean found = false; for (int i = 0; i < returnKey.ids.size(); i++) { if (id.equals(returnKey.ids.get(i))) { found = true; returnKey.ids.remove(i); returnKey.idTrusts.remove(i); returnKey.idSigs.remove(i); } } if (!found) { return null; } return returnKey; } /** * Remove a certification associated with a given id on a key. * * @param key the key the certifications are to be removed from. * @param id the id that the certification is to be removed from. * @param certification the certification to be removed. * @return the re-certified key, null if the certification was not found. */ public static PGPPublicKey removeCertification( PGPPublicKey key, String id, PGPSignature certification) { return removeCert(key, id, certification); } /** * Remove a certification associated with a given user attributes on a key. * * @param key the key the certifications are to be removed from. * @param userAttributes the user attributes that the certification is to be removed from. * @param certification the certification to be removed. * @return the re-certified key, null if the certification was not found. */ public static PGPPublicKey removeCertification( PGPPublicKey key, PGPUserAttributeSubpacketVector userAttributes, PGPSignature certification) { return removeCert(key, userAttributes, certification); } private static PGPPublicKey removeCert( PGPPublicKey key, Object id, PGPSignature certification) { PGPPublicKey returnKey = new PGPPublicKey(key); boolean found = false; for (int i = 0; i < returnKey.ids.size(); i++) { if (id.equals(returnKey.ids.get(i))) { found = ((List)returnKey.idSigs.get(i)).remove(certification); } } if (!found) { return null; } return returnKey; } /** * Add a revocation or some other key certification to a key. * * @param key the key the revocation is to be added to. * @param certification the key signature to be added. * @return the new changed public key object. */ public static PGPPublicKey addCertification( PGPPublicKey key, PGPSignature certification) { if (key.isMasterKey()) { if (certification.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) { throw new IllegalArgumentException("signature type incorrect for master key revocation."); } } else { if (certification.getSignatureType() == PGPSignature.KEY_REVOCATION) { throw new IllegalArgumentException("signature type incorrect for sub-key revocation."); } } PGPPublicKey returnKey = new PGPPublicKey(key); if (returnKey.subSigs != null) { returnKey.subSigs.add(certification); } else { returnKey.keySigs.add(certification); } return returnKey; } /** * Remove a certification from the key. * * @param key the key the certifications are to be removed from. * @param certification the certification to be removed. * @return the modified key, null if the certification was not found. */ public static PGPPublicKey removeCertification( PGPPublicKey key, PGPSignature certification) { PGPPublicKey returnKey = new PGPPublicKey(key); boolean found; if (returnKey.subSigs != null) { found = returnKey.subSigs.remove(certification); } else { found = returnKey.keySigs.remove(certification); } if (!found) { for (Iterator it = key.getUserIDs(); it.hasNext();) { String id = (String)it.next(); for (Iterator sIt = key.getSignaturesForID(id); sIt.hasNext();) { if (certification == sIt.next()) { found = true; returnKey = PGPPublicKey.removeCertification(returnKey, id, certification); } } } if (!found) { for (Iterator it = key.getUserAttributes(); it.hasNext();) { PGPUserAttributeSubpacketVector id = (PGPUserAttributeSubpacketVector)it.next(); for (Iterator sIt = key.getSignaturesForUserAttribute(id); sIt.hasNext();) { if (certification == sIt.next()) { found = true; returnKey = PGPPublicKey.removeCertification(returnKey, id, certification); } } } } } return returnKey; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy