Source for gnu.java.security.der.DERWriter

   1: /* DERWriter.java -- write Java types in DER format.
   2:    Copyright (C) 2003, 2004, 2005, 2010  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.security.der;
  40: 
  41: import gnu.java.security.OID;
  42: 
  43: import java.io.ByteArrayOutputStream;
  44: import java.io.IOException;
  45: import java.io.OutputStream;
  46: 
  47: import java.math.BigInteger;
  48: 
  49: import java.text.SimpleDateFormat;
  50: 
  51: import java.util.Date;
  52: import java.util.Iterator;
  53: import java.util.List;
  54: import java.util.Set;
  55: import java.util.TimeZone;
  56: 
  57: /**
  58:  * Methods that allow various Java types to be written as a DER
  59:  * (Distinguished Encoding Rules) stream to the specified output stream.
  60:  * DER is used to encode ASN.1 constructions, but this class provides no
  61:  * methods for interacting with ASN.1. Rather, callers should construct
  62:  * their output objects properly for whatever ASN.1 construct is being
  63:  * output.
  64:  *
  65:  * <p>This class only defines static methods; there are no instance
  66:  * variables needed.
  67:  *
  68:  * @author Casey Marshall (csm@gnu.org)
  69:  */
  70: public class DERWriter implements DER
  71: {
  72: 
  73:   // Constructors.
  74:   // ------------------------------------------------------------------------
  75: 
  76:   /** This class only has static methods. */
  77:   private DERWriter()
  78:   {
  79:   }
  80: 
  81:   // Class methods.
  82:   // ------------------------------------------------------------------------
  83: 
  84:   public static int write(OutputStream out, DERValue object)
  85:     throws IOException
  86:   {
  87:     if (DER.CONSTRUCTED_VALUE.equals (object.getValue ()))
  88:       {
  89:         out.write (object.getEncoded ());
  90:         return object.getLength ();
  91:       }
  92: 
  93:     out.write(object.getExternalTag());
  94:     Object value = object.getValue();
  95:     if (value == null)
  96:       {
  97:         writeLength(out, 0);
  98:         return 0;
  99:       }
 100:     if (value instanceof Boolean)
 101:       return writeBoolean(out, (Boolean) value);
 102:     else if (value instanceof BigInteger)
 103:       return writeInteger(out, (BigInteger) value);
 104:     else if (value instanceof Date)
 105:       return writeDate(out, object.getExternalTag(), (Date) value);
 106:     else if (value instanceof String)
 107:       return writeString(out, object.getExternalTag(), (String) value);
 108:     else if (value instanceof List)
 109:       return writeSequence(out, (List) value);
 110:     else if (value instanceof Set)
 111:       return writeSet(out, (Set) value);
 112:     else if (value instanceof BitString)
 113:       return writeBitString(out, (BitString) value);
 114:     else if (value instanceof OID)
 115:       return writeOID(out, (OID) value);
 116:     else if (value instanceof byte[])
 117:       {
 118:         writeLength(out, ((byte[]) value).length);
 119:         out.write((byte[]) value);
 120:         return ((byte[]) value).length;
 121:       }
 122:     else if (value instanceof DERValue)
 123:       {
 124:         ByteArrayOutputStream bout = new ByteArrayOutputStream();
 125:         write(bout, (DERValue) value);
 126:         byte[] buf = bout.toByteArray();
 127:         writeLength(out, buf.length);
 128:         out.write(buf);
 129:         return buf.length;
 130:       }
 131:     else
 132:       throw new DEREncodingException("cannot encode " + value.getClass().getName());
 133:   }
 134: 
 135:   public static int definiteEncodingSize(int length)
 136:   {
 137:     if (length < 128)
 138:       return 1;
 139:     else if (length < 256)
 140:       return 2;
 141:     else if (length < 65536)
 142:       return 3;
 143:     else if (length < 16777216)
 144:       return 4;
 145:     else
 146:       return 5;
 147:   }
 148: 
 149:   // Own methods.
 150:   // ------------------------------------------------------------------------
 151: 
 152:   /**
 153:    * Write a BOOLEAN type to the given output stream.
 154:    *
 155:    * @param out The sink output stream.
 156:    * @param b   The boolean value to write.
 157:    */
 158:   private static int writeBoolean(OutputStream out, Boolean b)
 159:     throws IOException
 160:   {
 161:     writeLength(out, 1);
 162:     if (b.booleanValue())
 163:       out.write(0xFF);
 164:     else
 165:       out.write(0);
 166:     return 1;
 167:   }
 168: 
 169:   /**
 170:    * Write an INTEGER type to the given output stream.
 171:    *
 172:    * @param out The sink output stream.
 173:    * @param integer The integer to write.
 174:    */
 175:   private static int writeInteger(OutputStream out, BigInteger integer)
 176:     throws IOException
 177:   {
 178:     byte[] bytes = integer.toByteArray();
 179:     writeLength(out, bytes.length);
 180:     out.write(bytes);
 181:     return bytes.length;
 182:   }
 183: 
 184:   private static int writeSequence(OutputStream out, List sequence)
 185:     throws IOException
 186:   {
 187:     ByteArrayOutputStream bout = new ByteArrayOutputStream();
 188:     for (Iterator i = sequence.iterator(); i.hasNext(); )
 189:       {
 190:         write(bout, (DERValue) i.next());
 191:       }
 192:     byte[] buf = bout.toByteArray();
 193:     writeLength(out, buf.length);
 194:     out.write(buf);
 195:     return buf.length;
 196:   }
 197: 
 198:   private static int writeSet(OutputStream out, Set set)
 199:     throws IOException
 200:   {
 201:     ByteArrayOutputStream bout = new ByteArrayOutputStream();
 202:     for (Iterator i = set.iterator(); i.hasNext(); )
 203:       {
 204:         write(bout, (DERValue) i.next());
 205:       }
 206:     byte[] buf = bout.toByteArray();
 207:     writeLength(out, buf.length);
 208:     out.write(buf);
 209:     return buf.length;
 210:   }
 211: 
 212:   private static int writeOID(OutputStream out, OID oid)
 213:     throws IOException
 214:   {
 215:     byte[] der = oid.getDER();
 216:     writeLength(out, der.length);
 217:     out.write(der);
 218:     return der.length;
 219:   }
 220: 
 221:   private static int writeBitString(OutputStream out, BitString bs)
 222:     throws IOException
 223:   {
 224:     byte[] buf = bs.getShiftedByteArray();
 225:     writeLength(out, buf.length + 1);
 226:     out.write(bs.getIgnoredBits());
 227:     out.write(buf);
 228:     return buf.length + 1;
 229:   }
 230: 
 231:   private static int writeString(OutputStream out, int tag, String str)
 232:     throws IOException
 233:   {
 234:     byte[] b = null;
 235:     switch (tag & 0x1F)
 236:       {
 237:         case NUMERIC_STRING:
 238:         case PRINTABLE_STRING:
 239:         case T61_STRING:
 240:         case VIDEOTEX_STRING:
 241:         case IA5_STRING:
 242:         case GRAPHIC_STRING:
 243:         case ISO646_STRING:
 244:         case GENERAL_STRING:
 245:           b = toIso88591(str);
 246:           break;
 247: 
 248:         case UNIVERSAL_STRING:
 249:         case BMP_STRING:
 250:           b = toUtf16Be(str);
 251:           break;
 252: 
 253:         case UTF8_STRING:
 254:         default:
 255:           b = toUtf8(str);
 256:           break;
 257:       }
 258:     writeLength(out, b.length);
 259:     out.write(b);
 260:     return b.length;
 261:   }
 262: 
 263:   private static byte[] toIso88591(String string)
 264:   {
 265:     byte[] result = new byte[string.length()];
 266:     for (int i = 0; i < string.length(); i++)
 267:       result[i] = (byte) string.charAt(i);
 268:     return result;
 269:   }
 270: 
 271:   private static byte[] toUtf16Be(String string)
 272:   {
 273:     byte[] result = new byte[string.length() * 2];
 274:     for (int i = 0; i < string.length(); i++)
 275:       {
 276:         result[i*2  ] = (byte) ((string.charAt(i) >>> 8) & 0xFF);
 277:         result[i*2+1] = (byte)  (string.charAt(i) & 0xFF);
 278:       }
 279:     return result;
 280:   }
 281: 
 282:   private static byte[] toUtf8(String string)
 283:   {
 284:     int len = string.length();
 285:     ByteArrayOutputStream buf = new ByteArrayOutputStream(len + (len >> 1));
 286:     for (int i = 0; i < len; i++)
 287:       {
 288:         char c = string.charAt(i);
 289:         if (c < 0x0080)
 290:           buf.write(c & 0xFF);
 291:         else if (c < 0x0800)
 292:           {
 293:             buf.write(0xC0 | ((c >>> 6) & 0x3F));
 294:             buf.write(0x80 |  (c & 0x3F));
 295:           }
 296:         else
 297:           {
 298:             buf.write(0xE0 | ((c >>> 12) & 0x0F));
 299:             buf.write(0x80 | ((c >>>  6) & 0x3F));
 300:             buf.write(0x80 |  (c & 0x3F));
 301:           }
 302:       }
 303:     return buf.toByteArray();
 304:   }
 305: 
 306:   private static int writeDate(OutputStream out, int tag, Date date)
 307:     throws IOException
 308:   {
 309:     SimpleDateFormat sdf = null;
 310:     if ((tag & 0x1F) == UTC_TIME)
 311:       sdf = new SimpleDateFormat("yyMMddHHmmss'Z'");
 312:     else
 313:       sdf = new SimpleDateFormat("yyyyMMddHHmmss'.'SSS'Z'");
 314:     sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
 315:     byte[] b = sdf.format(date).getBytes("ISO-8859-1");
 316:     writeLength(out, b.length);
 317:     out.write(b);
 318:     return b.length;
 319:   }
 320: 
 321:   // Package method.
 322:   // ------------------------------------------------------------------------
 323: 
 324:   static void writeLength(OutputStream out, int len) throws IOException
 325:   {
 326:     if (len < 128)
 327:       out.write(len);
 328:     else if (len < 256)
 329:       {
 330:         out.write(0x81);
 331:         out.write(len);
 332:       }
 333:     else if (len < 65536)
 334:       {
 335:         out.write(0x82);
 336:         out.write(len >> 8);
 337:         out.write(len);
 338:       }
 339:     else if (len < 16777216)
 340:       {
 341:         out.write(0x83);
 342:         out.write(len >> 16);
 343:         out.write(len >>  8);
 344:         out.write(len);
 345:       }
 346:     else
 347:       {
 348:         out.write(0x84);
 349:         out.write(len >> 24);
 350:         out.write(len >> 16);
 351:         out.write(len >>  8);
 352:         out.write(len);
 353:       }
 354:   }
 355: }