/* * 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.signature; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.util.Objects; import org.apache.sshd.common.util.NumberUtils; import org.apache.sshd.common.util.Pair; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.BufferUtils; import org.apache.sshd.common.util.security.SecurityUtils; /** * Useful base class for {@link Signature} implementation * * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a> */ public abstract class AbstractSignature implements Signature { private java.security.Signature signatureInstance; private final String algorithm; protected AbstractSignature(String algorithm) { this.algorithm = ValidateUtils.checkNotNullAndNotEmpty(algorithm, "No signature algorithm specified"); } @Override public final String getAlgorithm() { return algorithm; } /** * Initializes the internal signature instance * * @param algo The signature's algorithm * @param forSigning If {@code true} then it is being initialized for signing, * otherwise for verifying a signature * @return The {@link java.security.Signature} instance * @throws GeneralSecurityException if failed to initialize */ protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException { return SecurityUtils.getSignature(algo); } /** * @return The current {@link java.security.Signature} instance * - {@code null} if not initialized * @see #doInitSignature(String, boolean) */ protected java.security.Signature getSignature() { return signatureInstance; } @Override public byte[] sign() throws Exception { java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized"); return signature.sign(); } @Override public void initVerifier(PublicKey key) throws Exception { String algo = getAlgorithm(); signatureInstance = Objects.requireNonNull(doInitSignature(algo, false), "No signature instance create"); signatureInstance.initVerify(Objects.requireNonNull(key, "No public key provided")); } @Override public void initSigner(PrivateKey key) throws Exception { String algo = getAlgorithm(); signatureInstance = Objects.requireNonNull(doInitSignature(algo, true), "No signature instance create"); signatureInstance.initSign(Objects.requireNonNull(key, "No private key provided")); } @Override public void update(byte[] hash, int off, int len) throws Exception { java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized"); signature.update(hash, off, len); } /** * Makes an attempt to detect if the signature is encoded or pure data * * @param sig The original signature * @return A {@link Pair} where first value is the key type and second * value is the data - {@code null} if not encoded */ protected Pair<String, byte[]> extractEncodedSignature(byte[] sig) { final int dataLen = NumberUtils.length(sig); // if it is encoded then we must have at least 2 UINT32 values if (dataLen < (2 * Integer.BYTES)) { return null; } long keyTypeLen = BufferUtils.getUInt(sig, 0, dataLen); // after the key type we MUST have data bytes if (keyTypeLen >= (dataLen - Integer.BYTES)) { return null; } int keyTypeStartPos = Integer.BYTES; int keyTypeEndPos = keyTypeStartPos + (int) keyTypeLen; int remainLen = dataLen - keyTypeEndPos; // must have UINT32 with the data bytes length if (remainLen < Integer.BYTES) { return null; } long dataBytesLen = BufferUtils.getUInt(sig, keyTypeEndPos, remainLen); // make sure reported number of bytes does not exceed available if (dataBytesLen > (remainLen - Integer.BYTES)) { return null; } String keyType = new String(sig, keyTypeStartPos, (int) keyTypeLen, StandardCharsets.UTF_8); byte[] data = new byte[(int) dataBytesLen]; System.arraycopy(sig, keyTypeEndPos + Integer.BYTES, data, 0, (int) dataBytesLen); return new Pair<>(keyType, data); } protected boolean doVerify(byte[] data) throws SignatureException { java.security.Signature signature = Objects.requireNonNull(getSignature(), "Signature not initialized"); return signature.verify(data); } @Override public String toString() { return getClass().getSimpleName() + "[" + getAlgorithm() + "]"; } }