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

org.bouncycastle.bcpg.SignaturePacket Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java APIs for the OpenPGP Protocol. The APIs are designed primarily to be used in conjunction with the BC FIPS provider. The APIs may also be used with other providers although if being used in a FIPS context it is the responsibility of the user to ensure that any other providers used are FIPS certified and used appropriately.

There is a newer version: 2.0.9
Show newest version
package org.bouncycastle.bcpg;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Vector;

import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.Streams;

/**
 * generic signature packet
 */
public class SignaturePacket 
    extends ContainedPacket implements PublicKeyAlgorithmTags
{
    private int                    version;
    private int                    signatureType;
    private long                   creationTime;
    private long                   keyID;
    private int                    keyAlgorithm;
    private int                    hashAlgorithm;
    private MPInteger[]            signature;
    private byte[]                 fingerPrint;
    private SignatureSubpacket[]   hashedData;
    private SignatureSubpacket[]   unhashedData;
    private byte[]                 signatureEncoding;
    
    SignaturePacket(
        BCPGInputStream    in)
        throws IOException
    {
        version = in.read();
        
        if (version == 3 || version == 2)
        {
            int    l = in.read();
            
            signatureType = in.read();
            creationTime = (((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read()) * 1000;
            keyID |= (long)in.read() << 56;
            keyID |= (long)in.read() << 48;
            keyID |= (long)in.read() << 40;
            keyID |= (long)in.read() << 32;
            keyID |= (long)in.read() << 24;
            keyID |= (long)in.read() << 16;
            keyID |= (long)in.read() << 8;
            keyID |= in.read();
            keyAlgorithm = in.read();
            hashAlgorithm = in.read();
        }
        else if (version == 4)
        {
            signatureType = in.read();
            keyAlgorithm = in.read();
            hashAlgorithm = in.read();
            
            int        hashedLength = (in.read() << 8) | in.read();
            byte[]    hashed = new byte[hashedLength];
            
            in.readFully(hashed);

            //
            // read the signature sub packet data.
            //
            SignatureSubpacket    sub;
            SignatureSubpacketInputStream    sIn = new SignatureSubpacketInputStream(
                                                                 new ByteArrayInputStream(hashed));

            Vector    v = new Vector();
            while ((sub = sIn.readPacket()) != null)
            {
                v.addElement(sub);
            }
            
            hashedData = new SignatureSubpacket[v.size()];
            
            for (int i = 0; i != hashedData.length; i++)
            {
                SignatureSubpacket    p = (SignatureSubpacket)v.elementAt(i);
                if (p instanceof IssuerKeyID)
                {
                    keyID = ((IssuerKeyID)p).getKeyID();
                }
                else if (p instanceof SignatureCreationTime)
                {
                    creationTime = ((SignatureCreationTime)p).getTime().getTime();
                }
                
                hashedData[i] = p;
            }
            
            int        unhashedLength = (in.read() << 8) | in.read();
            byte[]    unhashed = new byte[unhashedLength];
            
            in.readFully(unhashed);
            
            sIn = new SignatureSubpacketInputStream(
                                     new ByteArrayInputStream(unhashed));
                                                    
            v.removeAllElements();
            while ((sub = sIn.readPacket()) != null)
            {
                v.addElement(sub);
            }
            
            unhashedData = new SignatureSubpacket[v.size()];
            
            for (int i = 0; i != unhashedData.length; i++)
            {
                SignatureSubpacket    p = (SignatureSubpacket)v.elementAt(i);
                if (p instanceof IssuerKeyID)
                {
                    keyID = ((IssuerKeyID)p).getKeyID();
                }
                
                unhashedData[i] = p;
            }
        }
        else
        {
            Streams.drain(in);

            throw new UnsupportedPacketVersionException("unsupported version: " + version);
        }
        
        fingerPrint = new byte[2];
        in.readFully(fingerPrint);
        
        switch (keyAlgorithm)
        {
        case RSA_GENERAL:
        case RSA_SIGN:
            MPInteger    v = new MPInteger(in);
                
            signature = new MPInteger[1];
            signature[0] = v;
            break;
        case DSA:
            MPInteger    r = new MPInteger(in);
            MPInteger    s = new MPInteger(in);
                
            signature = new MPInteger[2];
            signature[0] = r;
            signature[1] = s;
            break;
        case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes.
        case ELGAMAL_GENERAL:
            MPInteger       p = new MPInteger(in);
            MPInteger       g = new MPInteger(in);
            MPInteger       y = new MPInteger(in);
            
            signature = new MPInteger[3];
            signature[0] = p;
            signature[1] = g;
            signature[2] = y;
            break;
        case ECDSA:
            MPInteger    ecR = new MPInteger(in);
            MPInteger    ecS = new MPInteger(in);

            signature = new MPInteger[2];
            signature[0] = ecR;
            signature[1] = ecS;
            break;
        case EDDSA:
            MPInteger    edR = new MPInteger(in);
            MPInteger    edS = new MPInteger(in);

            signature = new MPInteger[2];
            signature[0] = edR;
            signature[1] = edS;
            break;
        default:
            if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11)
            {
                signature = null;
                ByteArrayOutputStream bOut = new ByteArrayOutputStream();
                int ch;
                while ((ch = in.read()) >= 0)
                {
                    bOut.write(ch);
                }
                signatureEncoding = bOut.toByteArray();
            }
            else
            {
                throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
            }
        }
    }
    
    /**
     * Generate a version 4 signature packet.
     * 
     * @param signatureType
     * @param keyAlgorithm
     * @param hashAlgorithm
     * @param hashedData
     * @param unhashedData
     * @param fingerPrint
     * @param signature
     */
    public SignaturePacket(
        int                     signatureType,
        long                    keyID,
        int                     keyAlgorithm,
        int                     hashAlgorithm,
        SignatureSubpacket[]    hashedData,
        SignatureSubpacket[]    unhashedData,
        byte[]                  fingerPrint,
        MPInteger[]             signature)
    {
        this(4, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, signature);
    }
    
    /**
     * Generate a version 2/3 signature packet.
     * 
     * @param signatureType
     * @param keyAlgorithm
     * @param hashAlgorithm
     * @param fingerPrint
     * @param signature
     */
    public SignaturePacket(
        int                     version,
        int                     signatureType,
        long                    keyID,
        int                     keyAlgorithm,
        int                     hashAlgorithm,
        long                    creationTime,
        byte[]                  fingerPrint,
        MPInteger[]             signature)
    {
        this(version, signatureType, keyID, keyAlgorithm, hashAlgorithm, null, null, fingerPrint, signature);
        
        this.creationTime = creationTime;
    }
    
    public SignaturePacket(
        int                     version,
        int                     signatureType,
        long                    keyID,
        int                     keyAlgorithm,
        int                     hashAlgorithm,
        SignatureSubpacket[]    hashedData,
        SignatureSubpacket[]    unhashedData,
        byte[]                  fingerPrint,
        MPInteger[]             signature)
    {
        this.version = version;
        this.signatureType = signatureType;
        this.keyID = keyID;
        this.keyAlgorithm = keyAlgorithm;
        this.hashAlgorithm = hashAlgorithm;
        this.hashedData = hashedData;
        this.unhashedData = unhashedData;
        this.fingerPrint = fingerPrint;
        this.signature = signature;

        if (hashedData != null)
        {
            setCreationTime();
        }
    }
    
    /**
     * get the version number
     */
    public int getVersion()
    {
        return version;
    }
    
    /**
     * return the signature type.
     */
    public int getSignatureType()
    {
        return signatureType;
    }
    
    /**
     * return the keyID
     * @return the keyID that created the signature.
     */
    public long getKeyID()
    {
        return keyID;
    }

    /**
     * Return the signatures fingerprint.
     * @return fingerprint (digest prefix) of the signature
     */
    public byte[] getFingerPrint()
    {
        byte[] fp = new byte[fingerPrint.length];
        System.arraycopy(fingerPrint, 0, fp, 0, fingerPrint.length);
        return fp;
    }
    
    /**
     * return the signature trailer that must be included with the data
     * to reconstruct the signature
     * 
     * @return byte[]
     */
    public byte[] getSignatureTrailer()
    {
        byte[]    trailer = null;
        
        if (version == 3 || version == 2)
        {
            trailer = new byte[5];
            
            long    time = creationTime / 1000;
            
            trailer[0] = (byte)signatureType;
            trailer[1] = (byte)(time >> 24);
            trailer[2] = (byte)(time >> 16);
            trailer[3] = (byte)(time >> 8);
            trailer[4] = (byte)(time);
        }
        else
        {
            ByteArrayOutputStream    sOut = new ByteArrayOutputStream();
        
            try
            {
                sOut.write((byte)this.getVersion());
                sOut.write((byte)this.getSignatureType());
                sOut.write((byte)this.getKeyAlgorithm());
                sOut.write((byte)this.getHashAlgorithm());
            
                ByteArrayOutputStream    hOut = new ByteArrayOutputStream();
                SignatureSubpacket[]     hashed = this.getHashedSubPackets();
            
                for (int i = 0; i != hashed.length; i++)
                {
                    hashed[i].encode(hOut);
                }
                
                byte[]                   data = hOut.toByteArray();
            
                sOut.write((byte)(data.length >> 8));
                sOut.write((byte)data.length);
                sOut.write(data);
            
                byte[]    hData = sOut.toByteArray();
            
                sOut.write((byte)this.getVersion());
                sOut.write((byte)0xff);
                sOut.write((byte)(hData.length>> 24));
                sOut.write((byte)(hData.length >> 16));
                sOut.write((byte)(hData.length >> 8));
                sOut.write((byte)(hData.length));
            }
            catch (IOException e)
            {
                throw new RuntimeException("exception generating trailer: " + e);
            }
                
            trailer = sOut.toByteArray();
        }
        
        return trailer;
    }
    
    /**
     * return the encryption algorithm tag
     */
    public int getKeyAlgorithm()
    {
        return keyAlgorithm;
    }
    
    /**
     * return the hashAlgorithm tag
     */
    public int getHashAlgorithm()
    {
        return hashAlgorithm;
    }
    
    /**
     * return the signature as a set of integers - note this is normalised to be the
     * ASN.1 encoding of what appears in the signature packet.
     */
    public MPInteger[] getSignature()
    {
        return signature;
    }

    /**
     * Return the byte encoding of the signature section.
     * @return uninterpreted signature bytes.
     */
    public byte[] getSignatureBytes()
    {
        if (signatureEncoding == null)
        {
            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
            BCPGOutputStream bcOut = new BCPGOutputStream(bOut);

            for (int i = 0; i != signature.length; i++)
            {
                try
                {
                    bcOut.writeObject(signature[i]);
                }
                catch (IOException e)
                {
                    throw new RuntimeException("internal error: " + e);
                }
            }
            return bOut.toByteArray();
        }
        else
        {
            return Arrays.clone(signatureEncoding);
        }
    }
    public SignatureSubpacket[] getHashedSubPackets()
    {
        return hashedData;
    }
    
    public SignatureSubpacket[] getUnhashedSubPackets()
    {
        return unhashedData;
    }
    
    /**
     * Return the creation time of the signature in milli-seconds.
     * 
     * @return the creation time in millis
     */
    public long getCreationTime()
    {
        return creationTime;
    }
    
    public void encode(
        BCPGOutputStream    out)
        throws IOException
    {
        ByteArrayOutputStream    bOut = new ByteArrayOutputStream();
        BCPGOutputStream         pOut = new BCPGOutputStream(bOut);
        
        pOut.write(version);
        
        if (version == 3 || version == 2)
        {
            pOut.write(5); // the length of the next block
            
            long    time = creationTime / 1000;
            
            pOut.write(signatureType);
            pOut.write((byte)(time >> 24));
            pOut.write((byte)(time >> 16));
            pOut.write((byte)(time >> 8));
            pOut.write((byte)time);

            pOut.write((byte)(keyID >> 56));
            pOut.write((byte)(keyID >> 48));
            pOut.write((byte)(keyID >> 40));
            pOut.write((byte)(keyID >> 32));
            pOut.write((byte)(keyID >> 24));
            pOut.write((byte)(keyID >> 16));
            pOut.write((byte)(keyID >> 8));
            pOut.write((byte)(keyID));
            
            pOut.write(keyAlgorithm);
            pOut.write(hashAlgorithm);
        }
        else if (version == 4)
        {
            pOut.write(signatureType);
            pOut.write(keyAlgorithm);
            pOut.write(hashAlgorithm);
            
            ByteArrayOutputStream    sOut = new ByteArrayOutputStream();
            
            for (int i = 0; i != hashedData.length; i++)
            {
                hashedData[i].encode(sOut);
            }
            
            byte[]                   data = sOut.toByteArray();
    
            pOut.write(data.length >> 8);
            pOut.write(data.length);
            pOut.write(data);
            
            sOut.reset();
            
            for (int i = 0; i != unhashedData.length; i++)
            {
                unhashedData[i].encode(sOut);
            }
            
            data = sOut.toByteArray();
      
            pOut.write(data.length >> 8);
            pOut.write(data.length);
            pOut.write(data);
        }
        else
        {
            throw new IOException("unknown version: " + version);
        }
        
        pOut.write(fingerPrint);

        if (signature != null)
        {
            for (int i = 0; i != signature.length; i++)
            {
                pOut.writeObject(signature[i]);
            }
        }
        else
        {
            pOut.write(signatureEncoding);
        }

        pOut.close();

        out.writePacket(SIGNATURE, bOut.toByteArray());
    }

    private void setCreationTime()
    {
        for (int i = 0; i != hashedData.length; i++)
        {
            if (hashedData[i] instanceof SignatureCreationTime)
            {
                creationTime = ((SignatureCreationTime)hashedData[i]).getTime().getTime();
                break;
            }
        }
    }

    public static SignaturePacket fromByteArray(byte[] data)
        throws IOException
    {
        BCPGInputStream in = new BCPGInputStream(new ByteArrayInputStream(data));

        return new SignaturePacket(in);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy