public class Asn1Object {
    
    private Header header;
    private byte[] value;

    public Asn1Object(Header header, byte[] value) {
        this.header = header;
        this.value = value;
    }

    public Header getHeader() {
        return header;
    }

    public byte[] getValue() {
        return value;
    }
    
    public void setValue(byte[] value) {
        this.value = value;
    }
    
    public void invalidateValue() {
        this.value = null;
    }
    
    public void encodeTo(OutputStream out) throws IOException {
        header.encodeTo(out);
        if (value != null)
            out.write(value);
    }
} public class EncodableHeader implements Header {
    
    private final Tag tag;
    private final Length length;
    
    public EncodableHeader(int tag,
                           TagClass tagClass,
                           boolean isConstructed,
                           boolean isInfinite) {
        this(new Tag(tag, tagClass, isConstructed), new Length(isInfinite));
    }
    
    public EncodableHeader(Tag tag, Length length) {
        if (tag == null) throw new NullPointerException();
        if (length == null) throw new NullPointerException();
        
        this.tag = tag;
        this.length = length;
    }

    @Override
    public void encodeTo(OutputStream out) {
        try {
            out.write(tag.getEncoding());
            out.write(length.getEncoding());
        }
        catch (IOException ex) {
            throw new SerializeException(ex);
        }
    }

    @Override
    public int getHeaderLength() {
        byte[] tagEncoding = tag.getEncoding();
        byte[] lengthEncoding = length.getEncoding();
        return tagEncoding.length + lengthEncoding.length;
    }

    @Override
    public Length getLength() {
        return length;
    }

    @Override
    public Tag getTag() {
        return tag;
    }
} public interface Header {

    public static final byte CONSTRUCTED_MASK     = (byte)0x20;
    public static final byte COMPLEX_TAG_MASK     = (byte)0x1f;
    public static final byte INFINITE_LENGTH_MASK = (byte)0x80;
    
    public Tag getTag();
    public Length getLength();
    public int getHeaderLength();
    
    public void encodeTo(OutputStream out);
    
} public class Length {

    private boolean isInfiniteLength;
    private int length;
    private byte[] encoding;

    public Length(boolean isInfiniteLength) {
        this(0, isInfiniteLength, null);
    }

    public Length(int length, boolean isInfiniteLength, byte[] encoding) {
        this.isInfiniteLength = isInfiniteLength;
        this.length = length;
        this.encoding = encoding;
    }

    public byte[] getEncoding() {
        if (encoding == null) {
            encoding = computeEncoding();
        }
        return encoding;
    }

    public boolean isInfiniteLength() {
        return isInfiniteLength;
    }
    
    public void setInfiniteLength(boolean isInfiniteLength) {
        if (isInfiniteLength == this.isInfiniteLength)
            return;
        this.isInfiniteLength = isInfiniteLength;
        this.encoding = null;
    }

    public int getLength() {
        return length;
    }
    
    public void setLength(int length) {
        if (length == this.length)
            return;
        this.length = length;
        this.encoding = null;
    }
    
    public void invalidateEncoding() {
        this.encoding = null;
        this.length = 0;
    }
    
    public boolean hasBeenComputed() {
        return encoding != null;
    }
    
    private byte[] computeEncoding() {
        if (isInfiniteLength) {
            return new byte[]{Header.INFINITE_LENGTH_MASK};
        } else if (length <= 127) {
            return new byte[]{(byte) (length & 0xff)};
        } else {
            return computeComplexLength();
        }
    }

    private byte[] computeComplexLength() {
        int numShifts = Tag.determineNumberOfShifts(length, 8);
        int tmp = length;
        byte[] out = new byte[numShifts + 1];
        out[0] = (byte) (numShifts & 0xff);
        out[0] |= Header.INFINITE_LENGTH_MASK;

        for (int i = numShifts; i > 0; i--) {
            out[i] = (byte) (tmp & 0xff);
            tmp >>= 8;
        }

        return out;
    }
} public interface ParsedHeader extends Header {
    
    public void skipValue();
    public byte[] getValue();
    public InputStream getValueStream(boolean valuesOnly);
    public Asn1Object getObject();
} public class ParserFactory {
   
    public Parser newHeaderParser() {
        return new PullHeaderParser();
    }
} public class Tag {

    private int tag;
    private TagClass tc;
    private boolean isConstructed;
    private byte[] encoding;

    public Tag(int tag, TagClass tc, boolean isConstructed) {
        this(tag, tc, isConstructed, null);
    }

    public Tag(int tag, TagClass tc, boolean isConstructed, byte[] encoding) {
        this.tag = tag;
        this.tc = tc;
        this.isConstructed = isConstructed;
        this.encoding = encoding;
    }

    public byte[] getEncoding() {
        if (encoding == null) {
            encoding = computeEncoding();
        }
        return encoding;
    }

    public boolean isConstructed() {
        return isConstructed;
    }
    
    public void setConstructed(boolean isConstructed) {
        if (isConstructed == this.isConstructed)
            return;
        this.isConstructed = isConstructed;
        this.encoding = null;
    }

    public int getTag() {
        return tag;
    }
    
    public void setTag(int tag) {
        if (tag == this.tag)
            return;
        this.tag = tag;
        this.encoding = null;
    }
    
    public void invalidateEncoding() {
        this.encoding = null;
    }

    public TagClass getTagClass() {
        return tc;
    }
    
    public void setTagClass(TagClass tagClass) {
        if (tagClass == this.tc)
            return;
        this.tc = tagClass;
        this.encoding = null;
    }
    
    public boolean hasBeenComputed() {
        return encoding != null;
    }

    private byte[] computeEncoding() {
        if (tag < 31) {
            byte tagByte = isConstructed ? Header.CONSTRUCTED_MASK : (byte) 0x00;
            tagByte |= tc.getMask();
            tagByte |= (byte) (tag & 0xff);
            return new byte[]{tagByte};
        } else {
            return computeComplexTag();
        }
    }

    private byte[] computeComplexTag() {
        byte tagByte = isConstructed ? Header.CONSTRUCTED_MASK : (byte) 0x00;
        tagByte |= tc.getMask();
        tagByte |= Header.COMPLEX_TAG_MASK;

        int numShifts = determineNumberOfShifts(tag, 7);
        byte[] out = new byte[numShifts + 1];
        int tmpTag = tag;

        out[0] = tagByte;
        for (int i = numShifts; i > 0; i--) {
            tagByte = (byte) (tmpTag & 0x7f);
            if (i != numShifts) {
                tagByte |= Header.INFINITE_LENGTH_MASK;
            }
            out[i] = tagByte;
            tmpTag >>= 7;
        }
        return out;
    }

    static int determineNumberOfShifts(int value, int shiftBy) {
        int i;
        for (i = 0; value > 0; i++) {
            value >>= shiftBy;
        }
        return i;
    }
} public enum TagClass {
    
    UNIVERSAL       (Masks.UNIVERSAL_MASK),
    APPLICATION     (Masks.APPLICATION_MASK),
    CONTEXT_SPECIFIC(Masks.CONTEXT_SPECIFIC_MASK),
    PRIVATE         (Masks.PRIVATE_MASK);
    
    TagClass(byte mask) {
        this.mask = mask;
    }
    
    public static TagClass forName(String name) {
        if ("IMPLICIT".equals(name))
            return CONTEXT_SPECIFIC;
        if ("EXPLICIT".equals(name))
            return CONTEXT_SPECIFIC;
        return TagClass.valueOf(name);
    }
    
    private final byte mask;
    
    public byte getMask() {
        return mask;
    }
    
    public static TagClass of(byte b) {
        switch (b) {
            case Masks.UNIVERSAL_MASK:
                return UNIVERSAL;
            case Masks.APPLICATION_MASK:
                return APPLICATION;
            case Masks.CONTEXT_SPECIFIC_MASK:
                return CONTEXT_SPECIFIC;
            case Masks.PRIVATE_MASK:
                return PRIVATE;
            default:
                throw new IllegalArgumentException("Unknown tag class: " + b);
        }
    }
    
    private static class Masks {
        static final byte UNIVERSAL_MASK        = (byte)0x00;
        static final byte APPLICATION_MASK      = (byte)0x40;
        static final byte CONTEXT_SPECIFIC_MASK = (byte)0x80;
        static final byte PRIVATE_MASK          = (byte)0xc0;
    }

} public class CachingInputStream extends FilterInputStream {

    private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    private final byte[] singleByte = new byte[1];
    
    public CachingInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int read =  read(singleByte, 0, 1);
        if (read != 1)
            return read;
        return (singleByte[0] & 0xff);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int read = super.read(b, off, len);
        if (read != -1) {
            baos.write(b, off, read);
        }
        return read;
    }
    
    public byte[] getCachedBytes() {
        return baos.toByteArray();
    }
} public class PemInputStream extends FilterInputStream {

    private Base64Buffer b64Buffer;
    private final byte[] singleByte = new byte[1];
    
    public PemInputStream(InputStream in) {
        super(in);
        b64Buffer = new Base64Buffer(in);
    }
    
    public void continueStream() {
        b64Buffer.continueStream();
    }

    public String getCurrentName() {
        return b64Buffer.getName();
    }
    
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if (len <= 0)
            throw new IllegalArgumentException("Negative or zero length");
        return b64Buffer.read(b, off, len);
    }

    @Override
    public int read() throws IOException {
        int r = read(singleByte);
        while (r == 0) {
            r = read(singleByte);
        }
        if (r == -1)
            return r;
        return (singleByte[0] & 0xff);
    }
    
    @Override
    public boolean markSupported() {
        return false;
    }

    @Override
    public long skip(long n) throws IOException {
        throw new UnsupportedOperationException("Not implemented yet");
    }
    
    private static abstract class PemLineMatcher {
        private final Pattern pattern;
        private final String line;
        private String name;
        
        public PemLineMatcher(String line, final String beginOrEnd) {
            this.pattern = Pattern.compile("^-----" + beginOrEnd + " (\\w(\\w|\\s)*)-----$");
            this.line = line;
        }
        
        public boolean match() throws IOException {
            Matcher m = pattern.matcher(line);
            if (m.matches()) {
                name = m.group(1);
                if (name == null)
                    return false;
                return true;
            }
            return false;
        }
        
        public String getName() {
            return name;
        }
    }
    
    private static class PemHeaderMatcher extends PemLineMatcher{
        public PemHeaderMatcher(String line) {
            super(line, "BEGIN");
        }
    }
    
    private static class PemFooterMatcher extends PemLineMatcher{
        private final String name;
        
        public PemFooterMatcher(String line, String name) {
            super(line, "END");
            this.name = name;
        }

        @Override
        public boolean match() throws IOException {
            boolean match = super.match();
            if (match && name.equals(getName()))
                return true;
            else
                return false;
        }
    }
    
    private static enum State {
        HEADER,
        CONTENT,
        FOOTER,
        DONE
    }
    
    private static class Base64Buffer {
        private static final int THRESHOLD = 4096;
        
        private final BufferedReader in;
        private State state = State.HEADER;
        private byte[] buffer;
        private int bufpos = 0;
        private boolean eof = false;
        private String name;
        
        public Base64Buffer(InputStream in) {
            try {
                this.in = new BufferedReader(new InputStreamReader(in, "US-ASCII"));
            } catch (UnsupportedEncodingException ex) {
                throw new RuntimeException(ex);
            }
        }
        
        public void continueStream() {
            this.buffer = null;
            this.name = null;
            this.bufpos = 0;
            this.state = State.HEADER;
            this.eof = false;
        }
        
        private int decodeLine(String line, OutputStream out) throws IOException {
            try {
                byte[] bytes = line.getBytes("US-ASCII");
                Base64.decodeTo(bytes, 0, bytes.length, out);
                return bytes.length;
            } catch (UnsupportedEncodingException ex) {
                throw new RuntimeException(ex);
            }
        }
        
        private void fill() throws IOException {
            int total = 0;
            ByteArrayOutputStream baos = new ByteArrayOutputStream(THRESHOLD);
            String line = in.readLine();
            
            while (!state.equals(State.DONE) && total < THRESHOLD && line != null) {
                if (line.equals("")) {
                    line = in.readLine();
                    continue;
                }
                switch (state) {
                    case HEADER:
                        if (line.charAt(0) == '-') {
                            PemHeaderMatcher matcher = new PemHeaderMatcher(line);
                            if (matcher.match()) {
                                state = State.CONTENT;
                                name = matcher.getName();
                            }
                        }
                        line = in.readLine();
                        break;
                    case CONTENT:
                        if (line.charAt(0) == '-') {
                            state = State.FOOTER;
                        } else {
                            total += decodeLine(line, baos);
                            if (total < THRESHOLD)
                                line = in.readLine();
                        }
                        break;
                    case FOOTER:
                        if (line.charAt(0) == '-') {
                           PemFooterMatcher matcher = new PemFooterMatcher(line, name);
                            if (matcher.match()) {
                                state = State.DONE;
                            }
                            else {
                                line = in.readLine();
                            }
                        } else {
                            line = in.readLine();
                        }
                        break;
                    default:
                        break;
                }
            }
            
            if (state.equals(State.DONE) || line == null)
                eof = true;
            
            if (line == null && !state.equals(State.DONE)) {
                switch (state) {
                    case HEADER:
                        break; /* means we essentially never read PEM data */
                    case CONTENT:
                        throw new MalformedPemException("PEM data ended prematurely");
                    default:
                        throw new MalformedPemException("Could not find matching footer");
                }
            }
            
            buffer = baos.toByteArray();
            bufpos = 0;
        }
        
        private int consumeBytes(byte[] b, int off, int len) {
            if (bufpos == buffer.length)
                return 0;
            int available = buffer.length - bufpos;
            int toRead = len <  available ? len : available;
            System.arraycopy(buffer, bufpos, b, off, toRead);
            bufpos += toRead;
            return toRead;
        }
        
        public int read(byte[] b, int off, int len) throws IOException {
            int total = 0;
            if (buffer == null) {
                fill();
            }
            while (total != len && !(bufpos == buffer.length && eof)) {
                if (bufpos == buffer.length)
                    fill();
                total += consumeBytes(b, off + total, len - total);
            }
            if (total == 0 && eof)
                return -1;
            return total;
        }

        public String getName() {
            return name;
        }
    }
    
    public static class MalformedPemException extends RuntimeException {
        public MalformedPemException(Throwable cause) { super(cause); }
        public MalformedPemException(String message, Throwable cause) { super(message, cause); }
        public MalformedPemException(String message) { super(message); }
    }
} public class Errors {
   
    private Errors() { }
    
    public static RaiseException newKryptError(Ruby rt, String message) {
        return newError(rt, "Krypt::Error", message);
    }
    
    public static RaiseException newParseError(Ruby rt, String message) {
        return newError(rt, "Krypt::ASN1::ParseError", message);
    }
    
    public static RaiseException newSerializeError(Ruby rt, String message) {
        return newError(rt, "Krypt::ASN1::SerializeError", message);
    }
    
    public static RaiseException newASN1Error(Ruby rt, String message) {
        return newError(rt, "Krypt::ASN1::ASN1Error", message);
    }
    
    public static RaiseException newPEMError(Ruby rt, String message) {
        return newError(rt, "Krypt::PEM::PEMError", message);
    }
    
    public static RaiseException newHexError(Ruby rt, String message) {
        return newError(rt, "Krypt::Hex::HexError", message);
    }
    
    public static RaiseException newBase64Error(Ruby rt, String message) {
        return newError(rt, "Krypt::Base64::Base64Error", message);
    }
    
    public static RaiseException newDigestError(Ruby rt, String message) {
        return newError(rt, "Krypt::Digest::DigestError", message);
    }
    
    public static RaiseException newError(Ruby rt, String path, String message) {
        return new RaiseException(rt, getClassFromPath(rt, path), message, true);
    }
    
    public static RubyClass getClassFromPath(Ruby rt, String path) {
        return (RubyClass)rt.getClassFromPath(path);
    }
} public class Streams {
   private Streams() { }
   
   public static InputStream tryWrapAsInputStream(Ruby runtime, IRubyObject io) {
        try {
            return new IOInputStream(io);
        }
        catch (IllegalArgumentException ex) {
            throw runtime.newArgumentError(ex.getMessage());
        }
    }
    
   public static OutputStream tryWrapAsOuputStream(Ruby runtime, IRubyObject io) {
        try {
            return new IOOutputStream(io);
        }
        catch (IllegalArgumentException ex) {
            throw runtime.newArgumentError(ex.getMessage());
        }
    }
   
    public static InputStream asInputStreamDer(Ruby runtime, IRubyObject value) {
        if (value.respondsTo("read"))
            return Streams.tryWrapAsInputStream(runtime, value);
        else
            return new ByteArrayInputStream(toDerIfPossible(value).convertToString().getBytes());
    }
    
    public static InputStream asInputStreamPem(Ruby runtime, IRubyObject value) {
        if (value.respondsTo("read"))
            return Streams.tryWrapAsInputStream(runtime, value);
        else
            return new ByteArrayInputStream(toPemIfPossible(value).convertToString().getBytes());
    }
    
    private static IRubyObject convertData(IRubyObject obj, String convertMeth) {
        return obj.callMethod(obj.getRuntime().getCurrentContext(), convertMeth);
    }

    private static IRubyObject convertDataIfPossible(IRubyObject data, String convertMeth) {
        if(data.respondsTo(convertMeth)) {
            return convertData(data, convertMeth);
        } else {
            return data;
        }
    }
    
    public static IRubyObject toDer(IRubyObject obj) {
        return convertData(obj, "to_der");
    }

    public static IRubyObject toDerIfPossible(IRubyObject der) {
        return convertDataIfPossible(der, "to_der");
    }
    
    public static IRubyObject toPem(IRubyObject obj) {
        return convertData(obj, "to_pem");
    }

    public static IRubyObject toPemIfPossible(IRubyObject pem) {
        return convertDataIfPossible(pem, "to_pem");
    }
   
    public static void tryClose(Ruby runtime, InputStream in) {
        try {
            in.close();
        }
        catch (IOException ex) {
            throw runtime.newRuntimeError(ex.getMessage());
        }
    }
    
    public static void tryClose(Ruby runtime, OutputStream out) {
        try {
            out.close();
        }
        catch (IOException ex) {
            throw runtime.newRuntimeError(ex.getMessage());
        }
    }
    
    public static byte[] consume(InputStream in) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int read;
        byte[] buffer = new byte[8192];
        while ((read = in.read(buffer)) != -1) {
            baos.write(buffer, 0, read);
        }
        if (baos.size() == 0)
            return null;
        return baos.toByteArray();
    }
    
    public static boolean isConsumed(InputStream in) {
        try {
            if (in.read() != -1)
                return false;
            return true;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }
} public static class Asn1BitString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1BitString(runtime, type);
        }
    };
        
    private Asn1BitString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1BitString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        IRubyObject val = init(this, ctx, args, getDefaultTag());
        val.getInstanceVariables().setInstanceVariable("unused_bits", RubyFixnum.zero(ctx.getRuntime()));
        return val;
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.BIT_STRING;
    }
        
    @JRubyMethod(name={"unused_bits="})
    public IRubyObject set_unused_bits(ThreadContext ctx, IRubyObject value) {
        getInstanceVariables().setInstanceVariable("unused_bits", value);
        return value;
    }
        
    @JRubyMethod
    public IRubyObject unused_bits(ThreadContext ctx) {
        if (!isDecoded()) {
            decodeValue(ctx);
        }
        return getInstanceVariables().getInstanceVariable("unused_bits");
    }
} public static class Asn1BmpString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1BmpString(runtime, type);
        }
    };
        
    private Asn1BmpString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1BmpString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.BMP_STRING;
    }
} public static class Asn1Boolean extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Boolean(runtime, type);
        }
    };
        
    private Asn1Boolean(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Boolean(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.BOOLEAN;
    }
} public static class Asn1EndOfContents extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1EndOfContents(runtime, type);
        }
    };
        
    private Asn1EndOfContents(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1EndOfContents(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=0, optional=1)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        Ruby runtime = ctx.getRuntime();
            
        if (args.length == 1) {
            IRubyObject value = args[0];
            if (!value.isNil())
                throw runtime.newArgumentError("Value must be nil for END_OF_CONTENTS");
        }
            
        RubyAsn1.defaultInitialize(runtime,
                               this,
                               runtime.getNil(), 
                               runtime.newFixnum(getDefaultTag()), 
                               runtime.newSymbol(TagClass.UNIVERSAL.name()));
        return this;
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.END_OF_CONTENTS;
    }
        
    public static Asn1EndOfContents newInstance(ThreadContext ctx, RubyClass type) {
        Asn1EndOfContents eoc = new Asn1EndOfContents(ctx.getRuntime(), type);
        eoc.initialize(ctx, NULL_ARRAY);
        return eoc;
    }
} public static class Asn1Enumerated extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Enumerated(runtime, type);
        }
    };
        
    private Asn1Enumerated(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Enumerated(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.ENUMERATED;
    }
} public static class Asn1GeneralString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1GeneralString(runtime, type);
        }
    };
        
    private Asn1GeneralString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1GeneralString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.GENERAL_STRING;
    }
} public static class Asn1GeneralizedTime extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1GeneralizedTime(runtime, type);
        }
    };
        
    private Asn1GeneralizedTime(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1GeneralizedTime(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.GENERALIZED_TIME;
    }
} public static class Asn1GraphicString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1GraphicString(runtime, type);
        }
    };
        
    private Asn1GraphicString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1GraphicString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.GRAPHIC_STRING;
    }
} public static class Asn1Ia5String extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Ia5String(runtime, type);
        }
    };
        
    private Asn1Ia5String(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Ia5String(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.IA5_STRING;
    }
} public static class Asn1Integer extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Integer(runtime, type);
        }
    };
        
    private Asn1Integer(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Integer(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.INTEGER;
    }
} public static class Asn1Iso64String extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Iso64String(runtime, type);
        }
    };
        
    private Asn1Iso64String(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Iso64String(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.ISO64_STRING;
    }
} public static class Asn1Null extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Null(runtime, type);
        }
    };
        
    private Asn1Null(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Null(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=0, optional=3)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        Ruby runtime = ctx.getRuntime();
        if (args.length == 0) {
            RubyAsn1.defaultInitialize(runtime,
                                   this, 
                                   runtime.getNil(), 
                                   runtime.newFixnum(Asn1Tags.NULL), 
                                   runtime.newSymbol(TagClass.UNIVERSAL.name()));
            return this;
        } else {
            if (!args[0].isNil())
                throw runtime.newArgumentError("Value for ASN.1 NULL must be nil");
            return init(this, ctx, args, getDefaultTag());
        }
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.NULL;
    }
} public static class Asn1NumericString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1NumericString(runtime, type);
        }
    };
        
    private Asn1NumericString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1NumericString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.NUMERIC_STRING;
    }
} public static class Asn1ObjectId extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1ObjectId(runtime, type);
        }
    };
        
    private Asn1ObjectId(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1ObjectId(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.OBJECT_ID;
    }
} public static class Asn1OctetString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1OctetString(runtime, type);
        }
    };
        
    private Asn1OctetString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1OctetString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.OCTET_STRING;
    }
} public static class Asn1PrintableString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1PrintableString(runtime, type);
        }
    };
        
    private Asn1PrintableString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1PrintableString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.PRINTABLE_STRING;
    }
} public static class Asn1Sequence extends Asn1Constructive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Sequence(runtime, type);
        }
    };
        
    private Asn1Sequence(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Sequence(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.SEQUENCE;
    }
} public static class Asn1Set extends Asn1Constructive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Set(runtime, type);
        }
    };
        
    private Asn1Set(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Set(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.SET;
    }
} public static class Asn1T61String extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1T61String(runtime, type);
        }
    };
        
    private Asn1T61String(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1T61String(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.T61_STRING;
    }
} public static class Asn1UniversalString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1UniversalString(runtime, type);
        }
    };
        
    private Asn1UniversalString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1UniversalString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.UNIVERSAL_STRING;
    }
} public static class Asn1UtcTime extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1UtcTime(runtime, type);
        }
    };
        
    private Asn1UtcTime(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1UtcTime(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.UTC_TIME;
    }
} public static class Asn1Utf8String extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1Utf8String(runtime, type);
        }
    };
        
    private Asn1Utf8String(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1Utf8String(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.UTF8_STRING;
    }
} public static class Asn1VideotexString extends Asn1Primitive {
        
    static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
        @Override
        public IRubyObject allocate(Ruby runtime, RubyClass type) {
            return new Asn1VideotexString(runtime, type);
        }
    };
        
    private Asn1VideotexString(Ruby runtime, RubyClass type) {
        super(runtime, type);
    }
        
    public Asn1VideotexString(Ruby runtime, RubyClass type, Asn1Object object) {
        super(runtime, type, object);
    }
        
    @JRubyMethod(required=1, optional=2)
    public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
        return init(this, ctx, args, getDefaultTag());
    }

    @Override
    protected int getDefaultTag() {
        return Asn1Tags.VIDEOTEX_STRING;
    }
}
import impl.krypt.asn1.Asn1Object;
import impl.krypt.asn1.EncodableHeader;
import impl.krypt.asn1.Header;
import impl.krypt.asn1.Length;
import impl.krypt.asn1.ParsedHeader;
import impl.krypt.asn1.ParserFactory;
import impl.krypt.asn1.Tag;
import impl.krypt.asn1.TagClass;
import impl.krypt.asn1.parser.CachingInputStream;
import impl.krypt.asn1.pem.PemInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyEnumerable;
import org.jruby.RubyFixnum;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubySymbol;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.ext.krypt.Errors;
import org.jruby.ext.krypt.Streams;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1BitString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1BmpString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Boolean;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1EndOfContents;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Enumerated;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1GeneralString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1GeneralizedTime;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1GraphicString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Ia5String;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Integer;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Iso64String;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Null;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1NumericString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1ObjectId;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1OctetString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1PrintableString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Sequence;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Set;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1T61String;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1UniversalString;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1UtcTime;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1Utf8String;
import org.jruby.ext.krypt.asn1.Asn1DataClasses.Asn1VideotexString;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockCallback;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.builtin.InstanceVariables;
import org.jruby.util.ByteList;
        }
    }
    
    @JRubyMethod(meta = true)
    public static IRubyObject decode_pem(ThreadContext ctx, IRubyObject recv, IRubyObject value) {
        try {
            Ruby rt = ctx.getRuntime();
            InputStream in = new PemInputStream(Streams.asInputStreamPem(rt, value));
            IRubyObject ret = generateAsn1Data(rt, in);
            if (ret == null) throw Errors.newASN1Error(ctx.getRuntime(), "Could not decode ASN.1 data");
            return ret;
        } catch(Exception e) {
            throw Errors.newParseError(ctx.getRuntime(), e.getMessage());
        }
    }
    
    @JRubyMethod(meta = true)
    public static IRubyObject decode(ThreadContext ctx, IRubyObject recv, IRubyObject value) {
        IRubyObject ret;
        try {
            Ruby rt = ctx.getRuntime();
            InputStream in = Streams.asInputStreamDer(rt, value);
            CachingInputStream cache = new CachingInputStream(in);
            try {
                InputStream pem = new PemInputStream(cache);
                if ((ret = generateAsn1Data(rt, pem)) != null) return ret;
            } catch (RaiseException ex) { }
            InputStream prefix = new ByteArrayInputStream(cache.getCachedBytes());
            InputStream retry = new SequenceInputStream(prefix, in);
            ret = generateAsn1Data(rt, retry);    
        } catch(Exception e) {
            throw Errors.newASN1Error(ctx.getRuntime(), e.getMessage());
        }
        if (ret == null) throw Errors.newASN1Error(ctx.getRuntime(), "Could not decode ASN.1 data");
        return ret;
    }
    
    protected static IRubyObject generateAsn1Data(Ruby runtime, InputStream in) {
        ParsedHeader h = PARSER.next(in);
        if (h == null)
            return null;
        return Asn1Data.newAsn1Data(runtime, h.getObject());
    }
    
    private static RubyClass cASN1Data;
    private static RubyClass cASN1Primitive;
    private static RubyClass cASN1Constructive;
    
    private static RubyClass cASN1EndOfContents;
    private static RubyClass cASN1Boolean;
    private static RubyClass cASN1Integer;
    private static RubyClass cASN1BitString;
    private static RubyClass cASN1OctetString;
    private static RubyClass cASN1Null;
    private static RubyClass cASN1ObjectId;
    private static RubyClass cASN1Enumerated;
    private static RubyClass cASN1Utf8String;
    private static RubyClass cASN1Sequence;
    private static RubyClass cASN1Set;
    private static RubyClass cASN1NumericString;
    private static RubyClass cASN1PrintableString;
    private static RubyClass cASN1T61String;
    private static RubyClass cASN1VideotexString;
    private static RubyClass cASN1Ia5String;
    private static RubyClass cASN1UtcTime;
    private static RubyClass cASN1GeneralizedTime;
    private static RubyClass cASN1GraphicString;
    private static RubyClass cASN1Iso64String;
    private static RubyClass cASN1GeneralString;
    private static RubyClass cASN1UniversalString;
    private static RubyClass cASN1BmpString;
    
    private static Object[][] ASN1_INFOS;
    
    public static void createAsn1(Ruby runtime, RubyModule krypt, RubyClass kryptError) {
        RubyModule mASN1 = runtime.defineModuleUnder("ASN1", krypt);

        RubyClass asn1Error = mASN1.defineClassUnder("ASN1Error", kryptError, kryptError.getAllocator());
        mASN1.defineClassUnder("ParseError", asn1Error, asn1Error.getAllocator());
        mASN1.defineClassUnder("SerializeError", asn1Error, asn1Error.getAllocator());

        mASN1.defineAnnotatedMethods(RubyAsn1.class);
        
        cASN1Data = mASN1.defineClassUnder("ASN1Data", runtime.getObject(), Asn1Data.ALLOCATOR);
        cASN1Data.includeModule(runtime.getModule("Comparable"));
        cASN1Data.defineAnnotatedMethods(Asn1Data.class);

        cASN1Primitive = mASN1.defineClassUnder("Primitive", cASN1Data, Asn1Primitive.PRIMITIVE_ALLOCATOR);
        cASN1Primitive.defineAnnotatedMethods(Asn1Primitive.class);

        cASN1Constructive = mASN1.defineClassUnder("Constructive", cASN1Data, Asn1Constructive.CONSTRUCTIVE_ALLOCATOR);
        cASN1Constructive.includeModule(runtime.getModule("Enumerable"));
        cASN1Constructive.defineAnnotatedMethods(Asn1Constructive.class);

        cASN1EndOfContents   = mASN1.defineClassUnder("EndOfContents", cASN1Primitive, Asn1EndOfContents.ALLOCATOR);
        cASN1EndOfContents.defineAnnotatedMethods(Asn1EndOfContents.class);
        cASN1Boolean         = mASN1.defineClassUnder("Boolean", cASN1Primitive, Asn1Boolean.ALLOCATOR);
        cASN1Boolean.defineAnnotatedMethods(Asn1Boolean.class);
        cASN1Integer         = mASN1.defineClassUnder("Integer", cASN1Primitive, Asn1Integer.ALLOCATOR);
        cASN1Integer.defineAnnotatedMethods(Asn1Integer.class);
        cASN1Enumerated      = mASN1.defineClassUnder("Enumerated", cASN1Primitive, Asn1Enumerated.ALLOCATOR);
        cASN1Enumerated.defineAnnotatedMethods(Asn1Enumerated.class);
        cASN1BitString       = mASN1.defineClassUnder("BitString", cASN1Primitive, Asn1BitString.ALLOCATOR);
        cASN1BitString.defineAnnotatedMethods(Asn1BitString.class);
        cASN1OctetString     = mASN1.defineClassUnder("OctetString",cASN1Primitive, Asn1OctetString.ALLOCATOR);
        cASN1OctetString.defineAnnotatedMethods(Asn1OctetString.class);
        cASN1Utf8String      = mASN1.defineClassUnder("UTF8String",cASN1Primitive, Asn1Utf8String.ALLOCATOR);
        cASN1Utf8String.defineAnnotatedMethods(Asn1Utf8String.class);
        cASN1NumericString   = mASN1.defineClassUnder("NumericString",cASN1Primitive, Asn1NumericString.ALLOCATOR);
        cASN1NumericString.defineAnnotatedMethods(Asn1NumericString.class);
        cASN1PrintableString = mASN1.defineClassUnder("PrintableString",cASN1Primitive, Asn1PrintableString.ALLOCATOR);
        cASN1PrintableString.defineAnnotatedMethods(Asn1PrintableString.class);
        cASN1T61String       = mASN1.defineClassUnder("T61String",cASN1Primitive, Asn1T61String.ALLOCATOR);
        cASN1T61String.defineAnnotatedMethods(Asn1T61String.class);
        cASN1VideotexString  = mASN1.defineClassUnder("VideotexString",cASN1Primitive, Asn1VideotexString.ALLOCATOR);
        cASN1VideotexString.defineAnnotatedMethods(Asn1VideotexString.class);
        cASN1Ia5String       = mASN1.defineClassUnder("IA5String",cASN1Primitive, Asn1Ia5String.ALLOCATOR);
        cASN1Ia5String.defineAnnotatedMethods(Asn1Ia5String.class);
        cASN1GraphicString   = mASN1.defineClassUnder("GraphicString",cASN1Primitive, Asn1GraphicString.ALLOCATOR);
        cASN1GraphicString.defineAnnotatedMethods(Asn1GraphicString.class);
        cASN1Iso64String     = mASN1.defineClassUnder("ISO64String",cASN1Primitive, Asn1Iso64String.ALLOCATOR);