com.itextpdf.signatures.DigestAlgorithms Maven / Gradle / Ivy
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2024 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package com.itextpdf.signatures;
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
import com.itextpdf.signatures.exceptions.SignExceptionMessageConstant;
import com.itextpdf.signatures.logs.SignLogMessageConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.HashMap;
import java.util.Map;
/**
* Class that contains a map with the different message digest algorithms.
*/
public class DigestAlgorithms {
/**
* Algorithm available for signatures since PDF 1.3.
*/
public static final String SHA1 = "SHA-1";
/**
* Algorithm available for signatures since PDF 1.6.
*/
public static final String SHA256 = "SHA-256";
/**
* Algorithm available for signatures since PDF 1.7.
*/
public static final String SHA384 = "SHA-384";
/**
* Algorithm available for signatures since PDF 1.7.
*/
public static final String SHA512 = "SHA-512";
/**
* Algorithm available for signatures since PDF 1.7.
*/
public static final String RIPEMD160 = "RIPEMD160";
/**
* Algorithm available for signatures since PDF 2.0
* extended by ISO/TS 32001.
*/
public static final String SHA3_256 = "SHA3-256";
/**
* Algorithm available for signatures since PDF 2.0
* extended by ISO/TS 32001.
*/
public static final String SHA3_512 = "SHA3-512";
/**
* Algorithm available for signatures since PDF 2.0
* extended by ISO/TS 32001.
*/
public static final String SHA3_384 = "SHA3-384";
/**
* Algorithm available for signatures since PDF 2.0
* extended by ISO/TS 32001.
*
*
* The output length is fixed at 512 bits (64 bytes).
*/
public static final String SHAKE256 = "SHAKE256";
/**
* Maps the digest IDs with the human-readable name of the digest algorithm.
*/
private static final Map digestNames = new HashMap<>();
/**
* Maps digest algorithm that are unknown by the JDKs MessageDigest object to a known one.
*/
private static final Map fixNames = new HashMap<>();
/**
* Maps the name of a digest algorithm with its ID.
*/
private static final Map allowedDigests = new HashMap<>();
/**
* Maps algorithm names to output lengths in bits.
*/
private static final Map bitLengths = new HashMap<>();
private static final IBouncyCastleFactory BOUNCY_CASTLE_FACTORY = BouncyCastleFactoryCreator.getFactory();
private static final Logger LOGGER = LoggerFactory.getLogger(DigestAlgorithms.class);
static {
digestNames.put("1.2.840.113549.2.5", "MD5");
digestNames.put("1.2.840.113549.2.2", "MD2");
digestNames.put("1.3.14.3.2.26", "SHA1");
digestNames.put("2.16.840.1.101.3.4.2.4", "SHA224");
digestNames.put("2.16.840.1.101.3.4.2.1", "SHA256");
digestNames.put("2.16.840.1.101.3.4.2.2", "SHA384");
digestNames.put("2.16.840.1.101.3.4.2.3", "SHA512");
digestNames.put("1.3.36.3.2.2", "RIPEMD128");
digestNames.put("1.3.36.3.2.1", "RIPEMD160");
digestNames.put("1.3.36.3.2.3", "RIPEMD256");
digestNames.put("1.2.840.113549.1.1.4", "MD5");
digestNames.put("1.2.840.113549.1.1.2", "MD2");
digestNames.put("1.2.840.113549.1.1.5", "SHA1");
digestNames.put("1.2.840.113549.1.1.14", "SHA224");
digestNames.put("1.2.840.113549.1.1.11", "SHA256");
digestNames.put("1.2.840.113549.1.1.12", "SHA384");
digestNames.put("1.2.840.113549.1.1.13", "SHA512");
digestNames.put("1.2.840.113549.2.5", "MD5");
digestNames.put("1.2.840.113549.2.2", "MD2");
digestNames.put("1.2.840.10040.4.3", "SHA1");
digestNames.put("2.16.840.1.101.3.4.3.1", "SHA224");
digestNames.put("2.16.840.1.101.3.4.3.2", "SHA256");
digestNames.put("2.16.840.1.101.3.4.3.3", "SHA384");
digestNames.put("2.16.840.1.101.3.4.3.4", "SHA512");
digestNames.put("1.3.36.3.3.1.3", "RIPEMD128");
digestNames.put("1.3.36.3.3.1.2", "RIPEMD160");
digestNames.put("1.3.36.3.3.1.4", "RIPEMD256");
digestNames.put("1.2.643.2.2.9", "GOST3411");
digestNames.put("2.16.840.1.101.3.4.2.7", "SHA3-224");
digestNames.put("2.16.840.1.101.3.4.2.8", "SHA3-256");
digestNames.put("2.16.840.1.101.3.4.2.9", "SHA3-384");
digestNames.put("2.16.840.1.101.3.4.2.10", "SHA3-512");
digestNames.put("2.16.840.1.101.3.4.2.12", "SHAKE256");
fixNames.put("SHA256", SHA256);
fixNames.put("SHA384", SHA384);
fixNames.put("SHA512", SHA512);
allowedDigests.put("MD2", "1.2.840.113549.2.2");
allowedDigests.put("MD-2", "1.2.840.113549.2.2");
allowedDigests.put("MD5", "1.2.840.113549.2.5");
allowedDigests.put("MD-5", "1.2.840.113549.2.5");
allowedDigests.put("SHA1", "1.3.14.3.2.26");
allowedDigests.put("SHA-1", "1.3.14.3.2.26");
allowedDigests.put("SHA224", "2.16.840.1.101.3.4.2.4");
allowedDigests.put("SHA-224", "2.16.840.1.101.3.4.2.4");
allowedDigests.put("SHA256", "2.16.840.1.101.3.4.2.1");
allowedDigests.put("SHA-256", "2.16.840.1.101.3.4.2.1");
allowedDigests.put("SHA384", "2.16.840.1.101.3.4.2.2");
allowedDigests.put("SHA-384", "2.16.840.1.101.3.4.2.2");
allowedDigests.put("SHA512", "2.16.840.1.101.3.4.2.3");
allowedDigests.put("SHA-512", "2.16.840.1.101.3.4.2.3");
allowedDigests.put("RIPEMD128", "1.3.36.3.2.2");
allowedDigests.put("RIPEMD-128", "1.3.36.3.2.2");
allowedDigests.put("RIPEMD160", "1.3.36.3.2.1");
allowedDigests.put("RIPEMD-160", "1.3.36.3.2.1");
allowedDigests.put("RIPEMD256", "1.3.36.3.2.3");
allowedDigests.put("RIPEMD-256", "1.3.36.3.2.3");
allowedDigests.put("GOST3411", "1.2.643.2.2.9");
allowedDigests.put("SHA3-224", "2.16.840.1.101.3.4.2.7");
allowedDigests.put("SHA3-256", "2.16.840.1.101.3.4.2.8");
allowedDigests.put("SHA3-384", "2.16.840.1.101.3.4.2.9");
allowedDigests.put("SHA3-512", "2.16.840.1.101.3.4.2.10");
allowedDigests.put("SHAKE256", "2.16.840.1.101.3.4.2.12");
bitLengths.put("MD2", 128);
bitLengths.put("MD-2", 128);
bitLengths.put("MD5", 128);
bitLengths.put("MD-5", 128);
bitLengths.put("SHA1", 160);
bitLengths.put("SHA-1", 160);
bitLengths.put("SHA224", 224);
bitLengths.put("SHA-224", 224);
bitLengths.put("SHA256", 256);
bitLengths.put("SHA-256", 256);
bitLengths.put("SHA384", 384);
bitLengths.put("SHA-384", 384);
bitLengths.put("SHA512", 512);
bitLengths.put("SHA-512", 512);
bitLengths.put("RIPEMD128", 128);
bitLengths.put("RIPEMD-128", 128);
bitLengths.put("RIPEMD160", 160);
bitLengths.put("RIPEMD-160", 160);
bitLengths.put("RIPEMD256", 256);
bitLengths.put("RIPEMD-256", 256);
bitLengths.put("SHA3-224", 224);
bitLengths.put("SHA3-256", 256);
bitLengths.put("SHA3-384", 384);
bitLengths.put("SHA3-512", 512);
bitLengths.put("SHAKE256", 512);
}
/**
* Get a digest algorithm.
*
* @param digestOid oid of the digest algorithm
* @param provider the provider you want to use to create the hash
* @return MessageDigest object
* @throws NoSuchAlgorithmException thrown when a particular cryptographic algorithm is
* requested but is not available in the environment
* @throws NoSuchProviderException thrown when a particular security provider is
* requested but is not available in the environment
*/
public static MessageDigest getMessageDigestFromOid(String digestOid, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
return getMessageDigest(getDigest(digestOid), provider);
}
/**
* Creates a MessageDigest object that can be used to create a hash.
*
* @param hashAlgorithm the algorithm you want to use to create a hash
* @param provider the provider you want to use to create the hash
* @return a MessageDigest object
* @throws NoSuchAlgorithmException thrown when a particular cryptographic algorithm is
* requested but is not available in the environment
* @throws NoSuchProviderException thrown when a particular security provider is
* requested but is not available in the environment
*/
public static MessageDigest getMessageDigest(String hashAlgorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
return SignUtils.getMessageDigest(hashAlgorithm, provider);
}
/**
* Creates a hash using a specific digest algorithm and a provider.
*
* @param data the message of which you want to create a hash
* @param hashAlgorithm the algorithm used to create the hash
* @param provider the provider used to create the hash
* @return the hash
* @throws GeneralSecurityException when requested cryptographic algorithm or security provider
* is not available
* @throws IOException signals that an I/O exception has occurred
*/
public static byte[] digest(InputStream data, String hashAlgorithm, String provider)
throws GeneralSecurityException, IOException {
MessageDigest messageDigest = getMessageDigest(hashAlgorithm, provider);
return digest(data, messageDigest);
}
/**
* Create a digest based on the inputstream.
*
* @param data data to be digested
* @param messageDigest algorithm to be used
* @return digest of the data
* @throws IOException signals that an I/O exception has occurred
*/
public static byte[] digest(InputStream data, MessageDigest messageDigest)
throws IOException {
byte[] buf = new byte[8192];
int n;
while ((n = data.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
return messageDigest.digest();
}
/**
* Create a digest based on the inputstream.
*
* @param data data to be digested
* @param hashAlgorithm algorithm to be used
* @param externalDigest external digest to be used
*
* @return digest of the data.
*
* @throws IOException signals that an I/O exception has occurred.
* @throws GeneralSecurityException when something goes wrong in calculating the digest.
*/
public static byte[] digest(InputStream data, String hashAlgorithm, IExternalDigest externalDigest)
throws IOException, GeneralSecurityException {
byte[] buf = new byte[8192];
int n;
MessageDigest messageDigest = SignUtils.getMessageDigest(hashAlgorithm, externalDigest);
while ((n = data.read(buf)) > 0) {
messageDigest.update(buf, 0, n);
}
return messageDigest.digest();
}
/**
* Gets the digest name for a certain id.
*
* @param oid an id (for instance "1.2.840.113549.2.5")
*
* @return a digest name (for instance "MD5")
*/
public static String getDigest(String oid) {
String ret = digestNames.get(oid);
if (ret == null) {
try {
String digest = getMessageDigest(oid, BOUNCY_CASTLE_FACTORY.getProviderName()).getAlgorithm();
LOGGER.warn(SignLogMessageConstant.ALGORITHM_NOT_FROM_SPEC);
return digest;
} catch (Exception e) {
return oid;
}
} else {
return ret;
}
}
/**
* Normalize the digest name.
*
* @param algo the name to be normalized
* @return normalized name
*/
public static String normalizeDigestName(String algo) {
if (fixNames.containsKey(algo)) {
return fixNames.get(algo);
}
return algo;
}
/**
* Returns the id of a digest algorithms that is allowed in PDF,
* or null if it isn't allowed.
*
* @param name The name of the digest algorithm.
* @return An oid.
*/
public static String getAllowedDigest(String name) {
if (name == null) {
throw new IllegalArgumentException(
SignExceptionMessageConstant.THE_NAME_OF_THE_DIGEST_ALGORITHM_IS_NULL);
}
String allowedDigest = allowedDigests.get(name.toUpperCase());
if (allowedDigest != null) {
return allowedDigest;
}
allowedDigest = BOUNCY_CASTLE_FACTORY.getDigestAlgorithmOid(name.toUpperCase());
if (allowedDigest != null) {
LOGGER.warn(SignLogMessageConstant.ALGORITHM_NOT_FROM_SPEC);
}
return allowedDigest;
}
/**
* Retrieve the output length in bits of the given digest algorithm.
*
* @param name the name of the digest algorithm
* @return the length of the output of the algorithm in bits
*/
public static int getOutputBitLength(String name) {
if (name == null) {
throw new IllegalArgumentException(
SignExceptionMessageConstant.THE_NAME_OF_THE_DIGEST_ALGORITHM_IS_NULL);
}
return bitLengths.get(name).intValue();
}
}