Source for gnu.javax.crypto.key.IncomingMessage

   1: /* IncomingMessage.java --
   2:    Copyright (C) 2003, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is a 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 of the License, or (at
   9: your option) 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; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: 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.javax.crypto.key;
  40: 
  41: import gnu.java.security.Registry;
  42: import gnu.java.security.key.IKeyPairCodec;
  43: import gnu.java.security.key.dss.DSSKeyPairPKCS8Codec;
  44: import gnu.java.security.key.dss.DSSKeyPairRawCodec;
  45: import gnu.java.security.key.dss.DSSKeyPairX509Codec;
  46: import gnu.java.security.key.rsa.RSAKeyPairPKCS8Codec;
  47: import gnu.java.security.key.rsa.RSAKeyPairRawCodec;
  48: import gnu.java.security.key.rsa.RSAKeyPairX509Codec;
  49: import gnu.javax.crypto.key.dh.DHKeyPairPKCS8Codec;
  50: import gnu.javax.crypto.key.dh.DHKeyPairRawCodec;
  51: import gnu.javax.crypto.key.dh.DHKeyPairX509Codec;
  52: import gnu.javax.crypto.key.srp6.SRPKeyPairRawCodec;
  53: 
  54: import java.io.ByteArrayInputStream;
  55: import java.io.UnsupportedEncodingException;
  56: import java.math.BigInteger;
  57: import java.security.PrivateKey;
  58: import java.security.PublicKey;
  59: 
  60: /**
  61:  * An implementation of an incoming message for use with key agreement
  62:  * protocols.
  63:  */
  64: public class IncomingMessage
  65: {
  66:   /** The internal buffer stream containing the message's contents. */
  67:   protected ByteArrayInputStream in;
  68:   /** The length of the message contents, according to its 4-byte header. */
  69:   protected int length;
  70: 
  71:   /**
  72:    * Constructs an incoming message given the message's encoded form, including
  73:    * its header bytes.
  74:    *
  75:    * @param b the encoded form, including the header bytes, of an incoming
  76:    *          message.
  77:    * @throws KeyAgreementException if the buffer is malformed.
  78:    */
  79:   public IncomingMessage(byte[] b) throws KeyAgreementException
  80:   {
  81:     this();
  82: 
  83:     if (b.length < 4)
  84:       throw new KeyAgreementException("message header too short");
  85:     length =  b[0]         << 24
  86:            | (b[1] & 0xFF) << 16
  87:            | (b[2] & 0xFF) << 8
  88:            | (b[3] & 0xFF);
  89:     if (length > Registry.SASL_BUFFER_MAX_LIMIT || length < 0)
  90:       throw new KeyAgreementException("message size limit exceeded");
  91:     in = new ByteArrayInputStream(b, 4, length);
  92:   }
  93: 
  94:   /** Trivial private constructor for use by the class method. */
  95:   private IncomingMessage()
  96:   {
  97:     super();
  98:   }
  99: 
 100:   /**
 101:    * Returns an instance of a message given its encoded contents, excluding the
 102:    * message's header bytes.
 103:    * <p>
 104:    * Calls the method with the same name and three arguments as:
 105:    * <code>getInstance(raw, 0, raw.length)</code>.
 106:    *
 107:    * @param raw the encoded form, excluding the header bytes.
 108:    * @return a new instance of <code>IncomingMessage</code>.
 109:    */
 110:   public static IncomingMessage getInstance(byte[] raw)
 111:   {
 112:     return getInstance(raw, 0, raw.length);
 113:   }
 114: 
 115:   /**
 116:    * Returns an instance of a message given its encoded contents, excluding the
 117:    * message's header bytes.
 118:    *
 119:    * @param raw the encoded form, excluding the header bytes.
 120:    * @param offset offset where to start using raw bytes from.
 121:    * @param len number of bytes to use.
 122:    * @return a new instance of <code>IncomingMessage</code>.
 123:    */
 124:   public static IncomingMessage getInstance(byte[] raw, int offset, int len)
 125:   {
 126:     IncomingMessage result = new IncomingMessage();
 127:     result.in = new ByteArrayInputStream(raw, offset, len);
 128:     return result;
 129:   }
 130: 
 131:   /**
 132:    * Converts two octets into the number that they represent.
 133:    *
 134:    * @param b the two octets.
 135:    * @return the length.
 136:    */
 137:   public static int twoBytesToLength(byte[] b) throws KeyAgreementException
 138:   {
 139:     int result = (b[0] & 0xFF) << 8 | (b[1] & 0xFF);
 140:     if (result > Registry.SASL_TWO_BYTE_MAX_LIMIT)
 141:       throw new KeyAgreementException("encoded MPI size limit exceeded");
 142:     return result;
 143:   }
 144: 
 145:   /**
 146:    * Converts four octets into the number that they represent.
 147:    *
 148:    * @param b the four octets.
 149:    * @return the length.
 150:    */
 151:   public static int fourBytesToLength(byte[] b) throws KeyAgreementException
 152:   {
 153:     int result =  b[0]         << 24
 154:                | (b[1] & 0xFF) << 16
 155:                | (b[2] & 0xFF) << 8
 156:                | (b[3] & 0xFF);
 157:     if (result > Registry.SASL_FOUR_BYTE_MAX_LIMIT || result < 0)
 158:       throw new KeyAgreementException("encoded entity size limit exceeded");
 159:     return result;
 160:   }
 161: 
 162:   public boolean hasMoreElements()
 163:   {
 164:     return (in.available() > 0);
 165:   }
 166: 
 167:   /**
 168:    * Decodes a public key from the message.
 169:    * <p>
 170:    * See {@link OutgoingMessage#writePublicKey(java.security.PublicKey)} for
 171:    * more details on the internal format.
 172:    *
 173:    * @throws KeyAgreementException if an encoding size constraint is violated or
 174:    *           a mismatch was detected in the encoding.
 175:    */
 176:   public PublicKey readPublicKey() throws KeyAgreementException
 177:   {
 178:     if (in.available() < 5)
 179:       throw new KeyAgreementException("not enough bytes for a public key in message");
 180:     byte[] elementLengthBytes = new byte[4];
 181:     in.read(elementLengthBytes, 0, 4);
 182:     int elementLength = fourBytesToLength(elementLengthBytes);
 183:     if (in.available() < elementLength)
 184:       throw new KeyAgreementException("illegal public key encoding");
 185:     int keyTypeAndFormatID = in.read() & 0xFF;
 186:     elementLength--;
 187:     byte[] kb = new byte[elementLength];
 188:     in.read(kb, 0, elementLength);
 189:     // instantiate the right codec and decode
 190:     IKeyPairCodec kpc = getKeyPairCodec(keyTypeAndFormatID);
 191:     return kpc.decodePublicKey(kb);
 192:   }
 193: 
 194:   /**
 195:    * Decodes a private key from the message.
 196:    * <p>
 197:    * See {@link OutgoingMessage#writePrivateKey(java.security.PrivateKey)} for
 198:    * more details.
 199:    *
 200:    * @throws KeyAgreementException if an encoding size constraint is violated or
 201:    *           a mismatch was detected in the encoding.
 202:    */
 203:   public PrivateKey readPrivateKey() throws KeyAgreementException
 204:   {
 205:     if (in.available() < 5)
 206:       throw new KeyAgreementException("not enough bytes for a private key in message");
 207:     byte[] elementLengthBytes = new byte[4];
 208:     in.read(elementLengthBytes, 0, 4);
 209:     int elementLength = fourBytesToLength(elementLengthBytes);
 210:     if (in.available() < elementLength)
 211:       throw new KeyAgreementException("illegal private key encoding");
 212:     int keyTypeAndFormatID = in.read() & 0xFF;
 213:     elementLength--;
 214:     byte[] kb = new byte[elementLength];
 215:     in.read(kb, 0, elementLength);
 216:     // instantiate the right codec and decode
 217:     IKeyPairCodec kpc = getKeyPairCodec(keyTypeAndFormatID);
 218:     return kpc.decodePrivateKey(kb);
 219:   }
 220: 
 221:   /**
 222:    * Decodes an MPI from the current message's contents.
 223:    *
 224:    * @return a native representation of an MPI.
 225:    * @throws KeyAgreementException if an encoding exception occurs during the
 226:    *           operation.
 227:    */
 228:   public BigInteger readMPI() throws KeyAgreementException
 229:   {
 230:     if (in.available() < 2)
 231:       throw new KeyAgreementException("not enough bytes for an MPI in message");
 232:     byte[] elementLengthBytes = new byte[2];
 233:     in.read(elementLengthBytes, 0, 2);
 234:     int elementLength = twoBytesToLength(elementLengthBytes);
 235:     if (in.available() < elementLength)
 236:       throw new KeyAgreementException("illegal MPI encoding");
 237:     byte[] element = new byte[elementLength];
 238:     in.read(element, 0, element.length);
 239:     return new BigInteger(1, element);
 240:   }
 241: 
 242:   public String readString() throws KeyAgreementException
 243:   {
 244:     if (in.available() < 2)
 245:       throw new KeyAgreementException("not enough bytes for a text in message");
 246:     byte[] elementLengthBytes = new byte[2];
 247:     in.read(elementLengthBytes, 0, 2);
 248:     int elementLength = twoBytesToLength(elementLengthBytes);
 249:     if (in.available() < elementLength)
 250:       throw new KeyAgreementException("illegal text encoding");
 251:     byte[] element = new byte[elementLength];
 252:     in.read(element, 0, element.length);
 253:     String result = null;
 254:     try
 255:       {
 256:         result = new String(element, "UTF8");
 257:       }
 258:     catch (UnsupportedEncodingException x)
 259:       {
 260:         throw new KeyAgreementException("unxupported UTF8 encoding", x);
 261:       }
 262:     return result;
 263:   }
 264: 
 265:   private IKeyPairCodec getKeyPairCodec(int keyTypeAndFormatID)
 266:       throws KeyAgreementException
 267:   {
 268:     int keyType = (keyTypeAndFormatID >>> 4) & 0x0F;
 269:     int formatID = keyTypeAndFormatID & 0x0F;
 270:     switch (formatID)
 271:       {
 272:       case Registry.RAW_ENCODING_ID:
 273:         switch (keyType)
 274:           {
 275:           case 0:
 276:             return new DSSKeyPairRawCodec();
 277:           case 1:
 278:             return new RSAKeyPairRawCodec();
 279:           case 2:
 280:             return new DHKeyPairRawCodec();
 281:           case 3:
 282:             return new SRPKeyPairRawCodec();
 283:           default:
 284:             throw new KeyAgreementException("Unknown key-type for Raw format: "
 285:                                             + keyType);
 286:           }
 287:       case Registry.X509_ENCODING_ID:
 288:         switch (keyType)
 289:           {
 290:           case 0:
 291:             return new DSSKeyPairX509Codec();
 292:           case 1:
 293:             return new RSAKeyPairX509Codec();
 294:           case 2:
 295:             return new DHKeyPairX509Codec();
 296:           default:
 297:             throw new KeyAgreementException("Unknown key-type for X.509 format: "
 298:                                             + keyType);
 299:           }
 300:       case Registry.PKCS8_ENCODING_ID:
 301:         switch (keyType)
 302:           {
 303:           case 0:
 304:             return new DSSKeyPairPKCS8Codec();
 305:           case 1:
 306:             return new RSAKeyPairPKCS8Codec();
 307:           case 2:
 308:             return new DHKeyPairPKCS8Codec();
 309:           default:
 310:             throw new KeyAgreementException("Unknown key-type for PKCS#8 format: "
 311:                                             + keyType);
 312:           }
 313:       default:
 314:         throw new KeyAgreementException("Unknown format identifier: "
 315:                                         + formatID);
 316:       }
 317:   }
 318: }