/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import javax.security.auth.x500.X500Principal;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.keys.content.x509.XMLX509SKI;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERT61String;
import org.bouncycastle.asn1.DERT61UTF8String;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.DLSet;
import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.IssuerSerial;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.asn1.x509.qualified.QCStatement;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.exception.DSSNullException;
import eu.europa.ec.markt.dss.signature.DSSDocument;
import eu.europa.ec.markt.dss.utils.Base64;
import eu.europa.ec.markt.dss.validation102853.CertificateToken;
import eu.europa.ec.markt.dss.validation102853.loader.DataLoader;
import eu.europa.ec.markt.dss.validation102853.loader.Protocol;
/**
* This class is a collection (grouping) of utility methods.
*/
public final class DSSUtils {
private static final Logger LOG = LoggerFactory.getLogger(DSSUtils.class);
public static final String CERT_BEGIN = "-----BEGIN CERTIFICATE-----\n";
public static final String CERT_END = "-----END CERTIFICATE-----";
private static final BouncyCastleProvider securityProvider = new BouncyCastleProvider();
/**
* Used to build output as Hex
*/
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Used to build output as Hex
*/
private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* FROM: Apache
* The index value when an element is not found in a list or array: {@code -1}.
* This value is returned by methods in this class and can also be used in comparisons with values returned by
* various method from {@link java.util.List}.
*/
public static final int INDEX_NOT_FOUND = -1;
/**
* The empty String {@code ""}.
*
* @since 2.0
*/
public static final String EMPTY = "";
/**
* <p>The maximum size to which the padding constant(s) can expand.</p>
*/
private static final int PAD_LIMIT = 8192;
private static final CertificateFactory certificateFactory;
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
* This date is used in the deterministic identifier computation when the signing time is unknown.
*/
private static final Date deterministicDate = DSSUtils.getUtcDate(1970, 04, 23);
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/**
* The default date pattern: "yyyy-MM-dd"
*/
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
private static MessageDigest sha1Digester;
private static JcaDigestCalculatorProviderBuilder jcaDigestCalculatorProviderBuilder;
static {
try {
Security.addProvider(securityProvider);
certificateFactory = CertificateFactory.getInstance("X.509", "BC");
sha1Digester = getMessageDigest(DigestAlgorithm.SHA1);
jcaDigestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder();
jcaDigestCalculatorProviderBuilder.setProvider("BC");
} catch (CertificateException e) {
LOG.error(e.toString());
throw new DSSException("Platform does not support X509 certificate", e);
} catch (NoSuchProviderException e) {
LOG.error(e.toString());
throw new DSSException("Platform does not support BouncyCastle", e);
} catch (NoSuchAlgorithmException e) {
LOG.error(e.toString());
throw new DSSException("The digest algorithm is not supported", e);
}
}
/**
* The default buffer size to use.
*/
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
/**
* This class is an utility class and cannot be instantiated.
*/
private DSSUtils() {
}
/**
* Formats a date to use for internal purposes (logging, toString)
*
* @param date the date to be converted
* @return the textual representation (a null date will result in "N/A")
*/
public static String formatInternal(final Date date) {
final String formatedDate = (date == null) ? "N/A" : new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT).format(date);
return formatedDate;
}
/**
* Formats the given date-time using the default pattern: {@code DSSUtils.DEFAULT_DATE_TIME_FORMAT}
*
* @param date
* @return
*/
public static String formatDate(final Date date) {
if (date != null) {
final String stringDate = new SimpleDateFormat(DSSUtils.DEFAULT_DATE_TIME_FORMAT).format(date);
return stringDate;
}
return EMPTY;
}
/**
* Converts the given string representation of the date using the {@code DEFAULT_DATE_TIME_FORMAT}.
*
* @param dateString the date string representation
* @return the {@code Date}
* @throws DSSException if the conversion is not possible the {@code DSSException} is thrown.
*/
public static Date parseDate(final String dateString) throws DSSException {
try {
final SimpleDateFormat sdf = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
final Date date = sdf.parse(dateString);
return date;
} catch (ParseException e) {
throw new DSSException(e);
}
}
/**
* Converts the given string representation of the date using the format pattern.
*
* @param format the format to use
* @param dateString the date string representation
* @return the {@code Date}
* @throws DSSException if the conversion is not possible the {@code DSSException} is thrown.
*/
public static Date parseDate(final String format, final String dateString) throws DSSException {
try {
final SimpleDateFormat sdf = new SimpleDateFormat(format);
final Date date = sdf.parse(dateString);
return date;
} catch (ParseException e) {
throw new DSSException(e);
}
}
/**
* Converts the given string representation of the date using the {@code DEFAULT_DATE_TIME_FORMAT}. If an exception is frown durring the prsing then null is returned.
*
* @param dateString the date string representation
* @return the {@code Date} or null if the parsing is not possible
*/
public static Date quietlyParseDate(final String dateString) throws DSSException {
try {
final SimpleDateFormat sdf = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
final Date date = sdf.parse(dateString);
return date;
} catch (Exception e) {
return null;
}
}
/**
* Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
* String will be double the length of the passed array, as it takes two characters to represent any given byte. If
* the input array is null then null is returned. The obtained string is converted to uppercase.
*
* @param value
* @return
*/
public static String toHex(final byte[] value) {
return (value != null) ? new String(encodeHex(value, false)) : null;
}
/**
* Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned
* String will be double the length of the passed array, as it takes two characters to represent any given byte.
*
* @param data a byte[] to convert to Hex characters
* @return A String containing hexadecimal characters
*/
public static String encodeHexString(final byte[] data) {
return new String(encodeHex(data));
}
/**
* Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The maximum length of the returned
* String is limited by {@code maxLength} parameter.
*
* @param data a byte[] to convert to Hex characters
* @param maxLength the maximum length of the returned string
* @return A String containing hexadecimal characters
*/
public static String encodeHexString(final byte[] data, int maxLength) {
byte[] data_ = data.length > maxLength ? Arrays.copyOf(data, maxLength) : data;
final String encoded = new String(encodeHex(data_)) + (data.length > maxLength ? "...(" + (data.length - maxLength) + " more)" : "");
return encoded;
}
/**
* Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
* The returned array will be double the length of the passed array, as it takes two characters to represent any
* given byte.
*
* @param data a byte[] to convert to Hex characters
* @return A char[] containing hexadecimal characters
*/
public static char[] encodeHex(byte[] data) {
return encodeHex(data, true);
}
/**
* Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
* The returned array will be double the length of the passed array, as it takes two characters to represent any
* given byte.
*
* @param data a byte[] to convert to Hex characters
* @param toLowerCase <code>true</code> converts to lowercase, <code>false</code> to uppercase
* @return A char[] containing hexadecimal characters
* @since 1.4
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
* The returned array will be double the length of the passed array, as it takes two characters to represent any
* given byte.
*
* @param data a byte[] to convert to Hex characters
* @param toDigits the output alphabet
* @return A char[] containing hexadecimal characters
* @since 1.4
*/
protected static char[] encodeHex(byte[] data, char[] toDigits) {
int l = data.length;
char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return out;
}
/**
* Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
* returned array will be half the length of the passed array, as it takes two characters to represent any given
* byte. An exception is thrown if the passed char array has an odd number of elements.
*
* @param data An array of characters containing hexadecimal digits
* @return A byte array containing binary data decoded from the supplied char array.
* @throws DSSException Thrown if an odd number or illegal of characters is supplied
*/
public static byte[] decodeHex(char[] data) throws DSSException {
int len = data.length;
if ((len & 0x01) != 0) {
throw new DSSException("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f = f | toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* Converts a hexadecimal character to an integer.
*
* @param ch A character to convert to an integer digit
* @param index The index of the character in the source
* @return An integer
* @throws DSSException Thrown if ch is an illegal hex character
*/
protected static int toDigit(char ch, int index) throws DSSException {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new DSSException("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
/**
* Decodes a Base64 String into bytes.
*
* @param base64String
* @return
*/
public static byte[] base64Decode(final String base64String) throws DSSException {
return Base64.decodeBase64(base64String);
}
/**
* Decodes a Base64 String into bytes.
*
* @param binaryData
* @return
*/
public static byte[] base64Decode(final byte[] binaryData) {
return Base64.decodeBase64(binaryData);
}
/**
* Encodes binary data using the base64 algorithm but does not chunk the output. NOTE: We changed the behaviour of
* this method from multi-line chunking (commons-codec-1.4) to single-line non-chunking (commons-codec-1.5).
*
* @param binaryData
* @return
*/
public static String base64Encode(final byte[] binaryData) {
return Base64.encodeBase64String(binaryData);
}
/**
* Encodes binary data using the base64 algorithm but does not chunk the output.
*
* @param binaryData
* @return
*/
public static byte[] base64BinaryEncode(final byte[] binaryData) {
return Base64.encodeBase64(binaryData);
}
/**
* This method re-encode base 64 encoded string to base 64 encoded byte array.
*
* @param base64String
* @return
*/
public static byte[] base64StringToBase64Binary(final String base64String) {
final byte[] decodedBase64 = Base64.decodeBase64(base64String);
final byte[] encodeBase64 = Base64.encodeBase64(decodedBase64);
return encodeBase64;
}
/**
* Encodes dss document using the base64 algorithm .
*
* @param dssDocument dss document to be encoded
* @return encoded base64 string
*/
public static String base64Encode(DSSDocument dssDocument) {
final byte[] bytes = dssDocument.getBytes();
final String base64EncodedBytes = base64Encode(bytes);
return base64EncodedBytes;
}
/**
* @param certificate
* @return
*/
public static String base64Encode(final X509Certificate certificate) throws DSSException {
try {
final byte[] bytes = certificate.getEncoded();
final String base64EncodedBytes = base64Encode(bytes);
return base64EncodedBytes;
} catch (CertificateEncodingException e) {
throw new DSSException(e);
}
}
/**
* FROM: Apache IOUtils
* Get the contents of an {@code InputStream} as a String
* using the default character encoding of the platform.
* <p/>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedInputStream}.
*
* @param input the {@code InputStream} to read from
* @return the requested String
* @throws NullPointerException if the input is null
* @throws DSSException if an I/O error occurs
*/
public static String toString(InputStream input) throws DSSException {
StringWriter sw = new StringWriter();
copy(input, sw);
return sw.toString();
}
/**
* FROM: Apache IOUtils
* Get the contents of an {@code InputStream} as a String using the specified character encoding.
* <p/>
* Character encoding names can be found at <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
* <p/>
* This method buffers the input internally, so there is no need to use a {@code BufferedInputStream}.
*
* @param input the {@code InputStream} to read from
* @param encoding the encoding to use, null means platform default
* @return the requested String
* @throws NullPointerException if the input is null
* @throws java.io.IOException if an I/O error occurs
*/
public static String toString(InputStream input, String encoding) throws DSSException {
StringWriter sw = new StringWriter();
copy(input, sw, encoding);
return sw.toString();
}
/**
* FROM: Apache IOUtils
* Copy bytes from an {@code InputStream} to chars on a {@code Writer} using the specified character
* encoding.
* <p/>
* This method buffers the input internally, so there is no need to use a {@code BufferedInputStream}.
* <p/>
* Character encoding names can be found at <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
* <p/>
* This method uses {@link java.io.InputStreamReader}.
*
* @param input the {@code InputStream} to read from
* @param output the {@code Writer} to write to
* @param encoding the encoding to use, null means platform default
* @throws DSSException if the input or output is null
* @since Commons IO 1.1
*/
public static void copy(InputStream input, Writer output, String encoding) throws DSSException {
try {
if (encoding == null) {
copy(input, output);
} else {
InputStreamReader in = new InputStreamReader(input, encoding);
copy(in, output);
}
} catch (IOException e) {
throw new DSSException(e);
}
}
public static void copy(InputStream input, Writer output) throws DSSException {
InputStreamReader in = new InputStreamReader(input);
copy(in, output);
}
/**
* FROM: Apache IOUtils
* Copy chars from a {@code Reader} to a {@code Writer}.
* <p/>
* This method buffers the input internally, so there is no need to use a {@code BufferedReader}.
* <p/>
* Large streams (over 2GB) will return a chars copied value of {@code -1} after the copy has completed since
* the correct number of chars cannot be returned as an int. For large streams use the
* {@code copyLarge(Reader, Writer)} method.
*
* @param input the {@code Reader} to read from
* @param output the {@code Writer} to write to
* @return the number of characters copied
* @throws NullPointerException if the input or output is null
* @throws java.io.IOException if an I/O error occurs
* @throws ArithmeticException if the character count is too large
* @since Commons IO 1.1
*/
public static int copy(Reader input, Writer output) throws DSSException {
long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
}
return (int) count;
}
/**
* FROM: Apache IOUtils
* Copy chars from a large (over 2GB) {@code Reader} to a {@code Writer}.
* <p/>
* This method buffers the input internally, so there is no need to use a {@code BufferedReader}.
*
* @param input the {@code Reader} to read from
* @param output the {@code Writer} to write to
* @return the number of characters copied
* @throws NullPointerException if the input or output is null
* @throws java.io.IOException if an I/O error occurs
* @since Commons IO 1.3
*/
private static long copyLarge(Reader input, Writer output) throws DSSException {
try {
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* FROM: Apache IOUtils
* Copy bytes from an {@code InputStream} to an
* {@code OutputStream}.
* <p/>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedInputStream}.
* <p/>
* Large streams (over 2GB) will return a bytes copied value of
* {@code -1} after the copy has completed since the correct
* number of bytes cannot be returned as an int. For large streams
* use the {@code copyLarge(InputStream, OutputStream)} method.
*
* @param input the {@code InputStream} to read from
* @param output the {@code OutputStream} to write to
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws DSSException if an I/O error occurs
* @throws ArithmeticException if the byte count is too large
* @since Commons IO 1.1
*/
public static int copy(final InputStream input, final OutputStream output) throws DSSException {
long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
}
return (int) count;
}
/**
* FROM: Apache IOUtils
* Copy bytes from a large (over 2GB) {@code InputStream} to an
* {@code OutputStream}.
* <p/>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedInputStream}.
*
* @param input the {@code InputStream} to read from
* @param output the {@code OutputStream} to write to
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws DSSException if an I/O error occurs
* @since Commons IO 1.3
*/
private static long copyLarge(InputStream input, OutputStream output) throws DSSException {
try {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* Writes bytes from a {@code byte[]} to an {@code OutputStream}.
*
* @param data the byte array to write, do not modify during output,
* null ignored
* @param output the {@code OutputStream} to write to
* @throws DSSException if output is null or an I/O error occurs
* @since Commons IO 1.1
*/
public static void write(byte[] data, OutputStream output) throws DSSException {
try {
if (data != null) {
output.write(data);
}
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method replaces all \ to /.
*
* @param path
* @return
*/
private static String normalisePath(String path) {
return path.replace('\\', '/');
}
/**
* This method checks if the resource with the given path exists.
*
* @param path
* @return
*/
public static boolean resourceExists(final String path) {
final String path_ = normalisePath(path);
final URL url = DSSUtils.class.getResource(path_);
return url != null;
}
/**
* This method checks if the file with the given path exists.
*
* @param path
* @return
*/
public static boolean fileExists(final String path) {
final String path_ = normalisePath(path);
final boolean exists = new File(path_).exists();
return exists;
}
/**
* This method returns a file reference. The file path is normalised (OS independent)
*
* @param filePath The path to the file.
* @return
*/
public static File getFile(final String filePath) {
final String normalisedFolderFileName = normalisePath(filePath);
final File file = new File(normalisedFolderFileName);
return file;
}
/**
* This method converts the given certificate into its PEM string.
*
* @param cert
* @return
* @throws java.security.cert.CertificateEncodingException
*/
public static String convertToPEM(final X509Certificate cert) throws DSSException {
try {
final Base64 encoder = new Base64(64);
final byte[] derCert = cert.getEncoded();
final String pemCertPre = new String(encoder.encode(derCert));
final String pemCert = CERT_BEGIN + pemCertPre + CERT_END;
return pemCert;
} catch (CertificateEncodingException e) {
throw new DSSException(e);
}
}
/**
* This method loads a certificate from the given resource. The certificate must be DER-encoded and may be supplied in binary or printable
* (Base64) encoding. If the certificate is provided in Base64 encoding, it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and
* must be bounded at the end by -----END CERTIFICATE-----. It throws an {@code DSSException} or return {@code null} when the
* certificate cannot be loaded.
*
* @param path resource location.
* @return
*/
public static X509Certificate loadCertificate(final String path) throws DSSException {
final InputStream inputStream = DSSUtils.class.getResourceAsStream(path);
return loadCertificate(inputStream);
}
/**
* This method loads a certificate from the given location. The certificate must be DER-encoded and may be supplied in binary or printable
* (Base64) encoding. If the certificate is provided in Base64 encoding, it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and
* must be bounded at the end by -----END CERTIFICATE-----. It throws an {@code DSSException} or return {@code null} when the
* certificate cannot be loaded.
*
* @param file
* @return
*/
public static X509Certificate loadCertificate(final File file) throws DSSException {
final InputStream inputStream = DSSUtils.toByteArrayInputStream(file);
final X509Certificate x509Certificate = loadCertificate(inputStream);
return x509Certificate;
}
/**
* This method loads a certificate from the given location. The certificate must be DER-encoded and may be supplied in binary or printable (Base64) encoding. If the
* certificate is provided in Base64 encoding, it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at the end by -----END CERTIFICATE-----.
* It throws an {@code DSSException} or return {@code null} when the certificate cannot be loaded.
*
* @param inputStream input stream containing the certificate
* @return
*/
public static X509Certificate loadCertificate(final InputStream inputStream) throws DSSException {
try {
final X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(inputStream);
return cert;
} catch (CertificateException e) {
throw new DSSException(e);
}
}
/**
* This method loads a certificate from the byte array. The certificate must be DER-encoded and may be supplied in binary or printable
* (Base64) encoding. If the certificate is provided in Base64 encoding, it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and
* must be bounded at the end by -----END CERTIFICATE-----. It throws an {@code DSSException} or return {@code null} when the
* certificate cannot be loaded.
*
* @param input array of bytes containing the certificate
* @return
*/
public static X509Certificate loadCertificate(final byte[] input) throws DSSException {
if (input == null) {
throw new DSSNullException(byte[].class, "X5009 certificate");
}
final ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
return loadCertificate(inputStream);
}
/**
* This method loads a certificate from a base 64 encoded String
*
* @param base64Encoded
* @return
*/
public static X509Certificate loadCertificateFromBase64EncodedString(final String base64Encoded) {
final byte[] bytes = DSSUtils.base64Decode(base64Encoded);
return loadCertificate(bytes);
}
/**
* This method loads the issuer certificate from the given location (AIA). The certificate must be DER-encoded and may be supplied in binary or
* printable (Base64) encoding. If the certificate is provided in Base64 encoding, it must be bounded at the beginning by -----BEGIN
* CERTIFICATE-----, and must be bounded at the end by -----END CERTIFICATE-----. It throws an
* {@code DSSException} or return {@code null} when the certificate cannot be loaded.
*
* @param cert certificate for which the issuer should be loaded
* @param loader the loader to use
* @return
*/
public static X509Certificate loadIssuerCertificate(final X509Certificate cert, final DataLoader loader) {
final String url = getAccessLocation(cert, X509ObjectIdentifiers.id_ad_caIssuers);
if (url == null) {
LOG.info("There is no AIA extension for certificate download.");
return null;
}
LOG.debug("Loading certificate from {}", url);
if (loader == null) {
throw new DSSNullException(DataLoader.class);
}
byte[] bytes = loader.get(url);
if (bytes == null || bytes.length <= 0) {
LOG.error("Unable to read data from {}.", url);
return null;
}
final X509Certificate issuerCert = loadCertificate(bytes);
if (issuerCert == null) {
LOG.error("Unable to read data from {}.", url);
return null;
}
if (!cert.getIssuerX500Principal().equals(issuerCert.getSubjectX500Principal())) {
LOG.info("There is AIA extension, but the issuer subject name and subject name does not match.");
LOG.info("CERT ISSUER : " + cert.getIssuerX500Principal().toString());
LOG.info("ISSUER SUBJECT : " + issuerCert.getSubjectX500Principal().toString());
// return null;
}
return issuerCert;
}
/**
* This method return SKI bytes from certificate or null.
*
* @param x509Certificate {@code X509Certificate}
* @return ski bytes from the given certificate
* @throws Exception
*/
public static byte[] getSki(final X509Certificate x509Certificate) throws DSSException {
try {
final byte[] skiBytesFromCert = XMLX509SKI.getSKIBytesFromCert(x509Certificate);
return skiBytesFromCert;
} catch (XMLSecurityException e) {
return null;
} catch (Exception e) {
throw new DSSException(e);
}
}
private static String getAccessLocation(final X509Certificate certificate, final ASN1ObjectIdentifier accessMethod) {
final byte[] authInfoAccessExtensionValue = certificate.getExtensionValue(Extension.authorityInfoAccess.getId());
if (null == authInfoAccessExtensionValue) {
return null;
}
// Parse the extension
ASN1Sequence asn1Sequence = null;
try {
asn1Sequence = DSSASN1Utils.getAsn1SequenceFromDerOctetString(authInfoAccessExtensionValue);
} catch (DSSException e) {
return null;
}
final AuthorityInformationAccess authorityInformationAccess = AuthorityInformationAccess.getInstance(asn1Sequence);
String accessLocation = null;
final AccessDescription[] accessDescriptions = authorityInformationAccess.getAccessDescriptions();
for (final AccessDescription accessDescription : accessDescriptions) {
// LOG.debug("access method: " + accessDescription.getAccessMethod());
final boolean correctAccessMethod = accessDescription.getAccessMethod().equals(accessMethod);
if (!correctAccessMethod) {
continue;
}
GeneralName gn = accessDescription.getAccessLocation();
if (gn.getTagNo() != GeneralName.uniformResourceIdentifier) {
// LOG.debug("not a uniform resource identifier");
continue;
}
final DERIA5String str = (DERIA5String) ((DERTaggedObject) gn.toASN1Primitive()).getObject();
accessLocation = str.getString();
// The HTTP protocol is preferred.
if (Protocol.isHttpUrl(accessLocation)) {
// LOG.debug("access location: " + accessLocation);
break;
}
}
return accessLocation;
}
/**
* This method loads a CRL from the given base 64 encoded string.
*
* @param base64Encoded
* @return
*/
public static X509CRL loadCRLBase64Encoded(final String base64Encoded) {
final byte[] derEncoded = DSSUtils.base64Decode(base64Encoded);
final X509CRL crl = loadCRL(new ByteArrayInputStream(derEncoded));
return crl;
}
/**
* This method loads a CRL from the given location.
*
* @param byteArray
* @return
*/
public static X509CRL loadCRL(final byte[] byteArray) {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArray);
final X509CRL crl = loadCRL(inputStream);
return crl;
}
/**
* This method loads a CRL from the given location.
*
* @param inputStream
* @return
*/
public static X509CRL loadCRL(final InputStream inputStream) {
try {
final X509CRL crl = (X509CRL) certificateFactory.generateCRL(inputStream);
return crl;
} catch (CRLException e) {
throw new DSSException(e);
}
}
/**
* This method loads an OCSP response from the given base 64 encoded string.
*
* @param base64Encoded base 64 encoded OCSP response
* @return {@code BasicOCSPResp}
*/
public static BasicOCSPResp loadOCSPBase64Encoded(final String base64Encoded) {
final byte[] derEncoded = DSSUtils.base64Decode(base64Encoded);
try {
final OCSPResp ocspResp = new OCSPResp(derEncoded);
final BasicOCSPResp basicOCSPResp = (BasicOCSPResp) ocspResp.getResponseObject();
return basicOCSPResp;
} catch (OCSPException e) {
throw new DSSException(e);
} catch (IOException e) {
throw new DSSException(e);
}
}
public static List<String> getPolicyIdentifiers(final X509Certificate cert) {
final byte[] certificatePolicies = cert.getExtensionValue(X509Extension.certificatePolicies.getId());
if (certificatePolicies == null) {
return Collections.emptyList();
}
ASN1Sequence seq = DSSASN1Utils.getAsn1SequenceFromDerOctetString(certificatePolicies);
final List<String> policyIdentifiers = new ArrayList<String>();
for (int ii = 0; ii < seq.size(); ii++) {
final PolicyInformation policyInfo = PolicyInformation.getInstance(seq.getObjectAt(ii));
// System.out.println("\t----> PolicyIdentifier: " + policyInfo.getPolicyIdentifier().getId());
policyIdentifiers.add(policyInfo.getPolicyIdentifier().getId());
}
return policyIdentifiers;
}
/**
* This method converts the {@code List} of {@code CertificateToken} to the {@code List} of {@code X509Certificate}.
*
* @param certTokens the list of {@code CertificateToken} to be converted
* @return a list for {@code X509Certificate} based on the input list
*/
public static List<X509Certificate> getX509Certificates(final List<CertificateToken> certTokens) {
final List<X509Certificate> certificateChain = new ArrayList<X509Certificate>();
for (final CertificateToken token : certTokens) {
certificateChain.add(token.getCertificate());
}
return certificateChain;
}
/**
* This method digests the given string with SHA1 algorithm and encode returned array of bytes as hex string.
*
* @param stringToDigest Everything in the name
* @return hex encoded digest value
*/
public static String getSHA1Digest(final String stringToDigest) {
final byte[] digest = sha1Digester.digest(stringToDigest.getBytes());
return encodeHexString(digest);
}
/**
* This method digests the given {@code InputStream} with SHA1 algorithm and encode returned array of bytes as hex string.
*
* @param inputStream
* @return
*/
public static String getSHA1Digest(final InputStream inputStream) {
final byte[] bytes = DSSUtils.toByteArray(inputStream);
final byte[] digest = sha1Digester.digest(bytes);
return encodeHexString(digest);
}
/**
* This method replaces in a string one pattern by another one without using regexp.
*
* @param string
* @param oldPattern
* @param newPattern
* @return
*/
public static StringBuffer replaceStrStr(final StringBuffer string, final String oldPattern, final String newPattern) {
if (string == null || oldPattern == null || oldPattern.equals("") || newPattern == null) {
return string;
}
final StringBuffer replaced = new StringBuffer();
int startIdx = 0;
int idxOld;
while ((idxOld = string.indexOf(oldPattern, startIdx)) >= 0) {
replaced.append(string.substring(startIdx, idxOld));
replaced.append(newPattern);
startIdx = idxOld + oldPattern.length();
}
replaced.append(string.substring(startIdx));
return replaced;
}
public static String replaceStrStr(final String string, final String oldPattern, final String newPattern) {
final StringBuffer stringBuffer = replaceStrStr(new StringBuffer(string), oldPattern, newPattern);
return stringBuffer.toString();
}
/**
* This method allows to digest the data with the given algorithm.
*
* @param digestAlgorithm the algorithm to use
* @param data the data to digest
* @return digested array of bytes
*/
public static byte[] digest(final DigestAlgorithm digestAlgorithm, final byte[] data) throws DSSException {
try {
final MessageDigest messageDigest = getMessageDigest(digestAlgorithm);
final byte[] digestValue = messageDigest.digest(data);
return digestValue;
} catch (NoSuchAlgorithmException e) {
throw new DSSException("Digest algorithm error: " + e.getMessage(), e);
}
}
/**
* @param digestAlgorithm
* @return
* @throws NoSuchAlgorithmException
*/
public static MessageDigest getMessageDigest(final DigestAlgorithm digestAlgorithm) throws NoSuchAlgorithmException {
// TODO-Bob (13/07/2014): To be checked if the default implementation copes with RIPEMD160
// if (digestAlgorithm.equals(DigestAlgorithm.RIPEMD160)) {
//
// final RIPEMD160Digest digest = new RIPEMD160Digest();
// final byte[] message = certificateToken.getEncoded();
// digest.update(message, 0, message.length);
// final byte[] digestValue = new byte[digest.getDigestSize()];
// digest.doFinal(digestValue, 0);
// recalculatedBase64DigestValue = DSSUtils.base64BinaryEncode(digestValue);
// } else {
final String digestAlgorithmOid = digestAlgorithm.getOid().getId();
// System.out.println(">>> " + digestAlgorithmOid);
final MessageDigest messageDigest = MessageDigest.getInstance(digestAlgorithmOid);
// System.out.println(">>> " + messageDigest.getProvider() + "/" + messageDigest.getClass().getName());
return messageDigest;
}
/**
* This method allows to digest the data in the {@code InputStream} with the given algorithm.
*
* @param digestAlgo the algorithm to use
* @param inputStream the data to digest
* @return digested array of bytes
*/
public static byte[] digest(final DigestAlgorithm digestAlgo, final InputStream inputStream) throws DSSException {
try {
final MessageDigest messageDigest = getMessageDigest(digestAlgo);
final byte[] buffer = new byte[4096];
int count = 0;
while ((count = inputStream.read(buffer)) > 0) {
messageDigest.update(buffer, 0, count);
}
final byte[] digestValue = messageDigest.digest();
return digestValue;
} catch (NoSuchAlgorithmException e) {
throw new DSSException("Digest algorithm error: " + e.getMessage(), e);
} catch (IOException e) {
throw new DSSException(e);
}
}
public static byte[] digest(DigestAlgorithm digestAlgorithm, byte[]... data) {
try {
final MessageDigest messageDigest = getMessageDigest(digestAlgorithm);
for (final byte[] bytes : data) {
messageDigest.update(bytes);
}
final byte[] digestValue = messageDigest.digest();
return digestValue;
} catch (NoSuchAlgorithmException e) {
throw new DSSException("Digest algorithm error: " + e.getMessage(), e);
}
}
/**
* This method digest and encrypt the given {@code InputStream} with indicated private key and signature algorithm. To find the signature object
* the list of registered security Providers, starting with the most preferred Provider is traversed.
* <p/>
* This method returns an array of bytes representing the signature value. Signature object that implements the specified signature algorithm. It traverses the list of
* registered security Providers, starting with the most preferred Provider. A new Signature object encapsulating the SignatureSpi implementation from the first Provider
* that supports the specified algorithm is returned. The {@code NoSuchAlgorithmException} exception is wrapped in a DSSException.
*
* @param javaSignatureAlgorithm signature algorithm under JAVA form.
* @param privateKey private key to use
* @param stream the data to digest
* @return digested and encrypted array of bytes
*/
public static byte[] encrypt(final String javaSignatureAlgorithm, final PrivateKey privateKey, final InputStream stream) {
try {
LOG.debug("Signature Algorithm: " + javaSignatureAlgorithm);
final Signature signature = Signature.getInstance(javaSignatureAlgorithm);
signature.initSign(privateKey);
final byte[] buffer = new byte[4096];
int count = 0;
while ((count = stream.read(buffer)) > 0) {
signature.update(buffer, 0, count);
}
final byte[] signatureValue = signature.sign();
return signatureValue;
} catch (SignatureException e) {
throw new DSSException(e);
} catch (InvalidKeyException e) {
throw new DSSException(e);
} catch (NoSuchAlgorithmException e) {
throw new DSSException(e);
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method digest and encrypt the given {@code InputStream} with indicated private key and signature algorithm. To find the signature object
* the list of registered security Providers, starting with the most preferred Provider is traversed.
* <p/>
* This method returns an array of bytes representing the signature value. Signature object that implements the specified signature algorithm. It traverses the list of
* registered security Providers, starting with the most preferred Provider. A new Signature object encapsulating the SignatureSpi implementation from the first Provider
* that supports the specified algorithm is returned. The {@code NoSuchAlgorithmException} exception is wrapped in a DSSException.
*
* @param javaSignatureAlgorithm signature algorithm under JAVA form.
* @param privateKey private key to use
* @param bytes the data to digest
* @return digested and encrypted array of bytes
*/
public static byte[] encrypt(final String javaSignatureAlgorithm, final PrivateKey privateKey, final byte[] bytes) {
try {
final Signature signature = Signature.getInstance(javaSignatureAlgorithm);
signature.initSign(privateKey);
signature.update(bytes);
final byte[] signatureValue = signature.sign();
return signatureValue;
} catch (SignatureException e) {
throw new DSSException(e);
} catch (InvalidKeyException e) {
throw new DSSException(e);
} catch (NoSuchAlgorithmException e) {
throw new DSSException(e);
}
}
/**
* Returns the {@code CertificateID} for the given certificate and its issuer's certificate.
*
* @param cert {@code X509Certificate} for which the id is created
* @param issuerCert {@code X509Certificate} issuer certificate of the {@code cert}
* @return {@code CertificateID}
* @throws org.bouncycastle.cert.ocsp.OCSPException
*/
public static CertificateID getOCSPCertificateID(final X509Certificate cert, final X509Certificate issuerCert) throws DSSException {
try {
final BigInteger serialNumber = cert.getSerialNumber();
final DigestCalculator digestCalculator = getSHA1DigestCalculator();
final X509CertificateHolder x509CertificateHolder = getX509CertificateHolder(issuerCert);
final CertificateID certificateID = new CertificateID(digestCalculator, x509CertificateHolder, serialNumber);
return certificateID;
} catch (OCSPException e) {
throw new DSSException(e);
}
}
/**
* Returns a {@code X509CertificateHolder} encapsulating the given {@code X509Certificate}.
*
* @param x509Certificate
* @return a X509CertificateHolder holding this certificate
*/
public static X509CertificateHolder getX509CertificateHolder(final X509Certificate x509Certificate) {
try {
return new X509CertificateHolder(x509Certificate.getEncoded());
} catch (IOException e) {
throw new DSSException(e);
} catch (CertificateEncodingException e) {
throw new DSSException(e);
}
}
/**
* Returns a {@code X509CertificateHolder} encapsulating the given {@code CertificateToken}.
*
* @param certificateToken
* @return a X509CertificateHolder holding this certificate
*/
public static X509CertificateHolder getX509CertificateHolder(final CertificateToken certificateToken) {
final X509CertificateHolder x509CertificateHolder = getX509CertificateHolder(certificateToken.getCertificate());
return x509CertificateHolder;
}
public static DigestCalculator getSHA1DigestCalculator() throws DSSException {
try {
// final ASN1ObjectIdentifier oid = DigestAlgorithm.SHA1.getOid();
// final DigestCalculator digestCalculator = digestCalculatorProvider.get(new AlgorithmIdentifier(oid));
final DigestCalculatorProvider digestCalculatorProvider = jcaDigestCalculatorProviderBuilder.build();
final DigestCalculator digestCalculator = digestCalculatorProvider.get(CertificateID.HASH_SHA1);
return digestCalculator;
} catch (OperatorCreationException e) {
throw new DSSException(e);
}
}
/**
* Returns the encoded (as ASN.1 DER) form of this X.509 certificate.
*
* @param cert certificate
* @return encoded array of bytes
*/
public static byte[] getEncoded(final X509Certificate cert) {
try {
byte[] encoded = cert.getEncoded();
return encoded;
} catch (CertificateEncodingException e) {
throw new DSSException(e);
}
}
/**
* Returns the encoded (as ASN.1 DER) form of this {@code TimeStampRequest}.
*
* @param timeStampRequest {@code TimeStampToken}
* @return encoded array of bytes
*/
public static byte[] getEncoded(final TimeStampRequest timeStampRequest) {
try {
return timeStampRequest.getEncoded();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* Returns the encoded (as ASN.1 DER) form of this {@code TimeStampToken}.
*
* @param timeStamp {@code TimeStampToken}
* @return encoded array of bytes
*/
public static byte[] getEncoded(final TimeStampToken timeStamp) {
try {
return timeStamp.getEncoded();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method opens the {@code URLConnection} using the given URL.
*
* @param url URL to be accessed
* @return {@code URLConnection}
*/
public static URLConnection openURLConnection(final String url) {
try {
final URL tspUrl = new URL(url);
return tspUrl.openConnection();
} catch (IOException e) {
throw new DSSException(e);
}
}
public static void writeToURLConnection(final URLConnection urlConnection, final byte[] bytes) throws DSSException {
try {
final OutputStream out = urlConnection.getOutputStream();
out.write(bytes);
out.close();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method returns an {@code InputStream} which needs to be closed, based on {@code FileInputStream}.
*
* @param filePath The path to the file to read
* @return an {@code InputStream} materialized by a {@code FileInputStream} representing the contents of the file
* @throws DSSException
*/
public static InputStream toInputStream(final String filePath) throws DSSException {
final File file = getFile(filePath);
final InputStream inputStream = toInputStream(file);
return inputStream;
}
/**
* This method returns an {@code InputStream} which needs to be closed, based on {@code FileInputStream}.
*
* @param file {@code File} to read.
* @return an {@code InputStream} materialized by a {@code FileInputStream} representing the contents of the file
* @throws DSSException
*/
public static InputStream toInputStream(final File file) throws DSSException {
if (file == null) {
throw new DSSNullException(File.class);
}
try {
final FileInputStream fileInputStream = openInputStream(file);
return fileInputStream;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method returns the {@code InputStream} which does not need to be closed, based on {@code ByteArrayInputStream}.
*
* @param bytes An array of {@code byte} to convert to {@code InputStream}
* @return the {@code InputStream} based on {@code ByteArrayInputStream}
*/
public static InputStream toInputStream(byte[] bytes) {
final InputStream inputStream = new ByteArrayInputStream(bytes);
return inputStream;
}
/**
* This method returns the {@code InputStream} based on the given {@code String} and char set. This stream does not need to be closed, it is based on {@code
* ByteArrayInputStream}.
*
* @param string {@code String} to convert
* @param charset char set to use
* @return the {@code InputStream} based on {@code ByteArrayInputStream}
*/
public static InputStream toInputStream(final String string, final String charset) throws DSSException {
try {
final InputStream inputStream = new ByteArrayInputStream(string.getBytes(charset));
return inputStream;
} catch (UnsupportedEncodingException e) {
throw new DSSException(e);
}
}
/**
* This method returns a {@code FileOutputStream} based on the provided path to the file.
*
* @param path to the file
* @return {@code FileOutputStream}
*/
public static FileOutputStream toFileOutputStream(final String path) throws DSSException {
try {
return new FileOutputStream(path);
} catch (FileNotFoundException e) {
throw new DSSException(e);
}
}
/**
* This method returns an {@code InputStream} which does not need to be closed, based on {@code ByteArrayInputStream}.
*
* @param file {@code File} to read
* @return {@code InputStream} based on {@code ByteArrayInputStream}
*/
public static InputStream toByteArrayInputStream(final File file) {
if (file == null) {
throw new DSSNullException(File.class);
}
try {
final byte[] bytes = readFileToByteArray(file);
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return byteArrayInputStream;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* This method returns the byte array representing the contents of the file.
*
* @param file {@code File} to read
* @return an array of {@code byte}
* @throws DSSException
*/
public static byte[] toByteArray(final File file) throws DSSException {
if (file == null) {
throw new DSSNullException(File.class);
}
try {
final byte[] bytes = readFileToByteArray(file);
return bytes;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* FROM: Apache
* <p/>
* Reads the contents of a file into a byte array.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
* @throws IOException in case of an I/O error
* @since Commons IO 1.1
*/
private static byte[] readFileToByteArray(final File file) throws IOException {
InputStream in = null;
try {
in = openInputStream(file);
return toByteArray_(in);
} finally {
closeQuietly(in);
}
}
/**
* FROM: Apache
* <p/>
* Opens a {@link java.io.FileInputStream} for the specified file, providing better
* error messages than simply calling {@code new FileInputStream(file)}.
* <p/>
* At the end of the method either the stream will be successfully opened,
* or an exception will have been thrown.
* <p/>
* An exception is thrown if the file does not exist.
* An exception is thrown if the file object exists but is a directory.
* An exception is thrown if the file exists but cannot be read.
*
* @param file the file to open for input, must not be {@code null}
* @return a new {@link java.io.FileInputStream} for the specified file
* @throws java.io.FileNotFoundException if the file does not exist
* @throws IOException if the file object is a directory
* @throws IOException if the file cannot be read
* @since Commons IO 1.3
*/
private static FileInputStream openInputStream(final File file) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (file.canRead() == false) {
throw new IOException("File '" + file + "' cannot be read");
}
} else {
throw new FileNotFoundException("File '" + file + "' does not exist");
}
return new FileInputStream(file);
}
/**
* Get the contents of an {@code InputStream} as a {@code byte[]}. The {@code inputStream} is closed.
*
* @param inputStream {@code InputStream} (cannot be {@code null})
* @return array of {@code byte}s
*/
public static byte[] toByteArray(final InputStream inputStream) {
if (inputStream == null) {
throw new DSSNullException(InputStream.class);
}
try {
final byte[] bytes = toByteArray_(inputStream);
return bytes;
} catch (IOException e) {
throw new DSSException(e);
} finally {
closeQuietly(inputStream);
}
}
/**
* This method Creates a {@code URL} object from the {@code String} representation..
*
* @param urlString {@code String} to parse as a URL
* @return {@code URL} or {@code null}
*/
public static URL toUrlQuietly(final String urlString) {
try {
return new URL(urlString);
} catch (MalformedURLException e) {
LOG.warn(e.toString(), e);
}
return null;
}
/**
* This method creates a {@code URI} object from the {@code String} representation.
*
* @param urlString {@code String} to parse as a URI
* @return {@code URI}
*/
public static URI toUri(final String urlString) {
try {
return new URI(urlString);
} catch (URISyntaxException e) {
throw new DSSException(e);
}
}
/**
* This method returns the content of a file referenced by its URL. All exceptions are caught.
*
* @param url {@code URL} representing a file
* @return array of {@code byte}s or null
*/
public static byte[] toByteArrayQuietly(final URL url) {
if (url == null) {
return null;
}
try {
final InputStream inputStream = url.openStream();
return DSSUtils.toByteArray(inputStream);
} catch (IOException e) {
LOG.warn(e.toString(), e);
}
return null;
}
/**
* FROM: Apache
* Get the contents of an {@code InputStream} as a {@code byte[]}.
* <p/>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedInputStream}.
*
* @param input the {@code InputStream} to read from
* @return the requested byte array
* @throws NullPointerException if the input is null
* @throws IOException if an I/O error occurs
*/
private static byte[] toByteArray_(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy(input, output);
return output.toByteArray();
}
public static byte[] toByteArray(final String string) {
return string.getBytes();
}
public static String toString(final byte[] bytes) {
if (bytes == null) {
throw new DSSNullException(byte[].class);
}
final String string = new String(bytes);
return string;
}
/**
* This method saves the given array of {@code byte} to the provided {@code File}.
*
* @param bytes to save
* @param file
* @throws DSSException
*/
public static void saveToFile(final byte[] bytes, final File file) throws DSSException {
file.getParentFile().mkdirs();
FileOutputStream fileOutputStream = null;
try {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
fileOutputStream = new FileOutputStream(file);
copy(byteArrayInputStream, fileOutputStream);
} catch (IOException e) {
throw new DSSException(e);
} finally {
closeQuietly(fileOutputStream);
}
}
/**
* This method saves the given {@code InputStream} to a file representing by the provided path. The {@code InputStream} is not closed.
*
* @param inputStream {@code InputStream} to save
* @param path the path to the file to be created
*/
public static void saveToFile(final InputStream inputStream, final String path) {
final FileOutputStream fileOutputStream = toFileOutputStream(path);
try {
copy(inputStream, fileOutputStream);
} finally {
closeQuietly(fileOutputStream);
}
}
/**
* @param certificate
* @return
*/
public static IssuerSerial getIssuerSerial(final X509Certificate certificate) {
final X500Name issuerX500Name = DSSUtils.getX509CertificateHolder(certificate).getIssuer();
final GeneralName generalName = new GeneralName(issuerX500Name);
final GeneralNames generalNames = new GeneralNames(generalName);
final BigInteger serialNumber = certificate.getSerialNumber();
final IssuerSerial issuerSerial = new IssuerSerial(generalNames, serialNumber);
return issuerSerial;
}
/**
* @param certificateToken
* @return
*/
public static IssuerSerial getIssuerSerial(final CertificateToken certificateToken) {
final IssuerSerial issuerSerial = getIssuerSerial(certificateToken.getCertificate());
return issuerSerial;
}
public static X509Certificate getCertificate(final X509CertificateHolder x509CertificateHolder) {
try {
final Certificate certificate = x509CertificateHolder.toASN1Structure();
final X509CertificateObject x509CertificateObject = new X509CertificateObject(certificate);
return x509CertificateObject;
} catch (CertificateParsingException e) {
throw new DSSException(e);
}
}
public static X509CRL toX509CRL(final X509CRLHolder x509CRLHolder) {
try {
final JcaX509CRLConverter jcaX509CRLConverter = new JcaX509CRLConverter();
final X509CRL x509CRL = jcaX509CRLConverter.getCRL(x509CRLHolder);
return x509CRL;
} catch (CRLException e) {
throw new DSSException(e);
}
}
public static byte[] getEncoded(X509CRL x509CRL) {
try {
final byte[] encoded = x509CRL.getEncoded();
return encoded;
} catch (CRLException e) {
throw new DSSException(e);
}
}
public static byte[] getEncoded(BasicOCSPResp basicOCSPResp) {
try {
final byte[] encoded = BasicOCSPResponse.getInstance(basicOCSPResp.getEncoded()).getEncoded(ASN1Encoding.DER);
return encoded;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* return a unique id for a date and the certificateToken id.
*
* @param signingTime
* @param id
* @return
*/
public static String getDeterministicId(final Date signingTime, final int id) {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("Z"));
Date signingTime_ = signingTime;
if (signingTime_ == null) {
signingTime_ = deterministicDate;
}
calendar.setTime(signingTime_);
final Date time = calendar.getTime();
final long milliseconds = time.getTime();
final long droppedMillis = 1000 * (milliseconds / 1000);
final byte[] timeBytes = Long.toString(droppedMillis).getBytes();
final ByteBuffer byteBuffer = ByteBuffer.allocate(4);
byteBuffer.putInt(id);
final byte[] certificateBytes = byteBuffer.array();
final byte[] digestValue = DSSUtils.digest(DigestAlgorithm.MD5, timeBytes, certificateBytes);
final String deterministicId = "id-" + toHex(digestValue);
return deterministicId;
}
public static Date getLocalDate(final Date gtmDate, final Date localDate) {
final Date newLocalDate = new Date(gtmDate.getTime() + TimeZone.getDefault().getOffset(localDate.getTime()));
return newLocalDate;
}
public static long toLong(final byte[] bytes) {
// Long.valueOf(new String(bytes)).longValue();
ByteBuffer buffer = ByteBuffer.allocate(8);
buffer.put(bytes, 0, Long.SIZE / 8);
// TODO: (Bob: 2014 Jan 22) To be checked if it is not platform dependent?
buffer.flip();//need flip
return buffer.getLong();
}
public static void delete(final File file) {
if (file != null) {
file.delete();
}
}
// Apache String Utils
/**
* <p>Checks if a String is empty ("") or null.</p>
* <p/>
* <pre>
* DSSUtils.isEmpty(null) = true
* DSSUtils.isEmpty("") = true
* DSSUtils.isEmpty(" ") = false
* DSSUtils.isEmpty("bob") = false
* DSSUtils.isEmpty(" bob ") = false
* </pre>
* <p/>
* <p>NOTE: This method changed in Lang version 2.0.
* It no longer trims the String.
* That functionality is available in isBlank().</p>
*
* @param str the String to check, may be null
* @return {@code true} if the String is empty or null
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
/**
* <p>Checks if a String is not empty ("") and not null.</p>
* <p/>
* <pre>
* DSSUtils.isNotEmpty(null) = false
* DSSUtils.isNotEmpty("") = false
* DSSUtils.isNotEmpty(" ") = true
* DSSUtils.isNotEmpty("bob") = true
* DSSUtils.isNotEmpty(" bob ") = true
* </pre>
*
* @param str the String to check, may be null
* @return {@code true} if the String is not empty and not null
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
* <p>Compares two Strings, returning {@code true} if they are equal.</p>
* <p/>
* <p>{@code null}s are handled without exceptions. Two {@code null}
* references are considered to be equal. The comparison is case sensitive.</p>
* <p/>
* <pre>
* DSSUtils.equals(null, null) = true
* DSSUtils.equals(null, "abc") = false
* DSSUtils.equals("abc", null) = false
* DSSUtils.equals("abc", "abc") = true
* DSSUtils.equals("abc", "ABC") = false
* </pre>
*
* @param str1 the first String, may be null
* @param str2 the second String, may be null
* @return {@code true} if the Strings are equal, case sensitive, or
* both {@code null}
* @see java.lang.String#equals(Object)
*/
public static boolean equals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
/**
* <p>Checks if a String is whitespace, empty ("") or null.</p>
* <p/>
* <pre>
* DSSUtils.isBlank(null) = true
* DSSUtils.isBlank("") = true
* DSSUtils.isBlank(" ") = true
* DSSUtils.isBlank("bob") = false
* DSSUtils.isBlank(" bob ") = false
* </pre>
*
* @param str the String to check, may be null
* @return {@code true} if the String is null, empty or whitespace
* @since 2.0
*/
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* <p>Checks if a String is not empty (""), not null and not whitespace only.</p>
* <p/>
* <pre>
* DSSUtils.isNotBlank(null) = false
* DSSUtils.isNotBlank("") = false
* DSSUtils.isNotBlank(" ") = false
* DSSUtils.isNotBlank("bob") = true
* DSSUtils.isNotBlank(" bob ") = true
* </pre>
*
* @param str the String to check, may be null
* @return {@code true} if the String is
* not empty and not null and not whitespace
* @since 2.0
*/
public static boolean isNotBlank(String str) {
return !isBlank(str);
}
// Apache Collection Utils
/**
* <p>Checks if the object is in the given array.</p>
* <p/>
* <p>The method returns {@code false} if a {@code null} array is passed in.</p>
*
* @param array the array to search through
* @param objectToFind the object to find
* @return {@code true} if the array contains the object
*/
public static boolean contains(Object[] array, Object objectToFind) {
return indexOf(array, objectToFind) != INDEX_NOT_FOUND;
}
/**
* <p>Finds the index of the given object in the array.</p>
* <p/>
* <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
*
* @param array the array to search through for the object, may be {@code null}
* @param objectToFind the object to find, may be {@code null}
* @return the index of the object within the array,
* {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
*/
public static int indexOf(Object[] array, Object objectToFind) {
return indexOf(array, objectToFind, 0);
}
/**
* <p>Finds the index of the given object in the array starting at the given index.</p>
* <p/>
* <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
* <p/>
* <p>A negative startIndex is treated as zero. A startIndex larger than the array
* length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
*
* @param array the array to search through for the object, may be {@code null}
* @param objectToFind the object to find, may be {@code null}
* @param startIndex the index to start searching at
* @return the index of the object within the array starting at the index,
* {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
*/
public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
if (array == null) {
return INDEX_NOT_FOUND;
}
if (startIndex < 0) {
startIndex = 0;
}
if (objectToFind == null) {
for (int i = startIndex; i < array.length; i++) {
if (array[i] == null) {
return i;
}
}
} else if (array.getClass().getComponentType().isInstance(objectToFind)) {
for (int i = startIndex; i < array.length; i++) {
if (objectToFind.equals(array[i])) {
return i;
}
}
}
return INDEX_NOT_FOUND;
}
/**
* Unconditionally close an {@code OutputStream}.
* <p/>
* Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored.
* This is typically used in finally blocks.
*
* @param output the OutputStream to close, may be null or already closed
*/
public static void closeQuietly(OutputStream output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
/**
* Unconditionally close an {@code InputStream}.
* <p/>
* Equivalent to {@link InputStream#close()}, except any exceptions will be ignored.
* This is typically used in finally blocks.
*
* @param input the InputStream to close, may be null or already closed
*/
public static void closeQuietly(final InputStream input) {
try {
if (input != null) {
input.close();
}
} catch (IOException ioe) {
// ignore
}
}
/**
* Unconditionally close an {@code Reader}.
* <p/>
* Equivalent to {@link Reader#close()}, except any exceptions will be ignored.
* This is typically used in finally blocks.
*
* @param input the Reader to close, may be null or already closed
*/
public static void closeQuietly(Reader input) {
try {
if (input != null) {
input.close();
}
} catch (IOException ioe) {
// ignore
}
}
/**
* Unconditionally close a {@code Writer}.
* <p/>
* Equivalent to {@link Writer#close()}, except any exceptions will be ignored.
* This is typically used in finally blocks.
*
* @param output the Writer to close, may be null or already closed
*/
public static void closeQuietly(Writer output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
/**
* Get the contents of an {@code InputStream} as a list of Strings,
* one entry per line, using the default character encoding of the platform.
* <p/>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedInputStream}.
*
* @param input the {@code InputStream} to read from, not null
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
* @throws DSSException if an I/O error occurs
* @since Commons IO 1.1
*/
public static List readLines(InputStream input) throws DSSException {
InputStreamReader reader = new InputStreamReader(input);
return readLines(reader);
}
/**
* Get the contents of a {@code Reader} as a list of Strings,
* one entry per line.
* <p/>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedReader}.
*
* @param input the {@code Reader} to read from, not null
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
* @throws DSSException if an I/O error occurs
* @since Commons IO 1.1
*/
public static List readLines(Reader input) throws DSSException {
try {
BufferedReader reader = new BufferedReader(input);
List list = new ArrayList();
String line = reader.readLine();
while (line != null) {
list.add(line);
line = reader.readLine();
}
return list;
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* <p>Joins the elements of the provided array into a single String
* containing the provided list of elements.</p>
* <p/>
* <p>No delimiter is added before or after the list.
* A {@code null} separator is the same as an empty String ("").
* Null objects or empty strings within the array are represented by
* empty strings.</p>
* <p/>
* <pre>
* DSSUtils.join(null, *) = null
* DSSUtils.join([], *) = ""
* DSSUtils.join([null], *) = ""
* DSSUtils.join(["a", "b", "c"], "--") = "a--b--c"
* DSSUtils.join(["a", "b", "c"], null) = "abc"
* DSSUtils.join(["a", "b", "c"], "") = "abc"
* DSSUtils.join([null, "", "a"], ',') = ",,a"
* </pre>
*
* @param array the array of values to join together, may be null
* @param separator the separator character to use, null treated as ""
* @return the joined String, {@code null} if null array input
*/
public static String join(Object[] array, String separator) {
if (array == null) {
return null;
}
return join(array, separator, 0, array.length);
}
/**
* <p>Joins the elements of the provided {@code Collection} into
* a single String containing the provided elements.</p>
* <p/>
* <p>No delimiter is added before or after the list.
* A {@code null} separator is the same as an empty String ("").</p>
* <p/>
* <p>See the examples here: {@link #join(Object[], String)}. </p>
*
* @param collection the {@code Collection} of values to join together, may be null
* @param separator the separator character to use, null treated as ""
* @return the joined String, {@code null} if null iterator input
* @since 2.3
*/
public static String join(Collection collection, String separator) {
if (collection == null) {
return null;
}
return join(collection.iterator(), separator);
}
/**
* <p>Joins the elements of the provided {@code Iterator} into
* a single String containing the provided elements.</p>
* <p/>
* <p>No delimiter is added before or after the list.
* A {@code null} separator is the same as an empty String ("").</p>
* <p/>
* <p>See the examples here: {@link #join(Object[], String)}. </p>
*
* @param iterator the {@code Iterator} of values to join together, may be null
* @param separator the separator character to use, null treated as ""
* @return the joined String, {@code null} if null iterator input
*/
public static String join(Iterator iterator, String separator) {
// handle null, zero and one elements before building a buffer
if (iterator == null) {
return null;
}
if (!iterator.hasNext()) {
return EMPTY;
}
Object first = iterator.next();
if (!iterator.hasNext()) {
return toString(first);
}
// two or more elements
StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
if (first != null) {
buf.append(first);
}
while (iterator.hasNext()) {
if (separator != null) {
buf.append(separator);
}
Object obj = iterator.next();
if (obj != null) {
buf.append(obj);
}
}
return buf.toString();
}
/**
* <p>Joins the elements of the provided array into a single String
* containing the provided list of elements.</p>
* <p/>
* <p>No delimiter is added before or after the list.
* A {@code null} separator is the same as an empty String ("").
* Null objects or empty strings within the array are represented by
* empty strings.</p>
* <p/>
* <pre>
* DSSUtils.join(null, *) = null
* DSSUtils.join([], *) = ""
* DSSUtils.join([null], *) = ""
* DSSUtils.join(["a", "b", "c"], "--") = "a--b--c"
* DSSUtils.join(["a", "b", "c"], null) = "abc"
* DSSUtils.join(["a", "b", "c"], "") = "abc"
* DSSUtils.join([null, "", "a"], ',') = ",,a"
* </pre>
*
* @param array the array of values to join together, may be null
* @param separator the separator character to use, null treated as ""
* @param startIndex the first index to start joining from. It is
* an error to pass in an end index past the end of the array
* @param endIndex the index to stop joining from (exclusive). It is
* an error to pass in an end index past the end of the array
* @return the joined String, {@code null} if null array input
*/
public static String join(Object[] array, String separator, int startIndex, int endIndex) {
if (array == null) {
return null;
}
if (separator == null) {
separator = EMPTY;
}
// endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator))
// (Assuming that all Strings are roughly equally long)
int bufSize = (endIndex - startIndex);
if (bufSize <= 0) {
return EMPTY;
}
bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + separator.length());
StringBuilder buf = new StringBuilder(bufSize);
for (int ii = startIndex; ii < endIndex; ii++) {
if (ii > startIndex) {
buf.append(separator);
}
if (array[ii] != null) {
buf.append(array[ii]);
}
}
return buf.toString();
}
/**
* <p>Gets the substring before the last occurrence of a separator.
* The separator is not returned.</p>
* <p/>
* <p>A {@code null} string input will return {@code null}.
* An empty ("") string input will return the empty string.
* An empty or {@code null} separator will return the input string.</p>
* <p/>
* <p>If nothing is found, the string input is returned.</p>
* <p/>
* <pre>
* DSSUtils.substringBeforeLast(null, *) = null
* DSSUtils.substringBeforeLast("", *) = ""
* DSSUtils.substringBeforeLast("abcba", "b") = "abc"
* DSSUtils.substringBeforeLast("abc", "c") = "ab"
* DSSUtils.substringBeforeLast("a", "a") = ""
* DSSUtils.substringBeforeLast("a", "z") = "a"
* DSSUtils.substringBeforeLast("a", null) = "a"
* DSSUtils.substringBeforeLast("a", "") = "a"
* </pre>
*
* @param str the String to get a substring from, may be null
* @param separator the String to search for, may be null
* @return the substring before the last occurrence of the separator,
* {@code null} if null String input
* @since 2.0
*/
public static String substringBeforeLast(String str, String separator) {
if (isEmpty(str) || isEmpty(separator)) {
return str;
}
int pos = str.lastIndexOf(separator);
if (pos == INDEX_NOT_FOUND) {
return str;
}
return str.substring(0, pos);
}
/**
* <p>Gets the substring after the last occurrence of a separator.
* The separator is not returned.</p>
* <p/>
* <p>A {@code null} string input will return {@code null}.
* An empty ("") string input will return the empty string.
* An empty or {@code null} separator will return the empty string if
* the input string is not {@code null}.</p>
* <p/>
* <p>If nothing is found, the empty string is returned.</p>
* <p/>
* <pre>
* DSSUtils.substringAfterLast(null, *) = null
* DSSUtils.substringAfterLast("", *) = ""
* DSSUtils.substringAfterLast(*, "") = ""
* DSSUtils.substringAfterLast(*, null) = ""
* DSSUtils.substringAfterLast("abc", "a") = "bc"
* DSSUtils.substringAfterLast("abcba", "b") = "a"
* DSSUtils.substringAfterLast("abc", "c") = ""
* DSSUtils.substringAfterLast("a", "a") = ""
* DSSUtils.substringAfterLast("a", "z") = ""
* </pre>
*
* @param str the String to get a substring from, may be null
* @param separator the String to search for, may be null
* @return the substring after the last occurrence of the separator,
* {@code null} if null String input
* @since 2.0
*/
public static String substringAfterLast(String str, String separator) {
if (isEmpty(str)) {
return str;
}
if (isEmpty(separator)) {
return EMPTY;
}
int pos = str.lastIndexOf(separator);
if (pos == INDEX_NOT_FOUND || pos == (str.length() - separator.length())) {
return EMPTY;
}
return str.substring(pos + separator.length());
}
/**
* <p>Repeat a String {@code repeat} times to form a
* new String.</p>
* <p/>
* <pre>
* DSSUtils.repeat(null, 2) = null
* DSSUtils.repeat("", 0) = ""
* DSSUtils.repeat("", 2) = ""
* DSSUtils.repeat("a", 3) = "aaa"
* DSSUtils.repeat("ab", 2) = "abab"
* DSSUtils.repeat("a", -2) = ""
* </pre>
*
* @param str the String to repeat, may be null
* @param repeat number of times to repeat str, negative treated as zero
* @return a new String consisting of the original String repeated,
* {@code null} if null String input
*/
public static String repeat(String str, int repeat) {
// Performance tuned for 2.0 (JDK1.4)
if (str == null) {
return null;
}
if (repeat <= 0) {
return EMPTY;
}
int inputLength = str.length();
if (repeat == 1 || inputLength == 0) {
return str;
}
if (inputLength == 1 && repeat <= PAD_LIMIT) {
return padding(repeat, str.charAt(0));
}
int outputLength = inputLength * repeat;
switch (inputLength) {
case 1:
char ch = str.charAt(0);
char[] output1 = new char[outputLength];
for (int i = repeat - 1; i >= 0; i--) {
output1[i] = ch;
}
return new String(output1);
case 2:
char ch0 = str.charAt(0);
char ch1 = str.charAt(1);
char[] output2 = new char[outputLength];
for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
output2[i] = ch0;
output2[i + 1] = ch1;
}
return new String(output2);
default:
StringBuilder buf = new StringBuilder(outputLength);
for (int i = 0; i < repeat; i++) {
buf.append(str);
}
return buf.toString();
}
}
/**
* <p>Returns padding using the specified delimiter repeated
* to a given length.</p>
* <p/>
* <pre>
* DSSUtils.padding(0, 'e') = ""
* DSSUtils.padding(3, 'e') = "eee"
* DSSUtils.padding(-2, 'e') = IndexOutOfBoundsException
* </pre>
* <p/>
* <p>Note: this method doesn't not support padding with
* <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a>
* as they require a pair of {@code char}s to be represented.
* If you are needing to support full I18N of your applications
* consider using {@link #repeat(String, int)} instead.
* </p>
*
* @param repeat number of times to repeat delim
* @param padChar character to repeat
* @return String with repeated character
* @throws DSSException if {@code repeat < 0}
* @see #repeat(String, int)
*/
private static String padding(int repeat, char padChar) throws DSSException {
if (repeat < 0) {
throw new DSSException("Cannot pad a negative amount: " + repeat);
}
final char[] buf = new char[repeat];
for (int i = 0; i < buf.length; i++) {
buf[i] = padChar;
}
return new String(buf);
}
/**
* <p>Gets the {@code toString} of an {@code Object} returning
* an empty string ("") if {@code null} input.</p>
* <p/>
* <pre>
* ObjectUtils.toString(null) = ""
* ObjectUtils.toString("") = ""
* ObjectUtils.toString("bat") = "bat"
* ObjectUtils.toString(Boolean.TRUE) = "true"
* </pre>
*
* @param obj the Object to {@code toString}, may be null
* @return the passed in Object's toString, or nullStr if {@code null} input
* @see String#valueOf(Object)
* @since 2.0
*/
public static String toString(Object obj) {
return obj == null ? "" : obj.toString();
}
/**
* This method returns the {@code X500Principal} corresponding to the given string or {@code null} if the conversion is not possible.
*
* @param x500PrincipalString a {@code String} representation of the {@code X500Principal}
* @return {@code X500Principal} or null
*/
public static X500Principal getX500PrincipalOrNull(final String x500PrincipalString) {
try {
final X500Principal x500Principal = new X500Principal(x500PrincipalString);
return x500Principal;
} catch (Exception e) {
LOG.warn(e.getMessage());
}
return null;
}
/**
* This method compares two {@code X500Principal}s. {@code X500Principal.CANONICAL} and {@code X500Principal.RFC2253} forms are compared. The {@code X500Principal}s are broken
* down into individual elements.
* TODO: (Bob: 2014 Feb 20) To be investigated why the standard equals does not work!?
*
* @param firstX500Principal
* @param secondX500Principal
* @return
*/
public static boolean equals(final X500Principal firstX500Principal, final X500Principal secondX500Principal) {
if (firstX500Principal == null || secondX500Principal == null) {
return false;
}
if (firstX500Principal.equals(secondX500Principal)) {
return true;
}
final HashMap<String, String> firstStringStringHashMap = get(firstX500Principal);
final HashMap<String, String> secondStringStringHashMap = get(secondX500Principal);
final boolean containsAll = firstStringStringHashMap.entrySet().containsAll(secondStringStringHashMap.entrySet());
return containsAll;
}
/**
* @param x500PrincipalString {@code String} representation of the {@code X500Principal}
* @return {@code X500Principal} built from a given {@code String}
*/
public static X500Principal getX500Principal(final String x500PrincipalString) throws DSSException {
try {
final X500Principal x500Principal = new X500Principal(x500PrincipalString);
final X500Principal normalizedX500Principal = getX500Principal(x500Principal);
return normalizedX500Principal;
} catch (IllegalArgumentException e) {
throw new DSSException(e);
}
}
/**
* This method transcodes {@code X500Principal} related to the subject name of a given {@code X509Certificate}.
*
* @param x509Certificate for which the subject name must be transcoded
* @return {@code X500Principal} transcoded
*/
public static X500Principal getSubjectX500Principal(final X509Certificate x509Certificate) {
final X500Principal x500Principal = x509Certificate.getSubjectX500Principal();
final X500Principal normalizedX500Principal = getX500Principal(x500Principal);
return normalizedX500Principal;
}
/**
* This method transcodes {@code X500Principal}.
*
* @param x500Principal to be transcoded
* @return {@code X500Principal} transcoded
*/
public static X500Principal getX500Principal(final X500Principal x500Principal) {
final String utf8Name = getUtf8String(x500Principal);
final X500Principal normalizedX500Principal = new X500Principal(utf8Name);
return normalizedX500Principal;
}
/**
* For a given {@code X509Certificate} this method returns its subject name as string representation of the X.500 distinguished name using the format defined in RFC 2253. The
* {@code X500Principal} is transcoded first.
*
* @param x509Certificate for which the string representation of the subject name must be returned
* @return {@code String} representation of the certificate's subject name using the format defined in RFC 2253
*/
public static String getSubjectX500PrincipalName(final X509Certificate x509Certificate) {
return getSubjectX500Principal(x509Certificate).getName();
}
/**
* This method transcodes {@code X500Principal} related to the issuer name of a given {@code X509Certificate}.
*
* @param x509Certificate for which the issuer subject name must be transcoded
* @return {@code X500Principal} transcoded
*/
public static X500Principal getIssuerX500Principal(final X509Certificate x509Certificate) {
final X500Principal x500Principal = x509Certificate.getIssuerX500Principal();
final X500Principal x500PrincipalNormalized = getX500Principal(x500Principal);
return x500PrincipalNormalized;
}
/**
* For a given {@code X509Certificate} this method returns its issuer name as string representation of the X.500 distinguished name using the format defined in RFC 2253. The
* {@code X500Principal} is transcoded first.
*
* @param x509Certificate for which the string representation of the issuer name must be returned
* @return {@code String} representation of the certificate's issuer name using the format defined in RFC 2253
*/
public static String getIssuerX500PrincipalName(final X509Certificate x509Certificate) {
return getIssuerX500Principal(x509Certificate).getName();
}
public static InputStream getResource(final String resourcePath) {
final InputStream resourceAsStream = DSSUtils.class.getClassLoader().getResourceAsStream(resourcePath);
return resourceAsStream;
}
/**
* This method returns an UTC date base on the year, the month and the day. The year must be encoded as 1978... and not 78
*
* @param year the value used to set the YEAR calendar field.
* @param month the month. Month value is 0-based. e.g., 0 for January.
* @param day the value used to set the DAY_OF_MONTH calendar field.
* @return the UTC date base on parameters
*/
public static Date getUtcDate(final int year, final int month, final int day) {
final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
calendar.set(year, month, day, 0, 0, 0);
final Date date = calendar.getTime();
return date;
}
/**
* This method adds or subtract the given number of days from the date
*
* @param date {@code Date} to change
* @param days number of days (can be negative)
* @return new {@code Date}
*/
public static Date getDate(final Date date, int days) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, days);
final Date newDate = calendar.getTime();
return newDate;
}
/**
* Constructs a new <code>String</code> by decoding the specified array of bytes using the UTF-8 charset.
*
* @param bytes The bytes to be decoded into characters
* @return A new <code>String</code> decoded from the specified array of bytes using the UTF-8 charset,
* or <code>null</code> if the input byte array was <code>null</code>.
* @throws IllegalStateException Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen since the
* charset is required.
*/
public static String getUtf8String(byte[] bytes) {
if (bytes == null) {
return null;
}
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new DSSException(e);
}
}
/**
* @param string
* @return
*/
public static String getUtf8String(final String string) {
return getUtf8String(string.getBytes());
}
public static byte[] getUtf8Bytes(final String string) {
if (string == null) {
return null;
}
try {
final byte[] bytes = string.getBytes("UTF-8");
return bytes;
} catch (UnsupportedEncodingException e) {
throw new DSSException(e);
}
}
private static HashMap<String, String> get(final X500Principal x500Principal) {
HashMap<String, String> treeMap = new HashMap<String, String>();
final byte[] encoded = x500Principal.getEncoded();
final ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(encoded);
final ASN1Encodable[] asn1Encodables = asn1Sequence.toArray();
for (final ASN1Encodable asn1Encodable : asn1Encodables) {
final DLSet dlSet = (DLSet) asn1Encodable;
for (int ii = 0; ii < dlSet.size(); ii++) {
final DLSequence dlSequence = (DLSequence) dlSet.getObjectAt(ii);
if (dlSequence.size() != 2) {
throw new DSSException("The DLSequence must contains exactly 2 elements.");
}
final ASN1Encodable asn1EncodableAttributeType = dlSequence.getObjectAt(0);
final String stringAttributeType = getString(asn1EncodableAttributeType);
final ASN1Encodable asn1EncodableAttributeValue = dlSequence.getObjectAt(1);
final String stringAttributeValue = getString(asn1EncodableAttributeValue);
treeMap.put(stringAttributeType, stringAttributeValue);
}
}
return treeMap;
}
private static String getUtf8String(final X500Principal x500Principal) {
final byte[] encoded = x500Principal.getEncoded();
final ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(encoded);
final ASN1Encodable[] asn1Encodables = asn1Sequence.toArray();
final StringBuilder stringBuilder = new StringBuilder();
/**
* RFC 4514 LDAP: Distinguished Names
* 2.1. Converting the RDNSequence
*
* If the RDNSequence is an empty sequence, the result is the empty or
* zero-length string.
*
* Otherwise, the output consists of the string encodings of each
* RelativeDistinguishedName in the RDNSequence (according to Section
* 2.2), starting with the last element of the sequence and moving
* backwards toward the first.
* ...
*/
for (int ii = asn1Encodables.length - 1; ii >= 0; ii--) {
final ASN1Encodable asn1Encodable = asn1Encodables[ii];
final DLSet dlSet = (DLSet) asn1Encodable;
for (int jj = 0; jj < dlSet.size(); jj++) {
final DLSequence dlSequence = (DLSequence) dlSet.getObjectAt(jj);
if (dlSequence.size() != 2) {
throw new DSSException("The DLSequence must contains exactly 2 elements.");
}
final ASN1Encodable attributeType = dlSequence.getObjectAt(0);
final ASN1Encodable attributeValue = dlSequence.getObjectAt(1);
String string = getString(attributeValue);
/**
* RFC 4514 LDAP: Distinguished Names
* ...
* Other characters may be escaped.
*
* Each octet of the character to be escaped is replaced by a backslash
* and two hex digits, which form a single octet in the code of the
* character. Alternatively, if and only if the character to be escaped
* is one of
*
* ' ', '"', '#', '+', ',', ';', '<', '=', '>', or '\'
* (U+0020, U+0022, U+0023, U+002B, U+002C, U+003B,
* U+003C, U+003D, U+003E, U+005C, respectively)
*
* it can be prefixed by a backslash ('\' U+005C).
* ...
*/
string = string.replace("\"", "\\\"");
string = string.replace("#", "\\#");
string = string.replace("+", "\\+");
string = string.replace(",", "\\,");
string = string.replace(";", "\\;");
string = string.replace("<", "\\<");
string = string.replace("=", "\\=");
string = string.replace(">", "\\>");
// System.out.println(">>> " + attributeType.toString() + "=" + attributeValue.getClass().getSimpleName() + "[" + string + "]");
if (stringBuilder.length() != 0) {
stringBuilder.append(',');
}
stringBuilder.append(attributeType).append('=').append(string);
}
}
//final X500Name x500Name = X500Name.getInstance(encoded);
return stringBuilder.toString();
}
private static String getString(ASN1Encodable attributeValue) {
String string;
if (attributeValue instanceof DERUTF8String) {
string = ((DERUTF8String) attributeValue).getString();
} else if (attributeValue instanceof DERPrintableString) {
string = ((DERPrintableString) attributeValue).getString();
} else if (attributeValue instanceof DERBMPString) {
string = ((DERBMPString) attributeValue).getString();
} else if (attributeValue instanceof DERT61String) {
string = ((DERT61String) attributeValue).getString();
} else if (attributeValue instanceof DERIA5String) {
string = ((DERIA5String) attributeValue).getString();
} else if (attributeValue instanceof ASN1ObjectIdentifier) {
string = ((ASN1ObjectIdentifier) attributeValue).getId();
} else if (attributeValue instanceof DERT61UTF8String) {
string = ((DERT61UTF8String) attributeValue).getString();
} else {
LOG.error("!!!*******!!! This encoding is unknown: " + attributeValue.getClass().getSimpleName());
string = attributeValue.toString();
LOG.error("!!!*******!!! value: " + string);
}
return string;
}
/**
* This method return the unique message id which can be used for translation purpose.
*
* @param message the {@code String} message on which the unique id is calculated.
* @return the unique id
*/
public static String getMessageId(final String message) {
final String message_ = message./*replace('\'', '_').*/toLowerCase().replaceAll("[^a-z_]", " ");
StringBuilder nameId = new StringBuilder();
final StringTokenizer stringTokenizer = new StringTokenizer(message_);
while (stringTokenizer.hasMoreElements()) {
final String word = (String) stringTokenizer.nextElement();
nameId.append(word.charAt(0));
}
final String nameIdString = nameId.toString();
return nameIdString.toUpperCase();
}
/**
* This method allows to convert the stack trace to a string.
*
* @param exception from which the stack trace should be extracted
* @return the exception's stack trace under the {@code String} form.
*/
public static String getStackTrace(final Exception exception) {
final StringWriter stringWriter = new StringWriter();
final PrintWriter printWriter = new PrintWriter(stringWriter);
exception.printStackTrace(printWriter);
return stringWriter.toString(); // stack trace as a string
}
/**
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* invocation of a method for this input stream. The next invocation
* might be the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
* <p/>
* <p> Note that while some implementations of {@code InputStream} will return
* the total number of bytes in the stream, many will not. It is
* never correct to use the return value of this method to allocate
* a buffer intended to hold all data in this stream.
* <p/>
* <p> A subclass' implementation of this method may choose to throw an
* {@link IOException} if this input stream has been closed by
* invoking the {@link InputStream#close()} method.
* <p/>
* <p> The {@code available} method for class {@code InputStream} always
* returns {@code 0}.
* <p/>
* <p> This method should be overridden by subclasses.
*
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking or {@code 0} when
* it reaches the end of the input stream.
* @throws DSSException if IOException occurs (if an I/O error occurs)
*/
public static int available(final InputStream is) throws DSSException {
try {
return is.available();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* Returns <tt>true</tt> if the first {@code elementNumber} elements of the two specified arrays are
* <i>equal</i>. Two arrays are considered equal if the first {@code elementNumber} corresponding pairs of elements in the two arrays are equal.
* Also, two array references are considered equal if both are <tt>null</tt>.<p>
*
* @param leftArray one array to be tested for equality
* @param rightArray the other array to be tested for equality
* @param elementNumber the number of elements from the beginning to be tested
* @return <tt>true</tt> if the two arrays are equal
*/
public static boolean equals(final byte[] leftArray, final byte[] rightArray, final int elementNumber) {
if (leftArray == rightArray) {
return true;
}
if (leftArray == null || rightArray == null) {
return false;
}
if (leftArray == null && rightArray == null) {
return true;
}
for (int ii = 0; ii < elementNumber; ii++) {
if (leftArray[ii] != rightArray[ii]) {
return false;
}
}
return true;
}
/**
* replaces e.g. "\xc3\xa9" with "é"
*
* @param s the input
* @return the output
*/
public static String unescapeMultiByteUtf8Literals(final String s) {
try {
final String q = new String(unescapePython(s.getBytes("UTF-8")), "UTF-8");
// if (!q.equals(s)) {
// LOG.log(Level.SEVERE, "multi byte utf literal found:\n" +
// " orig = " + s + "\n" +
// " escp = " + q
// );
// }
return q;
} catch (Exception e) {
// LOG.log(Level.SEVERE, "Could not unescape multi byte utf literal - will use original input: " + s, e);
return s;
}
}
private static byte[] unescapePython(final byte[] escaped) throws Exception {
// simple state machine iterates over the escaped bytes and converts
final byte[] unescaped = new byte[escaped.length];
int posTarget = 0;
for (int posSource = 0; posSource < escaped.length; posSource++) {
// if its not special then just move on
if (escaped[posSource] != '\\') {
unescaped[posTarget] = escaped[posSource];
posTarget++;
continue;
}
// if there is no next byte, throw incorrect encoding error
if (posSource + 1 >= escaped.length) {
throw new Exception("String incorrectly escaped, ends with escape character.");
}
// deal with hex first
if (escaped[posSource + 1] == 'x') {
// if there's no next byte, throw incorrect encoding error
if (posSource + 3 >= escaped.length) {
throw new Exception("String incorrectly escaped, ends early with incorrect hex encoding.");
}
unescaped[posTarget] = (byte) ((Character.digit(escaped[posSource + 2], 16) << 4) + Character.digit(escaped[posSource + 3], 16));
posTarget++;
posSource += 3;
}
// deal with n, then t, then r
else if (escaped[posSource + 1] == 'n') {
unescaped[posTarget] = '\n';
posTarget++;
posSource++;
} else if (escaped[posSource + 1] == 't') {
unescaped[posTarget] = '\t';
posTarget++;
posSource++;
} else if (escaped[posSource + 1] == 'r') {
unescaped[posTarget] = '\r';
posTarget++;
posSource++;
} else if (escaped[posSource + 1] == '\\') {
unescaped[posTarget] = escaped[posSource + 1];
posTarget++;
posSource++;
} else if (escaped[posSource + 1] == '\'') {
unescaped[posTarget] = escaped[posSource + 1];
posTarget++;
posSource++;
} else {
// invalid character
throw new Exception("String incorrectly escaped, invalid escaped character");
}
}
final byte[] result = new byte[posTarget];
System.arraycopy(unescaped, 0, result, 0, posTarget);
// return byte array, not string. Callers can convert to string.
return result;
}
public static void copyFile(final String path, final File sourceFile, final File destinationFile) throws IOException {
final File destinationPath = new File(path);
if (!destinationPath.exists()) {
destinationPath.mkdirs();
destinationFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destinationFile).getChannel();
destination.transferFrom(source, 0, source.size());
} finally {
if (source != null) {
source.close();
}
if (destination != null) {
destination.close();
}
}
}
public static byte[] toByteArray(final long longValue) {
return String.valueOf(longValue).getBytes();
}
/**
* @param x509Certificate
* @return
*/
public static List<String> getQCStatementsIdList(final X509Certificate x509Certificate) {
final List<String> extensionIdList = new ArrayList<String>();
final byte[] qcStatement = x509Certificate.getExtensionValue(Extension.qCStatements.getId());
if (qcStatement != null) {
final ASN1Sequence seq = DSSASN1Utils.getAsn1SequenceFromDerOctetString(qcStatement);
// Sequence of QCStatement
for (int ii = 0; ii < seq.size(); ii++) {
final QCStatement statement = QCStatement.getInstance(seq.getObjectAt(ii));
extensionIdList.add(statement.getStatementId().getId());
}
}
return extensionIdList;
}
/**
* This method closes the given {@code OutputStream} and throws a {@code DSSException} when the operation fails.
*
* @param outputStream {@code OutputStream} to be closed
*/
public static void close(final OutputStream outputStream) {
try {
outputStream.close();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* Returns the file extension based on the position of the '.' in the path. The paths as "xxx.y/toto" are not handled.
*
* @param path to be analysed
* @return the file extension or null
*/
public static String getFileExtension(final String path) {
String extension = null;
int lastIndexOf = path.lastIndexOf('.');
if (lastIndexOf > 0) {
extension = path.substring(lastIndexOf + 1);
}
return extension;
}
/**
* This method lists all defined security providers.
*/
public static void printSecurityProvides() {
final Provider[] providers = Security.getProviders();
for (final Provider provider : providers) {
System.out.println("PROVIDER: " + provider.getName());
final Set<Provider.Service> services = provider.getServices();
for (final Provider.Service service : services) {
System.out.println("\tALGORITHM: " + service.getAlgorithm() + " / " + service.getType() + " / " + service.getClassName());
}
}
}
/**
* This method returns the summary of the given exception. The analysis of the stack trace stops when the provided class is found.
*
* @param exception {@code Exception} to summarize
* @param javaClass {@code Class}
* @return {@code String} containing the summary message
*/
public static String getSummaryMessage(final Exception exception, final Class<?> javaClass) {
final String javaClassName = javaClass.getName();
final StackTraceElement[] stackTrace = exception.getStackTrace();
String message = "See log file for full stack trace.\n";
message += exception.toString() + '\n';
for (int ii = 0; ii < stackTrace.length; ii++) {
final String className = stackTrace[ii].getClassName();
if (className.equals(javaClassName)) {
message += stackTrace[ii].toString() + '\n';
break;
}
message += stackTrace[ii].toString() + '\n';
}
return message;
}
/**
* Reads maximum {@code headerLength} bytes from {@code dssDocument} to the given {@code byte} array.
*
* @param dssDocument {@code DSSDocument} to read
* @param headerLength {@code int}: maximum number of bytes to read
* @param destinationByteArray destination {@code byte} array
* @return
*/
public static int readToArray(final DSSDocument dssDocument, final int headerLength, final byte[] destinationByteArray) {
final InputStream inputStream = dssDocument.openStream();
try {
int read = inputStream.read(destinationByteArray, 0, headerLength);
return read;
} catch (IOException e) {
throw new DSSException(e);
} finally {
closeQuietly(inputStream);
}
}
/**
* Gets a difference between two dates
*
* @param date1 the oldest date
* @param date2 the newest date
* @param timeUnit the unit in which you want the diff
* @return the difference value, in the provided {@code TimeUnit}
*/
public static long getDateDiff(final Date date1, final Date date2, final TimeUnit timeUnit) {
long diff = date2.getTime() - date1.getTime();
return timeUnit.convert(diff, TimeUnit.MILLISECONDS);
}
/**
* This method returns an encoded representation of the {@code X509CertificateHolder}.
*
* @param x509CertificateHolder {@code X509CertificateHolder} to be encoded
* @return array of {@code byte}s
*/
public static byte[] getEncoded(final X509CertificateHolder x509CertificateHolder) {
try {
return x509CertificateHolder.getEncoded();
} catch (IOException e) {
throw new DSSException(e);
}
}
/**
* Null-safe check if the specified collection is empty.
* <p/>
* Null returns true.
*
* @param collection the collection to check, may be null
* @return true if empty or null
*/
public static boolean isEmpty(final Collection collection) {
return collection == null || collection.isEmpty();
}
public static boolean isNotEmpty(final Collection collection) {
return !isEmpty(collection);
}
/**
* Concatenates all the arrays into a new array. The new array contains all of the element of each array followed by all of the elements of the next array. When an array is
* returned, it is always a new array.
*
* @param arrays {@code byte} arrays to concatenate
* @return the new {@code byte} array
*/
public static byte[] concatenate(byte[]... arrays) {
if (arrays == null || arrays.length == 0 || (arrays.length == 1 && arrays[0] == null)) {
return null;
}
if (arrays.length == 1) {
return arrays[0].clone();
}
int joinedLength = 0;
for (final byte[] array : arrays) {
if (array != null) {
joinedLength += array.length;
}
}
byte[] joinedArray = new byte[joinedLength];
int destinationIndex = 0;
for (final byte[] array : arrays) {
if (array != null) {
System.arraycopy(array, 0, joinedArray, destinationIndex, array.length);
destinationIndex += array.length;
}
}
return joinedArray;
}
/**
* Creates a new instance of {@code TimeStampResponse} based on an array of bytes. The underlying exceptions are encapsulated into a {@code DSSException}.
*
* @param respBytes array of bytes
* @return {@code TimeStampResponse}
*/
public static TimeStampResponse newTimeStampResponse(final byte[] respBytes) {
try {
return new TimeStampResponse(respBytes);
} catch (TSPException e) {
throw new DSSException(e);
} catch (IOException e) {
throw new DSSException(e);
}
}
}