blob: 7ca06d6aeba1e1112ae19f3ec85fcfaf14fa3154 [file] [log] [blame]
package org.bouncycastle.asn1;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.bouncycastle.util.Arrays;
/**
* Base class for an application specific object
*/
public class DERApplicationSpecific
extends ASN1Object
{
private final boolean isConstructed;
private final int tag;
private final byte[] octets;
DERApplicationSpecific(
boolean isConstructed,
int tag,
byte[] octets)
{
this.isConstructed = isConstructed;
this.tag = tag;
this.octets = octets;
}
public DERApplicationSpecific(
int tag,
byte[] octets)
{
this(false, tag, octets);
}
public DERApplicationSpecific(
int tag,
DEREncodable object)
throws IOException
{
this(true, tag, object);
}
public DERApplicationSpecific(
boolean explicit,
int tag,
DEREncodable object)
throws IOException
{
byte[] data = object.getDERObject().getDEREncoded();
this.isConstructed = explicit;
this.tag = tag;
if (explicit)
{
this.octets = data;
}
else
{
int lenBytes = getLengthOfLength(data);
byte[] tmp = new byte[data.length - lenBytes];
System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
this.octets = tmp;
}
}
public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
{
this.tag = tagNo;
this.isConstructed = true;
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
for (int i = 0; i != vec.size(); i++)
{
try
{
bOut.write(((ASN1Encodable)vec.get(i)).getEncoded());
}
catch (IOException e)
{
throw new ASN1ParsingException("malformed object: " + e, e);
}
}
this.octets = bOut.toByteArray();
}
private int getLengthOfLength(byte[] data)
{
int count = 2; // TODO: assumes only a 1 byte tag number
while((data[count - 1] & 0x80) != 0)
{
count++;
}
return count;
}
public boolean isConstructed()
{
return isConstructed;
}
public byte[] getContents()
{
return octets;
}
public int getApplicationTag()
{
return tag;
}
/**
* Return the enclosed object assuming explicit tagging.
*
* @return the resulting object
* @throws IOException if reconstruction fails.
*/
public DERObject getObject()
throws IOException
{
return new ASN1InputStream(getContents()).readObject();
}
/**
* Return the enclosed object assuming implicit tagging.
*
* @param derTagNo the type tag that should be applied to the object's contents.
* @return the resulting object
* @throws IOException if reconstruction fails.
*/
public DERObject getObject(int derTagNo)
throws IOException
{
if (derTagNo >= 0x1f)
{
throw new IOException("unsupported tag number");
}
byte[] orig = this.getEncoded();
byte[] tmp = replaceTagNumber(derTagNo, orig);
if ((orig[0] & DERTags.CONSTRUCTED) != 0)
{
tmp[0] |= DERTags.CONSTRUCTED;
}
return new ASN1InputStream(tmp).readObject();
}
/* (non-Javadoc)
* @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream)
*/
void encode(DEROutputStream out) throws IOException
{
int classBits = DERTags.APPLICATION;
if (isConstructed)
{
classBits |= DERTags.CONSTRUCTED;
}
out.writeEncoded(classBits, tag, octets);
}
boolean asn1Equals(
DERObject o)
{
if (!(o instanceof DERApplicationSpecific))
{
return false;
}
DERApplicationSpecific other = (DERApplicationSpecific)o;
return isConstructed == other.isConstructed
&& tag == other.tag
&& Arrays.areEqual(octets, other.octets);
}
public int hashCode()
{
return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
}
private byte[] replaceTagNumber(int newTag, byte[] input)
throws IOException
{
int tagNo = input[0] & 0x1f;
int index = 1;
//
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
//
if (tagNo == 0x1f)
{
tagNo = 0;
int b = input[index++] & 0xff;
// X.690-0207 8.1.2.4.2
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
if ((b & 0x7f) == 0) // Note: -1 will pass
{
throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
}
while ((b >= 0) && ((b & 0x80) != 0))
{
tagNo |= (b & 0x7f);
tagNo <<= 7;
b = input[index++] & 0xff;
}
tagNo |= (b & 0x7f);
}
byte[] tmp = new byte[input.length - index + 1];
System.arraycopy(input, index, tmp, 1, tmp.length - 1);
tmp[0] = (byte)newTag;
return tmp;
}
}