com.subgraph.orchid.crypto.ASN1Parser Maven / Gradle / Ivy
package com.subgraph.orchid.crypto;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* A very minimal ASN.1 BER parser which only supports the ASN.1 object types needed
* for parsing encoded RSA public keys.
*/
public class ASN1Parser {
private final static int ASN1_TAG_SEQUENCE = 16;
private final static int ASN1_TAG_INTEGER = 2;
private final static int ASN1_TAG_BITSTRING = 3;
static interface ASN1Object {};
static class ASN1Sequence implements ASN1Object {
private final List items;
ASN1Sequence(List items) {
this.items = items;
}
List getItems() {
return items;
}
}
static class ASN1Integer implements ASN1Object {
final BigInteger value;
ASN1Integer(BigInteger value) {
this.value = value;
}
BigInteger getValue() {
return value;
}
}
static class ASN1BitString implements ASN1Object {
final byte[] bytes;
ASN1BitString(byte[] bytes) {
this.bytes = bytes;
}
byte[] getBytes() {
return bytes;
}
}
/* For object types we don't handle, just stuff the bytes into here */
static class ASN1Blob extends ASN1BitString {
ASN1Blob(byte[] bytes) {
super(bytes);
}
}
ASN1Object parseASN1(ByteBuffer data) {
final int typeOctet = data.get() & 0xFF;
final int tag = typeOctet & 0x1F;
final ByteBuffer objectBuffer = getObjectBuffer(data);
switch(tag) {
case ASN1_TAG_SEQUENCE:
return parseASN1Sequence(objectBuffer);
case ASN1_TAG_INTEGER:
return parseASN1Integer(objectBuffer);
case ASN1_TAG_BITSTRING:
return parseASN1BitString(objectBuffer);
default:
return createBlob(objectBuffer);
}
}
/*
* Read 'length' from data buffer, create a new buffer as a slice() which
* contains 'length' bytes of data following length field and return this
* buffer. Increment position pointer of data buffer to skip over these bytes.
*/
ByteBuffer getObjectBuffer(ByteBuffer data) {
final int length = parseASN1Length(data);
if(length > data.remaining()) {
throw new IllegalArgumentException();
}
final ByteBuffer objectBuffer = data.slice();
objectBuffer.limit(length);
data.position(data.position() + length);
return objectBuffer;
}
int parseASN1Length(ByteBuffer data) {
final int firstOctet = data.get() & 0xFF;
if(firstOctet < 0x80) {
return firstOctet;
}
return parseASN1LengthLong(firstOctet & 0x7F, data);
}
int parseASN1LengthLong(int lengthOctets, ByteBuffer data) {
if(lengthOctets == 0 || lengthOctets > 3) {
// indefinite form or too long
throw new IllegalArgumentException();
}
int length = 0;
for(int i = 0; i < lengthOctets; i++) {
length <<= 8;
length |= (data.get() & 0xFF);
}
return length;
}
ASN1Sequence parseASN1Sequence(ByteBuffer data) {
final List obs = new ArrayList();
while(data.hasRemaining()) {
obs.add(parseASN1(data));
}
return new ASN1Sequence(obs);
}
ASN1Integer parseASN1Integer(ByteBuffer data) {
return new ASN1Integer(new BigInteger(getRemainingBytes(data)));
}
ASN1BitString parseASN1BitString(ByteBuffer data) {
final int unusedBits = data.get() & 0xFF;
if(unusedBits != 0) {
throw new IllegalArgumentException();
}
return new ASN1BitString(getRemainingBytes(data));
}
ASN1Blob createBlob(ByteBuffer data) {
return new ASN1Blob(getRemainingBytes(data));
}
private byte[] getRemainingBytes(ByteBuffer data) {
final byte[] bs = new byte[data.remaining()];
data.get(bs);
return bs;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy