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 gnu.java.lang.CPStringBuilder;
071    
072    import java.io.ByteArrayInputStream;
073    import java.io.ByteArrayOutputStream;
074    import java.io.DataInputStream;
075    import java.io.InputStream;
076    import java.io.IOException;
077    import java.io.OutputStream;
078    
079    import java.math.BigInteger;
080    
081    import java.util.Arrays;
082    import java.util.StringTokenizer;
083    
084    /**
085     * <p>This class represents Universal Object Identifiers (Oids) and their
086     * associated operations.</p>
087     *
088     * <p>Oids are hierarchically globally-interpretable identifiers used
089     * within the GSS-API framework to identify mechanisms and name formats.</p>
090     *
091     * <p>The structure and encoding of Oids is defined in ISOIEC-8824 and
092     * ISOIEC-8825.  For example the Oid representation of Kerberos V5
093     * mechanism is "1.2.840.113554.1.2.2".</p>
094     *
095     * <p>The {@link GSSName} name class contains <code>public static Oid</code>
096     * objects representing the standard name types defined in GSS-API.</p>
097     */
098    public class Oid
099    {
100    
101      // Constants and fields.
102      // -------------------------------------------------------------------------
103    
104      private static final int OBJECT_IDENTIFIER = 0x06;
105      private static final int RELATIVE_OID      = 0x0d;
106    
107      private final int[] components;
108      private byte[] derOid;
109      private String strOid;
110      private boolean relative;
111    
112      // Constructors.
113      // -------------------------------------------------------------------------
114    
115      /**
116       * Creates an Oid object from a string representation of its integer
117       * components (e.g. "1.2.840.113554.1.2.2").
118       *
119       * @param strOid The string representation for the oid.
120       * @throws GSSException If the argument is badly formed.
121       */
122      public Oid(String strOid) throws GSSException
123      {
124        if (strOid == null)
125          throw new NullPointerException();
126        this.strOid = strOid;
127        try
128          {
129            StringTokenizer tok = new StringTokenizer(strOid, ".");
130            components = new int[tok.countTokens()];
131            int i = 0;
132            while (tok.hasMoreTokens() && i < components.length)
133              {
134                components[i++] = Integer.parseInt(tok.nextToken());
135              }
136          }
137        catch (Exception x)
138          {
139            throw new GSSException(GSSException.FAILURE);
140          }
141        relative = false;
142      }
143    
144      /**
145       * Creates an Oid object from its DER encoding. This refers to the full
146       * encoding including tag and length.  The structure and encoding of
147       * Oids is defined in ISOIEC-8824 and ISOIEC-8825.  This method is
148       * identical in functionality to its byte array counterpart.
149       *
150       * @param derOid Stream containing the DER encoded oid.
151       * @throws GSSException If the DER stream is badly formed, or if the
152       *                      input stream throws an exception.
153       */
154      public Oid(InputStream derOid) throws GSSException
155      {
156        DataInputStream in = new DataInputStream(derOid);
157        try
158          {
159            int tag = in.read() & 0x1F;
160            if (tag != OBJECT_IDENTIFIER && tag != RELATIVE_OID)
161              throw new IOException();
162            int len = in.read();
163            if ((len & ~0x7F) != 0)
164              {
165                byte[] buf = new byte[len & 0x7F];
166                in.readFully(buf);
167                len = new BigInteger(1, buf).intValue();
168              }
169            if (len < 0)
170              throw new IOException();
171            byte[] enc = new byte[len];
172            in.readFully(enc);
173            int[] comp = new int[len + 1];
174            int count = 0;
175            int i = 0;
176            relative = tag == RELATIVE_OID;
177            if (!relative && i < len)
178              {
179                int j = (enc[i] & 0xFF);
180                comp[count++] = j / 40;
181                comp[count++] = j % 40;
182                i++;
183              }
184            while (i < len)
185              {
186                int j = 0;
187                do
188                  {
189                    j = enc[i++] & 0xFF;
190                    comp[count] <<= 7;
191                    comp[count]  |= j & 0x7F;
192                    if (i >= len && (j & 0x80) != 0)
193                      throw new IOException();
194                  }
195                while ((j & 0x80) != 0);
196                count++;
197              }
198            if (count == len)
199              this.components = comp;
200            else
201              {
202                this.components = new int[count];
203                System.arraycopy(comp, 0, components, 0, count);
204              }
205          }
206        catch (IOException ioe)
207          {
208            throw new GSSException(GSSException.FAILURE);
209          }
210      }
211    
212      /**
213       * Creates an Oid object from its DER encoding. This refers to the full
214       * encoding including tag and length.  The structure and encoding of
215       * Oids is defined in ISOIEC-8824 and ISOIEC-8825.  This method is
216       * identical in functionality to its streaming counterpart.
217       *
218       * @param derOid Byte array storing a DER encoded oid.
219       * @throws GSSException If the DER bytes are badly formed.
220       */
221      public Oid(byte[] derOid) throws GSSException
222      {
223        this(new ByteArrayInputStream(derOid));
224        this.derOid = (byte[]) derOid.clone();
225      }
226    
227      Oid(int[] components)
228      {
229        this.components = components;
230        relative = false;
231      }
232    
233      // Instance methods.
234      // -------------------------------------------------------------------------
235    
236      /**
237       * Returns a string representation of the oid's integer components in
238       * dot separated notation (e.g. "1.2.840.113554.1.2.2").
239       *
240       * @return The string representation of this oid.
241       */
242      public String toString()
243      {
244        if (strOid == null)
245          {
246            CPStringBuilder buf = new CPStringBuilder();
247            for (int i = 0; i < components.length; i++)
248              {
249                buf.append(components[i]);
250                if (i < components.length - 1)
251                  buf.append('.');
252              }
253            strOid = buf.toString();
254          }
255        return strOid;
256      }
257    
258      /**
259       * Returns the full ASN.1 DER encoding for this oid object, which
260       * includes the tag and length.
261       *
262       * @return The ASN.1 DER encoding for this oid.
263       * @throws GSSException If encoding fails.
264       */
265      public byte[] getDER() throws GSSException
266      {
267        if (derOid == null)
268          {
269            ByteArrayOutputStream out = new ByteArrayOutputStream(256);
270            try
271              {
272                int i = 0;
273                if (!relative)
274                  {
275                    int b = components[i++] * 40 + (components.length > 1
276                                                    ? components[i++] : 0);
277                    encodeSubId(out, b);
278                  }
279                for ( ; i < components.length; i++)
280                  encodeSubId(out, components[i]);
281                byte[] oid = out.toByteArray();
282                out.reset();
283                if (relative)
284                  out.write(RELATIVE_OID);
285                else
286                  out.write(OBJECT_IDENTIFIER);
287                if (oid.length < 128)
288                  out.write(oid.length);
289                else if (oid.length < 256)
290                  {
291                    out.write(0x81);
292                    out.write(oid.length);
293                  }
294                else if (oid.length < 65536)
295                  {
296                    out.write(0x82);
297                    out.write((oid.length >>> 8) & 0xFF);
298                    out.write(oid.length & 0xFF);
299                  }
300                else if (oid.length < 16777216)
301                  {
302                    out.write(0x83);
303                    out.write((oid.length >>> 16) & 0xFF);
304                    out.write((oid.length >>>  8) & 0xFF);
305                    out.write(oid.length & 0xFF);
306                  }
307                else
308                  {
309                    out.write(0x84);
310                    out.write((oid.length >>> 24) & 0xFF);
311                    out.write((oid.length >>> 16) & 0xFF);
312                    out.write((oid.length >>>  8) & 0xFF);
313                    out.write(oid.length & 0xFF);
314                  }
315                out.write(oid);
316              }
317            catch (IOException ioe)
318              {
319                throw new GSSException(GSSException.FAILURE);
320              }
321            derOid = out.toByteArray();
322          }
323        return (byte[]) derOid.clone();
324      }
325    
326      /**
327       * A utility method to test if an Oid object is contained within the
328       * supplied Oid object array.
329       *
330       * @param oids An array of oids to search.
331       * @return True if this oid is contained in the given array.
332       */
333      public boolean containedIn(Oid[] oids)
334      {
335        for (int i = 0; i < oids.length; i++)
336          {
337            if (equals(oids[i]))
338              return true;
339          }
340        return false;
341      }
342    
343      public boolean equals(Object o)
344      {
345        if (!(o instanceof Oid))
346          return false;
347        Oid that = (Oid) o;
348        return Arrays.equals(components, that.components);
349      }
350    
351      public int hashCode()
352      {
353        int code = 0;
354        for (int i = 0; i < components.length; i++)
355          code += components[i];
356        return code;
357      }
358    
359      // Own methods.
360      // -------------------------------------------------------------------------
361    
362      private static void encodeSubId(OutputStream out, int id) throws IOException
363      {
364        if (id < 128)
365          {
366            out.write(id);
367          }
368        else if (id < 16384)
369          {
370            out.write((id >>> 7) | 0x80);
371            out.write(id & 0x7F);
372          }
373        else if (id < 2097152)
374          {
375            out.write((id >>> 14) | 0x80);
376            out.write(((id >>> 7) | 0x80) & 0xFF);
377            out.write(id & 0x7F);
378          }
379        else if (id < 268435456)
380          {
381            out.write( (id >>> 21) | 0x80);
382            out.write(((id >>> 14) | 0x80) & 0xFF);
383            out.write(((id >>>  7) | 0x80) & 0xFF);
384            out.write(id & 0x7F);
385          }
386      }
387    }