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 }