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

com.exceptionfactory.jagged.ssh.SshPublicKeyReader Maven / Gradle / Ivy

/*
 * Copyright 2023 Jagged Contributors
 *
 * Licensed 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 com.exceptionfactory.jagged.ssh;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;

/**
 * SSH Public Key Reader with shared methods
 *
 * @param  Public Key Type
 */
abstract class SshPublicKeyReader implements PublicKeyReader {
    private static final int INTEGER_LENGTH = 4;

    private static final byte SPACE_SEPARATOR = 32;

    private static final Base64.Decoder DECODER = Base64.getDecoder();

    private final String keyAlgorithm;

    private final byte[] keyAlgorithmBinary;

    /**
     * SSH Public Key Reader constructor with required key algorithm
     *
     * @param keyAlgorithm Public key algorithm
     */
    SshPublicKeyReader(final String keyAlgorithm) {
        this.keyAlgorithm = Objects.requireNonNull(keyAlgorithm, "Algorithm required");
        this.keyAlgorithmBinary = keyAlgorithm.getBytes(StandardCharsets.US_ASCII);
    }

    /**
     * Read Public Key
     *
     * @param inputBuffer Input Buffer to be read
     * @return RSA Public Key
     * @throws GeneralSecurityException Thrown on failures to parse input buffer
     */
    @Override
    public T read(final ByteBuffer inputBuffer) throws GeneralSecurityException {
        Objects.requireNonNull(inputBuffer, "Input Buffer required");

        final T publicKey;

        final byte[] algorithm = new byte[keyAlgorithmBinary.length];
        inputBuffer.get(algorithm);

        if (Arrays.equals(keyAlgorithmBinary, algorithm)) {
            final byte separator = inputBuffer.get();
            if (SPACE_SEPARATOR == separator) {
                publicKey = readEncodedPublicKey(inputBuffer);
            } else {
                throw new InvalidKeyException("Algorithm format space separator not found");
            }
        } else {
            throw new InvalidKeyException(String.format("Public key algorithm format [%s] not found", keyAlgorithm));
        }

        return publicKey;
    }

    /**
     * Read Public Key from decoded buffer
     *
     * @param decodedBuffer Buffer of bytes decoded from Base64 public key
     * @return Public Key
     * @throws GeneralSecurityException Thrown when the decoded buffer does not contain valid key information
     */
    protected abstract T readPublicKey(ByteBuffer decodedBuffer) throws GeneralSecurityException;

    /**
     * Read length-delimited array of bytes
     *
     * @param buffer Byte buffer to be read
     * @return Byte array read
     * @throws InvalidKeyException Thrown on invalid number of bytes indicated to be read
     */
    protected byte[] readBlock(final ByteBuffer buffer) throws InvalidKeyException {
        if (buffer.remaining() < INTEGER_LENGTH) {
            throw new InvalidKeyException(String.format("Public Key buffer size [%d] less than required", buffer.remaining()));
        }

        final int length = buffer.getInt();
        if (length > buffer.remaining()) {
            throw new InvalidKeyException(String.format("Public Key block length [%d] not valid", length));
        }

        final byte[] block = new byte[length];
        buffer.get(block);
        return block;
    }

    private T readEncodedPublicKey(final ByteBuffer inputBuffer) throws GeneralSecurityException {
        final T publicKey;

        final byte[] encoded = readEncodedBytes(inputBuffer);
        final byte[] decoded = DECODER.decode(encoded);
        final ByteBuffer decodedBuffer = ByteBuffer.wrap(decoded);

        final byte[] algorithm = readBlock(decodedBuffer);
        if (Arrays.equals(keyAlgorithmBinary, algorithm)) {
            publicKey = readPublicKey(decodedBuffer);
        } else {
            throw new InvalidKeyException(String.format("Encoded key algorithm [%s] not found", keyAlgorithm));
        }

        return publicKey;
    }

    private byte[] readEncodedBytes(final ByteBuffer inputBuffer) {
        final int startPosition = inputBuffer.position();

        int endPosition = startPosition;
        while (inputBuffer.hasRemaining()) {
            final byte character = inputBuffer.get();
            if (SPACE_SEPARATOR == character) {
                break;
            }
            endPosition = inputBuffer.position();
        }

        final int length = endPosition - startPosition;
        final byte[] encoded = new byte[length];
        inputBuffer.position(startPosition);
        inputBuffer.get(encoded);
        return encoded;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy