/* $Id: AsnOutputStream.java,v 1.1 2004/01/19 02:03:49 rgrimm Exp $ * * Copyright (c) 2000 The Cryptix Foundation Limited. All rights reserved. */ package cryptix.jce.provider.asn; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Class for writing DER-encoded AsnObjects to an OutputStream. * * @version $Revision: 1.1 $ * @author Jeroen C. van Gelderen (gelderen@cryptix.org) */ public final class AsnOutputStream { // Instance variables //............................................................................ private final OutputStream os; // Constructors and java.lang.Object overrides //............................................................................ /** * Convenience method that constructs an instance on top of an * internal ByteArrayOutputStream. */ public AsnOutputStream() { this.os = new ByteArrayOutputStream(); } /** * Constructs an instance on top of the given OutputStream. */ public AsnOutputStream(OutputStream os) { this.os = os; } // Public interface //............................................................................ /** * Calls flush() then close() on the underlying stream. Exceptions are * propagated up. * * @throws IOException * When underlying stream does so. */ public void close() throws IOException { this.os.flush(); this.os.close(); } /** * Calls flush() on the underlying stream. Exceptions are propagated up. * * @throws IOException * When underlying stream does so. */ public void flush() throws IOException { this.os.flush(); } /** * Writes a DER-encoded AsnObject to the underlying stream. */ public void write(AsnObject obj) throws IOException { obj.encode(this); } /** * Delegates to the underlying ByteArrayOutputStream if any. * * @throws IllegalStateException * If the underlying stream is not a ByteArrayOutputStream. */ public byte[] toByteArray() { if( !(this.os instanceof ByteArrayOutputStream) ) throw new IllegalStateException( "Underlying stream is not instanceof ByteArrayOutputStream."); ByteArrayOutputStream baos = (ByteArrayOutputStream)this.os; return baos.toByteArray(); } // AsnObject.encode(...) callbacks and helpers //............................................................................ /** * Returns the number of bytes writeLength() will write when called * with the given 'len'. Yes, ASN/DER encoding sucks, don't get me started. */ /*package*/ int getLengthOfLength(int len) { if( len <= 127 ) return 1; else if( len <= 0xFF ) return 2; else if( len <= 0xFFFF ) return 3; else if( len <= 0xFFFFFF ) return 4; else return 5; } /*package*/ void writeByte(byte b) throws IOException { this.os.write(b&0xFF); } /*package*/ void writeBytes(byte[] data) throws IOException { this.os.write(data); } /** * Write a variable length length (yuk!) as per ASN.1/DER specification. */ /*package*/ void writeLength(int len) throws IOException { if(len < 0 ) throw new IllegalArgumentException("len: < 0"); // shortcut for short form if(len <= 127) { this.os.write( (char)len ); return; } // long form int lenOfLenData = getLengthOfLength(len) - 1; this.os.write( (byte)(lenOfLenData | 0x80) ); while( lenOfLenData-- > 0 ) this.os.write( (byte)(len >>> (lenOfLenData*8)) ); } /*package*/ void writeType(byte type) throws IOException { this.os.write(type); } }