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      /** DER type identifier for ASN.1 "OBJECT IDENTIFIER" value. */
49      public static final byte TYPE_OBJECT_IDENTIFIER = 0x06;
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.ErrorCode.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.ErrorCode.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.ErrorCode.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 {  //NOPMD
138         if (pos >= arr.length) {
139             throw new WSSecurityException(
140                     WSSecurityException.ErrorCode.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.ErrorCode.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 {
177             int nbytes = arr[pos++] & 0x7F;
178             if (pos + nbytes > arr.length) {
179                 throw new WSSecurityException(
180                         WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
181                         "noSKIHandling",
182                         new Object[] {"Invalid DER format"}
183                 );
184             }
185             byte[] lenBytes = new byte[nbytes];
186             System.arraycopy(arr, pos, lenBytes, 0, lenBytes.length);
187             len = new BigInteger(1, lenBytes).intValue();
188             pos += nbytes;
189         }
190         return len;
191     }
192 
193     /**
194      * Return an array of bytes from the current position.
195      *
196      * @param length the number of bytes to return.
197      * @return an array of the requested number of bytes from the current
198      *         position.
199      * @throws WSSecurityException
200      *         if the current position is at the end of the array, or the
201      *         length is negative.
202      */
203     public byte[] getBytes(int length) throws WSSecurityException {
204         if (pos + length > arr.length) {
205             throw new WSSecurityException(
206                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
207                     "noSKIHandling",
208                     new Object[] {"Invalid DER format"}
209              );
210         } else if (length < 0) {
211             throw new WSSecurityException(
212                     WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
213                     "noSKIHandling",
214                     new Object[] {"Unsupported DER format"}
215             );
216         }
217         byte[] value = new byte[length];
218         System.arraycopy(arr, pos, value, 0, length);
219         pos += length;
220         return value;
221     }
222 
223 }