All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sshd.common.util.io.der.DERParser Maven / Gradle / Ivy

There is a newer version: 2.4.1.Final
Show newest version
/*
 * 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.sshd.common.util.io.der;

import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamCorruptedException;
import java.math.BigInteger;
import java.util.Arrays;

import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.buffer.BufferUtils;

/**
 * A bare minimum DER parser - just enough to be able to decode signatures and private keys
 *
 * @author Apache MINA SSHD Project
 */
public class DERParser extends FilterInputStream {
    /**
     * Maximum size of data allowed by {@link #readLength()} - it is a bit arbitrary since one can encode 32-bit length
     * data, but it is good enough for the keys
     */
    public static final int MAX_DER_VALUE_LENGTH = 2 * Short.MAX_VALUE;

    private final byte[] lenBytes = new byte[Integer.BYTES];

    public DERParser(byte... bytes) {
        this(bytes, 0, NumberUtils.length(bytes));
    }

    public DERParser(byte[] bytes, int offset, int len) {
        this(new ByteArrayInputStream(bytes, offset, len));
    }

    public DERParser(InputStream s) {
        super(s);
    }

    /**
     * Decode the length of the field. Can only support length encoding up to 4 octets. In BER/DER encoding, length can
     * be encoded in 2 forms:
     * 
    *
  • *

    * Short form - One octet. Bit 8 has value "0" and bits 7-1 give the length. *

    *
  • * *
  • *

    * Long form - Two to 127 octets (only 4 is supported here). Bit 8 of first octet has value "1" and bits 7-1 give * the number of additional length octets. Second and following octets give the length, base 256, most significant * digit first. *

    *
  • *
* * @return The length as integer * @throws IOException If invalid format found */ public int readLength() throws IOException { int i = read(); if (i == -1) { throw new StreamCorruptedException("Invalid DER: length missing"); } // A single byte short length if ((i & ~0x7F) == 0) { return i; } int num = i & 0x7F; // TODO We can't handle length longer than 4 bytes if ((i >= 0xFF) || (num > lenBytes.length)) { throw new StreamCorruptedException("Invalid DER: length field too big: " + i); } // place the read bytes last so that the 1st ones are zeroes as big endian Arrays.fill(lenBytes, (byte) 0); int n = read(lenBytes, 4 - num, num); if (n < num) { throw new StreamCorruptedException("Invalid DER: length data too short: expected=" + num + ", actual=" + n); } long len = BufferUtils.getUInt(lenBytes); if (len < 0x7FL) { // according to standard: "the shortest possible length encoding must be used" throw new StreamCorruptedException("Invalid DER: length not in shortest form: " + len); } if (len > MAX_DER_VALUE_LENGTH) { throw new StreamCorruptedException( "Invalid DER: data length too big: " + len + " (max=" + MAX_DER_VALUE_LENGTH + ")"); } // we know the cast is safe since it is less than MAX_DER_VALUE_LENGTH which is ~64K return (int) len; } public ASN1Object readObject() throws IOException { int tag = read(); if (tag == -1) { return null; } ASN1Type objType = ASN1Type.fromDERValue(tag); if (objType == ASN1Type.NULL) { return new ASN1Object((byte) tag, 0, GenericUtils.EMPTY_BYTE_ARRAY); } int length = readLength(); byte[] value = new byte[length]; int n = read(value); if (n < length) { throw new StreamCorruptedException( "Invalid DER: stream too short, missing value: read " + n + " out of required " + length); } return new ASN1Object((byte) tag, length, value); } public BigInteger readBigInteger() throws IOException { int type = read(); if (type != 0x02) { throw new StreamCorruptedException("Invalid DER: data type is not an INTEGER: 0x" + Integer.toHexString(type)); } int len = readLength(); byte[] value = new byte[len]; int n = read(value); if (n < len) { throw new StreamCorruptedException( "Invalid DER: stream too short, missing value: read " + n + " out of required " + len); } return new BigInteger(value); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy