blob: b08031ff3ac6514dfd5321b1143193aa48ce2d5e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.wss4j.common.crypto;
import java.math.BigInteger;
import org.apache.wss4j.common.ext.WSSecurityException;
/**
* Provides the means to navigate through a DER-encoded byte array, to help
* in decoding the contents.
* <p>
* It maintains a "current position" in the array that advances with each
* operation, providing a simple means to handle the type-length-value
* encoding of DER. For example
* <pre>
* decoder.expect(TYPE);
* int length = decoder.getLength();
* byte[] value = decoder.getBytes(len);
* </pre>
*/
public class DERDecoder {
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(DERDecoder.class);
/** DER type identifier for a bit string value */
public static final byte TYPE_BIT_STRING = 0x03;
/** DER type identifier for a octet string value */
public static final byte TYPE_OCTET_STRING = 0x04;
/** DER type identifier for a sequence value */
public static final byte TYPE_SEQUENCE = 0x30;
private byte[] arr;
private int pos;
/**
* Construct a DERDecoder for the given byte array.
*
* @param derEncoded the DER-encoded array to decode.
* @throws WSSecurityException if the given array is null.
*/
public DERDecoder(byte[] derEncoded) throws WSSecurityException {
if (derEncoded == null) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Invalid DER string"}
);
}
arr = derEncoded;
reset();
}
/**
* Reset the current position to the start of the array.
*/
public void reset() {
pos = 0;
}
/**
* Advance the current position by the given number of bytes.
*
* @param length the number of bytes to skip.
* @throws WSSecurityException if length is negative.
*/
public void skip(int length) throws WSSecurityException {
if (length < 0) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Unsupported DER format"}
);
}
pos += length;
}
/**
* Confirm that the byte at the current position matches the given value.
*
* @param val the expected next byte.
* @throws WSSecurityException
* if the current position is at the end of the array, or if the
* byte at the current position doesn't match the expected value.
*/
public void expect(int val) throws WSSecurityException {
expect((byte)(val & 0xFF));
}
/**
* Confirm that the byte at the current position matches the given value.
*
* @param val the expected next byte.
* @throws WSSecurityException
* if the current position is at the end of the array, or if the
* byte at the current position doesn't match the expected value.
*/
public void expect(byte val) throws WSSecurityException {
if (!test(val)) {
LOG.debug("DER mismatch: expected " + val + ", got " + arr[pos]);
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Invalid DER format"}
);
}
pos++;
}
/**
* Test if the byte at the current position matches the given value.
*
* @param val the value to test for a match with the current byte.
* @return true if the byte at the current position matches the given value.
* @throws WSSecurityException if the current position is at the end of
* the array.
*/
public boolean test(byte val) throws WSSecurityException { //NOPMD
if (pos >= arr.length) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Invalid DER format"}
);
}
return arr[pos] == val;
}
/**
* Get the DER length at the current position.
* <p>
* DER length is encoded as
* <ul>
* <li>If the first byte is 0x00 to 0x7F, it describes the actual length.
* <li>If the first byte is 0x80 + n with 0<n<0x7F, the actual length is
* described in the following 'n' bytes.
* <li>The length value 0x80, used only in constructed types, is
* defined as "indefinite length".
* </ul>
*
* @return the length, -1 for indefinite length.
* @throws WSSecurityException
* if the current position is at the end of the array or there is
* an incomplete length specification.
*/
public int getLength() throws WSSecurityException {
if (pos >= arr.length) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Invalid DER format"}
);
}
int len;
if ((arr[pos] & 0xFF) <= 0x7F) {
len = arr[pos++];
} else {
int nbytes = arr[pos++] & 0x7F;
if (pos + nbytes > arr.length) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Invalid DER format"}
);
}
byte[] lenBytes = new byte[nbytes];
System.arraycopy(arr, pos, lenBytes, 0, lenBytes.length);
len = new BigInteger(1, lenBytes).intValue();
pos += nbytes;
}
return len;
}
/**
* Return an array of bytes from the current position.
*
* @param length the number of bytes to return.
* @return an array of the requested number of bytes from the current
* position.
* @throws WSSecurityException
* if the current position is at the end of the array, or the
* length is negative.
*/
public byte[] getBytes(int length) throws WSSecurityException {
if (pos + length > arr.length) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Invalid DER format"}
);
} else if (length < 0) {
throw new WSSecurityException(
WSSecurityException.ErrorCode.UNSUPPORTED_SECURITY_TOKEN,
"noSKIHandling",
new Object[] {"Unsupported DER format"}
);
}
byte[] value = new byte[length];
System.arraycopy(arr, pos, value, 0, length);
pos += length;
return value;
}
}