001    /* Oid.java -- Object identifier class.
002       Copyright (C) 2004 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version.
037    
038       The documentation comments of this class are derived from the text
039       of RFC 2853:  Generic Security Service API Version 2: Java Bindings.
040       That document is covered under the following license notice:
041    
042    Copyright (C) The Internet Society (2000).  All Rights Reserved.
043    
044    This document and translations of it may be copied and furnished to
045    others, and derivative works that comment on or otherwise explain it
046    or assist in its implementation may be prepared, copied, published and
047    distributed, in whole or in part, without restriction of any kind,
048    provided that the above copyright notice and this paragraph are
049    included on all such copies and derivative works.  However, this
050    document itself may not be modified in any way, such as by removing
051    the copyright notice or references to the Internet Society or other
052    Internet organizations, except as needed for the purpose of developing
053    Internet standards in which case the procedures for copyrights defined
054    in the Internet Standards process must be followed, or as required to
055    translate it into languages other than English.
056    
057    The limited permissions granted above are perpetual and will not be
058    revoked by the Internet Society or its successors or assigns.
059    
060    This document and the information contained herein is provided on an
061    "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
062    TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT
063    NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN
064    WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
065    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
066    
067    
068    package org.ietf.jgss;
069    
070    import java.io.ByteArrayInputStream;
071    import java.io.ByteArrayOutputStream;
072    import java.io.DataInputStream;
073    import java.io.InputStream;
074    import java.io.IOException;
075    import java.io.OutputStream;
076    
077    import java.math.BigInteger;
078    
079    import java.util.Arrays;
080    import java.util.StringTokenizer;
081    
082    /**
083     * <p>This class represents Universal Object Identifiers (Oids) and their
084     * associated operations.</p>
085     *
086     * <p>Oids are hierarchically globally-interpretable identifiers used
087     * within the GSS-API framework to identify mechanisms and name formats.</p>
088     *
089     * <p>The structure and encoding of Oids is defined in ISOIEC-8824 and
090     * ISOIEC-8825.  For example the Oid representation of Kerberos V5
091     * mechanism is "1.2.840.113554.1.2.2".</p>
092     *
093     * <p>The {@link GSSName} name class contains <code>public static Oid</code>
094     * objects representing the standard name types defined in GSS-API.</p>
095     */
096    public class Oid
097    {
098    
099      // Constants and fields.
100      // -------------------------------------------------------------------------
101    
102      private static final int OBJECT_IDENTIFIER = 0x06;
103      private static final int RELATIVE_OID      = 0x0d;
104    
105      private final int[] components;
106      private byte[] derOid;
107      private String strOid;
108      private boolean relative;
109    
110      // Constructors.
111      // -------------------------------------------------------------------------
112    
113      /**
114       * Creates an Oid object from a string representation of its integer
115       * components (e.g. "1.2.840.113554.1.2.2").
116       *
117       * @param strOid The string representation for the oid.
118       * @throws GSSException If the argument is badly formed.
119       */
120      public Oid(String strOid) throws GSSException
121      {
122        if (strOid == null)
123          throw new NullPointerException();
124        this.strOid = strOid;
125        try
126          {
127            StringTokenizer tok = new StringTokenizer(strOid, ".");
128            components = new int[tok.countTokens()];
129            int i = 0;
130            while (tok.hasMoreTokens() && i < components.length)
131              {
132                components[i++] = Integer.parseInt(tok.nextToken());
133              }
134          }
135        catch (Exception x)
136          {
137            throw new GSSException(GSSException.FAILURE);
138          }
139        relative = false;
140      }
141    
142      /**
143       * Creates an Oid object from its DER encoding. This refers to the full
144       * encoding including tag and length.  The structure and encoding of
145       * Oids is defined in ISOIEC-8824 and ISOIEC-8825.  This method is
146       * identical in functionality to its byte array counterpart.
147       *
148       * @param derOid Stream containing the DER encoded oid.
149       * @throws GSSException If the DER stream is badly formed, or if the
150       *                      input stream throws an exception.
151       */
152      public Oid(InputStream derOid) throws GSSException
153      {
154        DataInputStream in = new DataInputStream(derOid);
155        try
156          {
157            int tag = in.read() & 0x1F;
158            if (tag != OBJECT_IDENTIFIER && tag != RELATIVE_OID)
159              throw new IOException();
160            int len = in.read();
161            if ((len & ~0x7F) != 0)
162              {
163                byte[] buf = new byte[len & 0x7F];
164                in.readFully(buf);
165                len = new BigInteger(1, buf).intValue();
166              }
167            if (len < 0)
168              throw new IOException();
169            byte[] enc = new byte[len];
170            in.readFully(enc);
171            int[] comp = new int[len + 1];
172            int count = 0;
173            int i = 0;
174            relative = tag == RELATIVE_OID;
175            if (!relative && i < len)
176              {
177                int j = (enc[i] & 0xFF);
178                comp[count++] = j / 40;
179                comp[count++] = j % 40;
180                i++;
181              }
182            while (i < len)
183              {
184                int j = 0;
185                do
186                  {
187                    j = enc[i++] & 0xFF;
188                    comp[count] <<= 7;
189                    comp[count]  |= j & 0x7F;
190                    if (i >= len && (j & 0x80) != 0)
191                      throw new IOException();
192                  }
193                while ((j & 0x80) != 0);
194                count++;
195              }
196            if (count == len)
197              this.components = comp;
198            else
199              {
200                this.components = new int[count];
201                System.arraycopy(comp, 0, components, 0, count);
202              }
203          }
204        catch (IOException ioe)
205          {
206            throw new GSSException(GSSException.FAILURE);
207          }
208      }
209    
210      /**
211       * Creates an Oid object from its DER encoding. This refers to the full
212       * encoding including tag and length.  The structure and encoding of
213       * Oids is defined in ISOIEC-8824 and ISOIEC-8825.  This method is
214       * identical in functionality to its streaming counterpart.
215       *
216       * @param derOid Byte array storing a DER encoded oid.
217       * @throws GSSException If the DER bytes are badly formed.
218       */
219      public Oid(byte[] derOid) throws GSSException
220      {
221        this(new ByteArrayInputStream(derOid));
222        this.derOid = (byte[]) derOid.clone();
223      }
224    
225      Oid(int[] components)
226      {
227        this.components = components;
228        relative = false;
229      }
230    
231      // Instance methods.
232      // -------------------------------------------------------------------------
233    
234      /**
235       * Returns a string representation of the oid's integer components in
236       * dot separated notation (e.g. "1.2.840.113554.1.2.2").
237       *
238       * @return The string representation of this oid.
239       */
240      public String toString()
241      {
242        if (strOid == null)
243          {
244            StringBuffer buf = new StringBuffer();
245            for (int i = 0; i < components.length; i++)
246              {
247                buf.append(components[i]);
248                if (i < components.length - 1)
249                  buf.append('.');
250              }
251            strOid = buf.toString();
252          }
253        return strOid;
254      }
255    
256      /**
257       * Returns the full ASN.1 DER encoding for this oid object, which
258       * includes the tag and length.
259       *
260       * @return The ASN.1 DER encoding for this oid.
261       * @throws GSSException If encoding fails.
262       */
263      public byte[] getDER() throws GSSException
264      {
265        if (derOid == null)
266          {
267            ByteArrayOutputStream out = new ByteArrayOutputStream(256);
268            try
269              {
270                int i = 0;
271                if (!relative)
272                  {
273                    int b = components[i++] * 40 + (components.length > 1
274                                                    ? components[i++] : 0);
275                    encodeSubId(out, b);
276                  }
277                for ( ; i < components.length; i++)
278                  encodeSubId(out, components[i]);
279                byte[] oid = out.toByteArray();
280                out.reset();
281                if (relative)
282                  out.write(RELATIVE_OID);
283                else
284                  out.write(OBJECT_IDENTIFIER);
285                if (oid.length < 128)
286                  out.write(oid.length);
287                else if (oid.length < 256)
288                  {
289                    out.write(0x81);
290                    out.write(oid.length);
291                  }
292                else if (oid.length < 65536)
293                  {
294                    out.write(0x82);
295                    out.write((oid.length >>> 8) & 0xFF);
296                    out.write(oid.length & 0xFF);
297                  }
298                else if (oid.length < 16777216)
299                  {
300                    out.write(0x83);
301                    out.write((oid.length >>> 16) & 0xFF);
302                    out.write((oid.length >>>  8) & 0xFF);
303                    out.write(oid.length & 0xFF);
304                  }
305                else
306                  {
307                    out.write(0x84);
308                    out.write((oid.length >>> 24) & 0xFF);
309                    out.write((oid.length >>> 16) & 0xFF);
310                    out.write((oid.length >>>  8) & 0xFF);
311                    out.write(oid.length & 0xFF);
312                  }
313                out.write(oid);
314              }
315            catch (IOException ioe)
316              {
317                throw new GSSException(GSSException.FAILURE);
318              }
319            derOid = out.toByteArray();
320          }
321        return (byte[]) derOid.clone();
322      }
323    
324      /**
325       * A utility method to test if an Oid object is contained within the
326       * supplied Oid object array.
327       *
328       * @param oids An array of oids to search.
329       * @return True if this oid is contained in the given array.
330       */
331      public boolean containedIn(Oid[] oids)
332      {
333        for (int i = 0; i < oids.length; i++)
334          {
335            if (equals(oids[i]))
336              return true;
337          }
338        return false;
339      }
340    
341      public boolean equals(Object o)
342      {
343        if (!(o instanceof Oid))
344          return false;
345        Oid that = (Oid) o;
346        return Arrays.equals(components, that.components);
347      }
348    
349      public int hashCode()
350      {
351        int code = 0;
352        for (int i = 0; i < components.length; i++)
353          code += components[i];
354        return code;
355      }
356    
357      // Own methods.
358      // -------------------------------------------------------------------------
359    
360      private static void encodeSubId(OutputStream out, int id) throws IOException
361      {
362        if (id < 128)
363          {
364            out.write(id);
365          }
366        else if (id < 16384)
367          {
368            out.write((id >>> 7) | 0x80);
369            out.write(id & 0x7F);
370          }
371        else if (id < 2097152)
372          {
373            out.write((id >>> 14) | 0x80);
374            out.write(((id >>> 7) | 0x80) & 0xFF);
375            out.write(id & 0x7F);
376          }
377        else if (id < 268435456)
378          {
379            out.write( (id >>> 21) | 0x80);
380            out.write(((id >>> 14) | 0x80) & 0xFF);
381            out.write(((id >>>  7) | 0x80) & 0xFF);
382            out.write(id & 0x7F);
383          }
384      }
385    }