View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security.components.crypto;
21  
22  import java.math.BigInteger;
23  import org.apache.ws.security.WSSecurityException;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  /**
29   * Provides the means to navigate through a DER-encoded byte array, to help
30   * in decoding the contents.
31   * <p>
32   * It maintains a "current position" in the array that advances with each
33   * operation, providing a simple means to handle the type-length-value
34   * encoding of DER. For example
35   * <pre>
36   *   decoder.expect(TYPE);
37   *   int length = decoder.getLength();
38   *   byte[] value = decoder.getBytes(len);
39   * </pre>
40   */
41  public class DERDecoder {
42      private static Log log = LogFactory.getLog(DERDecoder.class);
43  
44      /** DER type identifier for a bit string value */
45      public static final byte TYPE_BIT_STRING = 0x03;
46      /** DER type identifier for a octet string value */
47      public static final byte TYPE_OCTET_STRING = 0x04;
48      /** DER type identifier for a sequence value */
49      public static final byte TYPE_SEQUENCE = 0x30;
50  
51      private byte[] arr;
52      private int pos;
53  
54      /**
55       * Construct a DERDecoder for the given byte array.
56       *
57       * @param derEncoded the DER-encoded array to decode.
58       * @throws WSSecurityException if the given array is null.
59       */
60      public DERDecoder(byte[] derEncoded) throws WSSecurityException {
61          if (derEncoded == null) {
62              throw new WSSecurityException(
63                      WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
64                      "noSKIHandling",
65                      new Object[] { "Invalid DER string" }
66              );
67          }
68          arr = derEncoded;
69          reset();
70      }
71  
72  
73      /**
74       * Reset the current position to the start of the array.
75       */
76      public void reset() {
77          pos = 0;
78      }
79  
80      /**
81       * Advance the current position by the given number of bytes.
82       *
83       * @param length the number of bytes to skip.
84       * @throws WSSecurityException if length is negative.
85       */
86      public void skip(int length) throws WSSecurityException {
87          if (length < 0) {
88              throw new WSSecurityException(
89                      WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
90                      "noSKIHandling",
91                      new Object[] { "Unsupported DER format" }
92              );
93          }
94          pos += length;
95      }
96  
97      /**
98       * Confirm that the byte at the current position matches the given value.
99       *
100      * @param val the expected next byte.
101      * @throws WSSecurityException
102      *         if the current position is at the end of the array, or if the
103      *         byte at the current position doesn't match the expected value.
104      */
105     public void expect(int val) throws WSSecurityException {
106         expect((byte)(val & 0xFF));
107     }
108 
109     /**
110      * Confirm that the byte at the current position matches the given value.
111      *
112      * @param val the expected next byte.
113      * @throws WSSecurityException
114      *         if the current position is at the end of the array, or if the
115      *         byte at the current position doesn't match the expected value.
116      */
117     public void expect(byte val) throws WSSecurityException {
118         if (!test(val)) {
119             log.debug("DER mismatch: expected " + val + ", got " + arr[pos]);
120             throw new WSSecurityException(
121                     WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
122                     "noSKIHandling",
123                     new Object[] { "Invalid DER format" }
124             );
125         }
126         pos++;
127     }
128 
129     /**
130      * Test if the byte at the current position matches the given value.
131      *
132      * @param val the value to test for a match with the current byte.
133      * @return true if the byte at the current position matches the given value.
134      * @throws WSSecurityException if the current position is at the end of
135      *                             the array.
136      */
137     public boolean test(byte val) throws WSSecurityException {
138         if (pos >= arr.length) {
139             throw new WSSecurityException(
140                     WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
141                     "noSKIHandling",
142                     new Object[] { "Invalid DER format" }
143             );
144         }
145         return (arr[pos] == val);
146     }
147 
148     /**
149      * Get the DER length at the current position.
150      * <p>
151      * DER length is encoded as
152      * <ul>
153      * <li>If the first byte is 0x00 to 0x7F, it describes the actual length.
154      * <li>If the first byte is 0x80 + n with 0<n<0x7F, the actual length is
155      * described in the following 'n' bytes.
156      * <li>The length value 0x80, used only in constructed types, is
157      * defined as "indefinite length".
158      * </ul>
159      *
160      * @return the length, -1 for indefinite length.
161      * @throws WSSecurityException
162      *         if the current position is at the end of the array or there is
163      *         an incomplete length specification.
164      */
165     public int getLength() throws WSSecurityException {
166         if (pos >= arr.length) {
167             throw new WSSecurityException(
168                     WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
169                     "noSKIHandling",
170                     new Object[] { "Invalid DER format" }
171             );
172         }
173         int len;
174         if ((arr[pos] & 0xFF) <= 0x7F) {
175             len = arr[pos++];
176         } else if (arr[pos] == 0x80) {
177             len = -1;
178             pos++;
179         } else {
180             int nbytes = arr[pos++] & 0x7F;
181             if (pos + nbytes > arr.length) {
182                 throw new WSSecurityException(
183                         WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
184                         "noSKIHandling",
185                         new Object[] { "Invalid DER format" }
186                 );
187             }
188             byte[] lenBytes = new byte[nbytes];
189             System.arraycopy(arr, pos, lenBytes, 0, lenBytes.length);
190             len = new BigInteger(1, lenBytes).intValue();
191             pos += nbytes;
192         }
193         return len;
194     }
195 
196     /**
197      * Return an array of bytes from the current position.
198      *
199      * @param length the number of bytes to return.
200      * @return an array of the requested number of bytes from the current
201      *         position.
202      * @throws WSSecurityException
203      *         if the current position is at the end of the array, or the
204      *         length is negative.
205      */
206     public byte[] getBytes(int length) throws WSSecurityException {
207         if (pos + length > arr.length) {
208             throw new WSSecurityException(
209                     WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
210                     "noSKIHandling",
211                     new Object[] { "Invalid DER format" }
212              );
213         } else if (length < 0) {
214             throw new WSSecurityException(
215                     WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
216                     "noSKIHandling",
217                     new Object[] { "Unsupported DER format" }
218             );
219         }
220         byte[] value = new byte[length];
221         System.arraycopy(arr, pos, value, 0, length);
222         pos += length;
223         return value;
224     }
225     
226 }