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.wss4j.common.crypto;
21  
22  import java.math.BigInteger;
23  
24  import org.apache.wss4j.common.ext.WSSecurityException;
25  
26  /**
27   * Provides the means to navigate through a DER-encoded byte array, to help
28   * in decoding the contents.
29   * <p>
30   * It maintains a "current position" in the array that advances with each
31   * operation, providing a simple means to handle the type-length-value
32   * encoding of DER. For example
33   * <pre>
34   *   decoder.expect(TYPE);
35   *   int length = decoder.getLength();
36   *   byte[] value = decoder.getBytes(len);
37   * </pre>
38   */
39  public class DERDecoder {
40      private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(DERDecoder.class);
41  
42      /** DER type identifier for a bit string value */
43      public static final byte TYPE_BIT_STRING = 0x03;
44      /** DER type identifier for a octet string value */
45      public static final byte TYPE_OCTET_STRING = 0x04;
46      /** DER type identifier for a sequence value */
47      public static final byte TYPE_SEQUENCE = 0x30;
48  
49      private byte[] arr;
50      private int pos;
51  
52      /**
53       * Construct a DERDecoder for the given byte array.
54       *
55       * @param derEncoded the DER-encoded array to decode.
56       * @throws WSSecurityException if the given array is null.
57       */
58      public DERDecoder(byte[] derEncoded) throws WSSecurityException {
59          if (derEncoded == null) {
60              throw new WSSecurityException(
61                      WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
62                      "noSKIHandling",
63                      new Object[] {"Invalid DER string"}
64              );
65          }
66          arr = derEncoded;
67          reset();
68      }
69  
70  
71      /**
72       * Reset the current position to the start of the array.
73       */
74      public void reset() {
75          pos = 0;
76      }
77  
78      /**
79       * Advance the current position by the given number of bytes.
80       *
81       * @param length the number of bytes to skip.
82       * @throws WSSecurityException if length is negative.
83       */
84      public void skip(int length) throws WSSecurityException {
85          if (length < 0) {
86              throw new WSSecurityException(
87                      WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
88                      "noSKIHandling",
89                      new Object[] {"Unsupported DER format"}
90              );
91          }
92          pos += length;
93      }
94  
95      /**
96       * Confirm that the byte at the current position matches the given value.
97       *
98       * @param val the expected next byte.
99       * @throws WSSecurityException
100      *         if the current position is at the end of the array, or if the
101      *         byte at the current position doesn't match the expected value.
102      */
103     public void expect(int val) throws WSSecurityException {
104         expect((byte)(val & 0xFF));
105     }
106 
107     /**
108      * Confirm that the byte at the current position matches the given value.
109      *
110      * @param val the expected next byte.
111      * @throws WSSecurityException
112      *         if the current position is at the end of the array, or if the
113      *         byte at the current position doesn't match the expected value.
114      */
115     public void expect(byte val) throws WSSecurityException {
116         if (!test(val)) {
117             LOG.debug("DER mismatch: expected " + val + ", got " + arr[pos]);
118             throw new WSSecurityException(
119                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
120                     "noSKIHandling",
121                     new Object[] {"Invalid DER format"}
122             );
123         }
124         pos++;
125     }
126 
127     /**
128      * Test if the byte at the current position matches the given value.
129      *
130      * @param val the value to test for a match with the current byte.
131      * @return true if the byte at the current position matches the given value.
132      * @throws WSSecurityException if the current position is at the end of
133      *                             the array.
134      */
135     public boolean test(byte val) throws WSSecurityException {  //NOPMD
136         if (pos >= arr.length) {
137             throw new WSSecurityException(
138                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
139                     "noSKIHandling",
140                     new Object[] {"Invalid DER format"}
141             );
142         }
143         return arr[pos] == val;
144     }
145 
146     /**
147      * Get the DER length at the current position.
148      * <p>
149      * DER length is encoded as
150      * <ul>
151      * <li>If the first byte is 0x00 to 0x7F, it describes the actual length.
152      * <li>If the first byte is 0x80 + n with 0<n<0x7F, the actual length is
153      * described in the following 'n' bytes.
154      * <li>The length value 0x80, used only in constructed types, is
155      * defined as "indefinite length".
156      * </ul>
157      *
158      * @return the length, -1 for indefinite length.
159      * @throws WSSecurityException
160      *         if the current position is at the end of the array or there is
161      *         an incomplete length specification.
162      */
163     public int getLength() throws WSSecurityException {
164         if (pos >= arr.length) {
165             throw new WSSecurityException(
166                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
167                     "noSKIHandling",
168                     new Object[] {"Invalid DER format"}
169             );
170         }
171         int len;
172         if ((arr[pos] & 0xFF) <= 0x7F) {
173             len = arr[pos++];
174         } else {
175             int nbytes = arr[pos++] & 0x7F;
176             if (pos + nbytes > arr.length) {
177                 throw new WSSecurityException(
178                         WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
179                         "noSKIHandling",
180                         new Object[] {"Invalid DER format"}
181                 );
182             }
183             byte[] lenBytes = new byte[nbytes];
184             System.arraycopy(arr, pos, lenBytes, 0, lenBytes.length);
185             len = new BigInteger(1, lenBytes).intValue();
186             pos += nbytes;
187         }
188         return len;
189     }
190 
191     /**
192      * Return an array of bytes from the current position.
193      *
194      * @param length the number of bytes to return.
195      * @return an array of the requested number of bytes from the current
196      *         position.
197      * @throws WSSecurityException
198      *         if the current position is at the end of the array, or the
199      *         length is negative.
200      */
201     public byte[] getBytes(int length) throws WSSecurityException {
202         if (pos + length > arr.length) {
203             throw new WSSecurityException(
204                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
205                     "noSKIHandling",
206                     new Object[] {"Invalid DER format"}
207              );
208         } else if (length < 0) {
209             throw new WSSecurityException(
210                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
211                     "noSKIHandling",
212                     new Object[] {"Unsupported DER format"}
213             );
214         }
215         byte[] value = new byte[length];
216         System.arraycopy(arr, pos, value, 0, length);
217         pos += length;
218         return value;
219     }
220 
221 }