se.idsec.signservice.security.sign.pdf.configuration.PDFAlgorithmRegistry Maven / Gradle / Ivy
/*
* Copyright 2019-2024 IDsec Solutions AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package se.idsec.signservice.security.sign.pdf.configuration;
import org.apache.xml.security.signature.XMLSignature;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import se.swedenconnect.security.algorithms.Algorithm;
import se.swedenconnect.security.algorithms.AlgorithmPredicates;
import se.swedenconnect.security.algorithms.AlgorithmRegistry;
import se.swedenconnect.security.algorithms.MessageDigestAlgorithm;
import se.swedenconnect.security.algorithms.SignatureAlgorithm;
import se.swedenconnect.security.algorithms.impl.AlgorithmRegistryImpl;
import se.swedenconnect.security.algorithms.impl.StaticAlgorithmRegistry;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Registry for supported algorithms. This class adds support for the minimum supported set of algorithms and allows new
* algorithms to be added. By default, only RSA and ECDSA with SHA 245, 384 and 512 are supported.
*
* @author Martin Lindström ([email protected])
* @author Stefan Santesson ([email protected])
*/
public class PDFAlgorithmRegistry {
/** Mapping of algorithm IDs and signature properties. */
private static final AlgorithmRegistry algorithmRegistry;
static {
final Algorithm[] signatureAlgs = StaticAlgorithmRegistry.getDefaultSignatureAlgorithms();
final Function getAlgo = (s) -> Arrays.stream(signatureAlgs)
.filter(a -> Objects.equals(s, a.getUri()))
.findFirst()
.orElseThrow(SecurityException::new);
final AlgorithmRegistryImpl _algorithmRegistry = new AlgorithmRegistryImpl();
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384_MGF1));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA3_256_MGF1));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA3_384_MGF1));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA3_512_MGF1));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA256));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA384));
_algorithmRegistry.register(getAlgo.apply(XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA512));
algorithmRegistry = _algorithmRegistry;
}
/**
* Test if a particular algorithm is supported.
*
* @param algorithm algorithm to test
* @return true if supported, and false otherwise
*/
public static boolean isAlgoSupported(final String algorithm) {
return algorithmRegistry.getAlgorithm(algorithm) != null;
}
/**
* Get the URI identifier for a registered signature algorithm specified by an ASN.1 algorithm identifier.
*
* @param algorithmIdentifier the ASN.1 algorithm identifier
* @return URI identifier for the specified signature algorithm
* @throws NoSuchAlgorithmException if the specified algorithm is not supported or has illegal parameters
*/
public static String getAlgorithmURI(final AlgorithmIdentifier algorithmIdentifier) throws NoSuchAlgorithmException {
final SignatureAlgorithm algorithm = algorithmRegistry.getAlgorithm(
AlgorithmPredicates.fromAlgorithmIdentifierRelaxed(algorithmIdentifier), SignatureAlgorithm.class);
return Optional.ofNullable(algorithm)
.map(SignatureAlgorithm::getUri)
.orElseThrow(() -> new NoSuchAlgorithmException("Non supported signature algorithm"));
}
/**
* Get the URI identifier for a registered signature algorithm based on signature algorithm identifier and hash
* algorithm identifier.
*
* @param sigAlgoOid signature algorithm object identifier
* @param digestAlgoOid hash algorithm object identifier
* @return URI identifier for the combined signature algorithm
* @throws NoSuchAlgorithmException if the OID combinations are not supported
*/
public static String getAlgorithmURI(final ASN1ObjectIdentifier sigAlgoOid, final ASN1ObjectIdentifier digestAlgoOid)
throws NoSuchAlgorithmException {
final SignatureAlgorithm algorithm =
algorithmRegistry.getAlgorithm(fromOids(sigAlgoOid, digestAlgoOid), SignatureAlgorithm.class);
return Optional.ofNullable(algorithm)
.map(SignatureAlgorithm::getUri)
.orElseThrow(
() -> new NoSuchAlgorithmException("Non supported combination of signature algorithm and hash algorithm"));
}
/**
* Predicate to check if a signature method in the registry matches based on signature algorithm identifier and hash
* algorithm identifier.
*
* @param sigAlgoOid signature algorithm object identifier
* @param digestAlgoOid hash algorithm object identifier
* @return a predicate
*/
private static Predicate fromOids(final ASN1ObjectIdentifier sigAlgoOid,
final ASN1ObjectIdentifier digestAlgoOid) {
return (a) -> {
if (!(a instanceof final SignatureAlgorithm signatureAlgorithm)) {
return false;
}
return isSigAlgoEquivalent(signatureAlgorithm.getAlgorithmIdentifier().getAlgorithm(), sigAlgoOid, digestAlgoOid)
&& signatureAlgorithm.getMessageDigestAlgorithm().getAlgorithmIdentifier().getAlgorithm()
.equals(digestAlgoOid);
};
}
/**
* This method is designed to allow identifiers for RSA encryption to be equivalent to identifiers for various RSA
* combined with various hash functions
*
* @param signatureAlgorithmPropertyOid signature algorithm OID registered in the algorithm properties in this
* registry for the signature algorithm
* @param cmsSignatureAlgorithmOid CMS signature algorithm OID matched with the registered signature algorithm
* @param cmsDigestAlgorithmOid CMS digest algorithm used with this signature algorithm
* @return true if the CMS algorithms are equivalent with the registered signature algorithm OID
*/
private static boolean isSigAlgoEquivalent(final ASN1ObjectIdentifier signatureAlgorithmPropertyOid,
final ASN1ObjectIdentifier cmsSignatureAlgorithmOid, final ASN1ObjectIdentifier cmsDigestAlgorithmOid) {
// Allow RSA encryption identifier in place of explicit identifier for hash and public key algo
if (cmsSignatureAlgorithmOid.equals(PKCSObjectIdentifiers.rsaEncryption)) {
if (cmsDigestAlgorithmOid.equals(NISTObjectIdentifiers.id_sha224)
&& signatureAlgorithmPropertyOid.equals(PKCSObjectIdentifiers.sha224WithRSAEncryption)) {
return true;
}
if (cmsDigestAlgorithmOid.equals(NISTObjectIdentifiers.id_sha256)
&& signatureAlgorithmPropertyOid.equals(PKCSObjectIdentifiers.sha256WithRSAEncryption)) {
return true;
}
if (cmsDigestAlgorithmOid.equals(NISTObjectIdentifiers.id_sha384)
&& signatureAlgorithmPropertyOid.equals(PKCSObjectIdentifiers.sha384WithRSAEncryption)) {
return true;
}
if (cmsDigestAlgorithmOid.equals(NISTObjectIdentifiers.id_sha512)
&& signatureAlgorithmPropertyOid.equals(PKCSObjectIdentifiers.sha512WithRSAEncryption)) {
return true;
}
}
// If not then just compare OID:s
return signatureAlgorithmPropertyOid.equals(cmsSignatureAlgorithmOid);
}
/**
* Returns the algorithm parameters for a supported signature algorithm.
*
* @param algorithm signature algorithm
* @return algorithm properties
* @throws NoSuchAlgorithmException if the algorithm is not supported
*/
public static SignatureAlgorithm getAlgorithmProperties(final String algorithm) throws NoSuchAlgorithmException {
return Optional.ofNullable(algorithmRegistry.getAlgorithm(algorithm, SignatureAlgorithm.class))
.orElseThrow(() -> new NoSuchAlgorithmException("Unsupported Algorithm " + algorithm));
}
/**
* Get an instance of the message digest associated with the specified signature algorithm.
*
* @param algorithm algorithm URI identifier for signature algorithm
* @return s MessageDigest instance
* @throws NoSuchAlgorithmException if specified signature algorithm is not supported
*/
public static MessageDigest getMessageDigestInstance(final String algorithm) throws NoSuchAlgorithmException {
final String jcaName = Optional.ofNullable(algorithmRegistry.getAlgorithm(algorithm, SignatureAlgorithm.class))
.map(SignatureAlgorithm::getMessageDigestAlgorithm)
.map(MessageDigestAlgorithm::getJcaName)
.orElseThrow(() -> new NoSuchAlgorithmException("Unsupported Signature Algorithm " + algorithm));
return MessageDigest.getInstance(jcaName);
}
/**
* Get the algorithm name for the digest algorithm of the signature algorithm.
*
* @param algorithm algorithm URI identifier
* @return the name of the digest algorithm used to create instances of the digest algorithm
* @throws NoSuchAlgorithmException if the algorithm is not supported
*/
public static String getDigestName(final String algorithm) throws NoSuchAlgorithmException {
return Optional.ofNullable(algorithmRegistry.getAlgorithm(algorithm, SignatureAlgorithm.class))
.map(SignatureAlgorithm::getMessageDigestAlgorithm)
.map(MessageDigestAlgorithm::getJcaName)
.orElseThrow(() -> new NoSuchAlgorithmException("No supported digest algorithm for " + algorithm));
}
/**
* Get the algorithm name for the signature algorithm.
*
* @param algorithm algorithm URI identifier
* @return the name of the signature algorithm used to initiate the use of this algorithm in CMS signing
* @throws NoSuchAlgorithmException if the algorithm is not supported
*/
public static String getSigAlgoName(final String algorithm) throws NoSuchAlgorithmException {
return Optional.ofNullable(algorithmRegistry.getAlgorithm(algorithm))
.map(Algorithm::getJcaName)
.orElseThrow(() -> new NoSuchAlgorithmException("Unsupported algorithm: " + algorithm));
}
/**
* Register a new supported signature algorithm.
*
* @param signatureAlgorithm the signature algorithm to register
*/
public static void registerSupportedAlgorithm(final SignatureAlgorithm signatureAlgorithm) {
if (signatureAlgorithm == null) {
throw new IllegalArgumentException("signatureAlgorithm must not be null");
}
((AlgorithmRegistryImpl) algorithmRegistry).register(signatureAlgorithm);
}
/**
* Retrieve the algorithm family for a specific algorithm.
*
* @param algorithm the algorithm
* @return the algorithm type
* @throws IllegalArgumentException if the requested algorithm is not supported
*/
public static String getAlgoFamilyFromAlgo(final String algorithm) throws IllegalArgumentException {
return Optional.ofNullable(algorithmRegistry.getAlgorithm(algorithm, SignatureAlgorithm.class))
.map(SignatureAlgorithm::getKeyType)
.orElseThrow(() -> new IllegalArgumentException("No such algorithm"));
}
/**
* Private constructor preventing this class from being instantiated
*/
private PDFAlgorithmRegistry() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy