/** * 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.company.security.csp; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.InvalidParameterException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.ProviderException; import java.security.PublicKey; import java.security.SignatureException; import java.security.SignatureSpi; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.List; import org.company.security.csp.parameter.DigestParameterSpec; abstract class CSPSignature extends SignatureSpi { public static class GOST3411withGOST3410EL extends CSPSignature { public GOST3411withGOST3410EL() { super("GOST3411withGOST3410EL"); } } public static class NONEwithGOST3410EL extends CSPSignature.Raw { public NONEwithGOST3410EL() { super("GOST3411withGOST3410EL"); } } protected String digestName; protected String signName; // message digest implementation we use private final MessageDigest messageDigest; // message digest name private String messageDigestAlgorithm; // flag indicating whether the digest has been reset private boolean needsReset; // the signing key private CSPKey privateKey = null; // the verification key private CSPKey publicKey = null; private List<AlgorithmParameterSpec> parameters; public CSPSignature(String algorithm) { int of = algorithm.indexOf("with"); digestName = null; signName = null; if(of > 0) { digestName = algorithm.substring(0, of); signName = algorithm.substring(of + 4); } if(digestName == null || digestName.equalsIgnoreCase("NONE")) { messageDigest = null; messageDigestAlgorithm = null; } else { try { messageDigest = MessageDigest.getInstance(digestName, CSPProvider.CSP_PROVIDER); // Get the digest's canonical name messageDigestAlgorithm = messageDigest.getAlgorithm(); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); } catch (NoSuchProviderException e) { throw new ProviderException(e); } } needsReset = false; } public abstract static class Raw extends CSPSignature { // the longest supported digest is 512 bits (SHA-512) private static final int RAW_MAX = 64; private final byte[] precomputedDigest; private int offset = 0; public Raw(String algorithm) { super(algorithm); precomputedDigest = new byte[RAW_MAX]; } // Stores the precomputed message digest value. @Override protected void engineUpdate(byte b) throws SignatureException { if (offset >= precomputedDigest.length) { offset = RAW_MAX + 1; return; } precomputedDigest[offset++] = b; } // Stores the precomputed message digest value. @Override protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { if (offset + len > precomputedDigest.length) { offset = RAW_MAX + 1; return; } System.arraycopy(b, off, precomputedDigest, offset, len); offset += len; } // Stores the precomputed message digest value. @Override protected void engineUpdate(ByteBuffer byteBuffer) { int len = byteBuffer.remaining(); if (len <= 0) { return; } if (offset + len > precomputedDigest.length) { offset = RAW_MAX + 1; return; } byteBuffer.get(precomputedDigest, offset, len); offset += len; } @Override protected void resetDigest(){ offset = 0; } // Returns the precomputed message digest value. @Override protected byte[] getDigestValue() throws SignatureException { if (offset > RAW_MAX) { throw new SignatureException("Message digest is too long"); } // Determine the digest algorithm from the digest length if (offset == 20) { setDigestName("SHA1"); } else if (offset == 36) { setDigestName("SHA1+MD5"); } else if (offset == 32) { if(signName != null && signName.startsWith("GOST3410")) { setDigestName("GOST3411"); } else { setDigestName("SHA-256"); } } else if (offset == 48) { setDigestName("SHA-384"); } else if (offset == 64) { setDigestName("SHA-512"); } else if (offset == 16) { setDigestName("MD5"); } else { throw new SignatureException( "Message digest length is not supported"); } byte[] result = new byte[offset]; System.arraycopy(precomputedDigest, 0, result, 0, offset); offset = 0; return result; } } @Override protected void engineInitVerify(PublicKey key) throws InvalidKeyException { if(!(key instanceof CSPPublicKey)) { // берем данные открытого ключа if(key instanceof PublicKey) { byte[] encoded = key.getEncoded(); if(encoded != null) { key = initPublicKey(encoded, encoded.length); } } } if(!(key instanceof CSPPublicKey)) { throw new InvalidKeyException("Key type not supported"); } publicKey = (CSPPublicKey) key; privateKey = null; needsReset = true; resetDigest(); } private CSPPublicKey initPublicKey(byte[] encoded, int length) throws InvalidKeyException { try { return NativeCrypto.initPublicKey(encoded, length); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e.getMessage(), e); } } @Override protected void engineInitSign(PrivateKey key) throws InvalidKeyException { if(!(key instanceof CSPPrivateKey)) { throw new InvalidKeyException("Key type not supported"); } privateKey = (CSPPrivateKey) key; publicKey = null; needsReset = true; resetDigest(); } protected void resetDigest() { if(needsReset) { messageDigest.reset(); needsReset = false; } } protected byte[] getDigestValue() throws SignatureException { needsReset = false; return messageDigest.digest(); } protected void setDigestName(String name) { messageDigestAlgorithm = name; } @Override protected void engineUpdate(byte b) throws SignatureException { messageDigest.update(b); needsReset = true; } @Override protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { messageDigest.update(b, off, len); needsReset = true; } @Override protected void engineUpdate(ByteBuffer input) { messageDigest.update(input); needsReset = true; } @Override protected byte[] engineSign() throws SignatureException { byte[] hash = getDigestValue(); boolean noHashOID = this instanceof Raw; byte[] result; result = signHash(noHashOID, hash, hash.length, messageDigestAlgorithm, privateKey.getProviderId(), privateKey.getContainer()); // Convert signature array from little endian to big endian return convertEndianArray(result); } @SuppressWarnings("unused") private byte[] signHash(boolean noHashOID, byte[] hash, int hashSize, String messageDigestAlgorithm, long hCryptoProvider, long hCryptoKey) { return NativeCrypto.signHash(this, noHashOID, hash, hashSize, messageDigestAlgorithm, hCryptoProvider, hCryptoKey); } private byte[] signHash(boolean noHashOID, byte[] hash, int hashSize, String messageDigestAlgorithm, int providerId, String context) { return NativeCrypto.sign(this, noHashOID, hash, hashSize, messageDigestAlgorithm, providerId, context); } private boolean verifySignedHash(byte[] hash, int hashSize, String messageDigestAlgorithm, byte[] signature, int signatureSize, long hCryptoProvider, long hCryptoKey) { return NativeCrypto.verifySignedHash(hash, hashSize, messageDigestAlgorithm, signature, signatureSize, hCryptoProvider, hCryptoKey); } /** * Convert array from big endian to little endian, or vice versa. */ private byte[] convertEndianArray(byte[] byteArray) { if (byteArray == null || byteArray.length == 0) return byteArray; byte[] retval = new byte[byteArray.length]; // make it big endian for (int i = 0; i < byteArray.length; i++) retval[i] = byteArray[byteArray.length - i - 1]; return retval; } @Override protected boolean engineVerify(byte[] sigBytes) throws SignatureException { byte[] hash = getDigestValue(); return verifySignedHash(hash, hash.length, messageDigestAlgorithm, convertEndianArray(sigBytes), sigBytes.length, publicKey.getHCryptProvider(), publicKey.getHCryptKey()); } @Override @Deprecated protected void engineSetParameter(String param, Object value) throws InvalidParameterException { } @Override @Deprecated protected Object engineGetParameter(String param) throws InvalidParameterException { return null; } @Override protected void engineSetParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { if(params != null) { if(parameters == null) { parameters = new ArrayList<AlgorithmParameterSpec>(); } parameters.add(params); } } /** * Вызывается из {@link NativeCrypto#digestInit(CSPDigest, String)} * * @param hCryptoProvider нативный крипто провайдер * @param hCryptoHash нативный крипто хеш * @param length размер хеша в битах */ public void initDigestParameters(long hCryptoProvider, long hCryptoHash) { if(parameters != null) for(AlgorithmParameterSpec parameter : parameters) if(parameter instanceof DigestParameterSpec) ((DigestParameterSpec) parameter).initDigestParameter(hCryptoProvider, hCryptoHash); } }