org.bouncycastle.bcpg.SignaturePacket Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcpg-fips Show documentation
Show all versions of bcpg-fips Show documentation
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.
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);
}
}