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

org.apache.sshd.common.config.keys.KeyEntryResolver Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.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.config.keys;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StreamCorruptedException;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Map;

import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.io.IoUtils;

/**
 * @param   Type of {@link PublicKey}
 * @param   Type of {@link PrivateKey}
 * @author       Apache MINA SSHD Project
 */
public interface KeyEntryResolver
        extends IdentityResourceLoader {
    /**
     * @param  keySize                  Key size in bits
     * @return                          A {@link KeyPair} with the specified key size
     * @throws GeneralSecurityException if unable to generate the pair
     */
    default KeyPair generateKeyPair(int keySize) throws GeneralSecurityException {
        KeyPairGenerator gen = getKeyPairGenerator();
        gen.initialize(keySize);
        return gen.generateKeyPair();
    }

    /**
     * @param  kp                       The {@link KeyPair} to be cloned - ignored if {@code null}
     * @return                          A cloned pair (or {@code null} if no original pair)
     * @throws GeneralSecurityException If failed to clone - e.g., provided key pair does not contain keys of the
     *                                  expected type
     * @see                             #getPublicKeyType()
     * @see                             #getPrivateKeyType()
     */
    default KeyPair cloneKeyPair(KeyPair kp) throws GeneralSecurityException {
        if (kp == null) {
            return null;
        }

        PUB pubCloned = null;
        PublicKey pubOriginal = kp.getPublic();
        Class pubExpected = getPublicKeyType();
        if (pubOriginal != null) {
            Class orgType = pubOriginal.getClass();
            if (!pubExpected.isAssignableFrom(orgType)) {
                throw new InvalidKeyException("Mismatched public key types: expected="
                                              + pubExpected.getSimpleName()
                                              + ", actual=" + orgType.getSimpleName());
            }

            PUB castPub = pubExpected.cast(pubOriginal);
            pubCloned = clonePublicKey(castPub);
        }

        PRV prvCloned = null;
        PrivateKey prvOriginal = kp.getPrivate();
        Class prvExpected = getPrivateKeyType();
        if (prvOriginal != null) {
            Class orgType = prvOriginal.getClass();
            if (!prvExpected.isAssignableFrom(orgType)) {
                throw new InvalidKeyException("Mismatched private key types: expected="
                                              + prvExpected.getSimpleName()
                                              + ", actual=" + orgType.getSimpleName());
            }

            PRV castPrv = prvExpected.cast(prvOriginal);
            prvCloned = clonePrivateKey(castPrv);
        }

        return new KeyPair(pubCloned, prvCloned);
    }

    /**
     * @param  key                      The {@link PublicKey} to clone - ignored if {@code null}
     * @return                          The cloned key (or {@code null} if no original key)
     * @throws GeneralSecurityException If failed to clone the key
     */
    PUB clonePublicKey(PUB key) throws GeneralSecurityException;

    /**
     * @param  key                      The {@link PrivateKey} to clone - ignored if {@code null}
     * @return                          The cloned key (or {@code null} if no original key)
     * @throws GeneralSecurityException If failed to clone the key
     */
    PRV clonePrivateKey(PRV key) throws GeneralSecurityException;

    /**
     * @return                          A {@link KeyPairGenerator} suitable for this decoder
     * @throws GeneralSecurityException If failed to create the generator
     */
    KeyPairGenerator getKeyPairGenerator() throws GeneralSecurityException;

    /**
     * @return                          A {@link KeyFactory} suitable for the specific decoder type
     * @throws GeneralSecurityException If failed to create one
     */
    KeyFactory getKeyFactoryInstance() throws GeneralSecurityException;

    static int encodeString(OutputStream s, String v) throws IOException {
        return encodeString(s, v, StandardCharsets.UTF_8);
    }

    static int encodeString(OutputStream s, String v, String charset) throws IOException {
        return encodeString(s, v, Charset.forName(charset));
    }

    static int encodeString(OutputStream s, String v, Charset cs) throws IOException {
        return writeRLEBytes(s, v.getBytes(cs));
    }

    static int encodeBigInt(OutputStream s, BigInteger v) throws IOException {
        return writeRLEBytes(s, v.toByteArray());
    }

    static int writeRLEBytes(OutputStream s, byte... bytes) throws IOException {
        return writeRLEBytes(s, bytes, 0, bytes.length);
    }

    static int writeRLEBytes(OutputStream s, byte[] bytes, int off, int len) throws IOException {
        byte[] lenBytes = encodeInt(s, len);
        s.write(bytes, off, len);
        return lenBytes.length + len;
    }

    static byte[] encodeInt(OutputStream s, int v) throws IOException {
        byte[] bytes = {
                (byte) ((v >> 24) & 0xFF),
                (byte) ((v >> 16) & 0xFF),
                (byte) ((v >> 8) & 0xFF),
                (byte) (v & 0xFF)
        };
        s.write(bytes);
        return bytes;
    }

    static String decodeString(InputStream s, int maxChars) throws IOException {
        return decodeString(s, StandardCharsets.UTF_8, maxChars);
    }

    static String decodeString(InputStream s, String charset, int maxChars) throws IOException {
        return decodeString(s, Charset.forName(charset), maxChars);
    }

    static String decodeString(InputStream s, Charset cs, int maxChars) throws IOException {
        byte[] bytes = readRLEBytes(s, maxChars * 4 /* in case UTF-8 with weird characters */);
        return new String(bytes, cs);
    }

    static BigInteger decodeBigInt(InputStream s) throws IOException {
        return new BigInteger(readRLEBytes(s, IdentityResourceLoader.MAX_BIGINT_OCTETS_COUNT));
    }

    static byte[] readRLEBytes(InputStream s, int maxAllowed) throws IOException {
        int len = decodeInt(s);
        if (len > maxAllowed) {
            throw new StreamCorruptedException(
                    "Requested block length (" + len + ") exceeds max. allowed (" + maxAllowed + ")");
        }
        if (len < 0) {
            throw new StreamCorruptedException("Negative block length requested: " + len);
        }

        byte[] bytes = new byte[len];
        IoUtils.readFully(s, bytes);
        return bytes;
    }

    static int decodeInt(InputStream s) throws IOException {
        byte[] bytes = { 0, 0, 0, 0 };
        IoUtils.readFully(s, bytes);
        return ((bytes[0] & 0xFF) << 24)
               | ((bytes[1] & 0xFF) << 16)
               | ((bytes[2] & 0xFF) << 8)
               | (bytes[3] & 0xFF);
    }

    static Map.Entry decodeString(byte[] buf, int maxChars) {
        return decodeString(buf, 0, NumberUtils.length(buf), maxChars);
    }

    static Map.Entry decodeString(byte[] buf, int offset, int available, int maxChars) {
        return decodeString(buf, offset, available, StandardCharsets.UTF_8, maxChars);
    }

    static Map.Entry decodeString(byte[] buf, Charset cs, int maxChars) {
        return decodeString(buf, 0, NumberUtils.length(buf), cs, maxChars);
    }

    /**
     * Decodes a run-length encoded string
     *
     * @param  buf       The buffer with the data bytes
     * @param  offset    The offset in the buffer to decode the string
     * @param  available The max. available data starting from the offset
     * @param  cs        The {@link Charset} to use to decode the string
     * @param  maxChars  Max. allowed characters in string - if more than that is encoded then an
     *                   {@link IndexOutOfBoundsException} will be thrown
     * @return           The decoded string + the offset of the next byte after it
     * @see              #readRLEBytes(byte[], int, int, int)
     */
    static Map.Entry decodeString(
            byte[] buf, int offset, int available, Charset cs, int maxChars) {
        Map.Entry result = readRLEBytes(buf, offset, available, maxChars * 4 /*
                                                                                               * in case UTF-8 with
                                                                                               * weird characters
                                                                                               */);
        byte[] bytes = result.getKey();
        Integer nextOffset = result.getValue();
        return new SimpleImmutableEntry<>(new String(bytes, cs), nextOffset);
    }

    static Map.Entry readRLEBytes(byte[] buf, int maxAllowed) {
        return readRLEBytes(buf, 0, NumberUtils.length(buf), maxAllowed);
    }

    /**
     * Decodes a run-length encoded byte array
     *
     * @param  buf        The buffer with the data bytes
     * @param  offset     The offset in the buffer to decode the array
     * @param  available  The max. available data starting from the offset
     * @param  maxAllowed Max. allowed data in decoded buffer - if more than that is encoded then an
     *                    {@link IndexOutOfBoundsException} will be thrown
     * @return            The decoded data buffer + the offset of the next byte after it
     */
    static Map.Entry readRLEBytes(byte[] buf, int offset, int available, int maxAllowed) {
        int len = decodeInt(buf, offset, available);
        if (len > maxAllowed) {
            throw new IndexOutOfBoundsException(
                    "Requested block length (" + len + ") exceeds max. allowed (" + maxAllowed + ")");
        }
        if (len < 0) {
            throw new IndexOutOfBoundsException("Negative block length requested: " + len);
        }

        available -= Integer.BYTES;
        if (len > available) {
            throw new IndexOutOfBoundsException("Requested block length (" + len + ") exceeds remaing (" + available + ")");
        }

        byte[] bytes = new byte[len];
        offset += Integer.BYTES;
        System.arraycopy(buf, offset, bytes, 0, len);
        return new SimpleImmutableEntry<>(bytes, Integer.valueOf(offset + len));
    }

    static int decodeInt(byte[] buf) {
        return decodeInt(buf, 0, NumberUtils.length(buf));
    }

    static int decodeInt(byte[] buf, int offset, int available) {
        if (available < Integer.BYTES) {
            throw new IndexOutOfBoundsException(
                    "Available data length (" + available + ") cannot accommodate integer encoding");
        }

        return ((buf[offset] & 0xFF) << 24)
               | ((buf[offset + 1] & 0xFF) << 16)
               | ((buf[offset + 2] & 0xFF) << 8)
               | (buf[offset + 3] & 0xFF);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy