From 588f7edc0438d5f2f044ce6e63b77dadde7271aa Mon Sep 17 00:00:00 2001 From: Lonzak Date: Thu, 28 Mar 2024 18:20:15 +0100 Subject: [PATCH] Fix for invalid ecdsa oids for signatures (#1105) * fix for https://stackoverflow.com/questions/72470601/wrong-ecdsa-oid-in-itext7 * Fixed Codacy issues --- .../com/lowagie/text/ExceptionConverter.java | 25 ++- .../java/com/lowagie/text/pdf/PdfPKCS7.java | 156 ++++++++++++++---- 2 files changed, 137 insertions(+), 44 deletions(-) diff --git a/openpdf/src/main/java/com/lowagie/text/ExceptionConverter.java b/openpdf/src/main/java/com/lowagie/text/ExceptionConverter.java index 2c7092639..0f7d5270f 100644 --- a/openpdf/src/main/java/com/lowagie/text/ExceptionConverter.java +++ b/openpdf/src/main/java/com/lowagie/text/ExceptionConverter.java @@ -89,7 +89,7 @@ public class ExceptionConverter extends RuntimeException { */ public ExceptionConverter(Exception ex) { this.ex = ex; - prefix = (ex instanceof RuntimeException) ? "" : "ExceptionConverter: "; + this.prefix = (ex instanceof RuntimeException) ? "" : "ExceptionConverter: "; } /** @@ -113,7 +113,7 @@ public static final RuntimeException convertException(Exception ex) { * @return the original exception */ public Exception getException() { - return ex; + return this.ex; } /** @@ -121,8 +121,9 @@ public Exception getException() { * * @return message of the original exception */ + @Override public String getMessage() { - return ex.getMessage(); + return this.ex.getMessage(); } /** @@ -130,8 +131,9 @@ public String getMessage() { * * @return localized version of the message */ + @Override public String getLocalizedMessage() { - return ex.getLocalizedMessage(); + return this.ex.getLocalizedMessage(); } /** @@ -139,13 +141,15 @@ public String getLocalizedMessage() { * * @return String version of the exception */ + @Override public String toString() { - return prefix + ex; + return this.prefix + this.ex; } /** * we have to override this as well */ + @Override public void printStackTrace() { printStackTrace(System.err); } @@ -155,10 +159,11 @@ public void printStackTrace() { * * @param printStream printStream */ + @Override public void printStackTrace(java.io.PrintStream printStream) { synchronized (printStream) { - printStream.print(prefix); - ex.printStackTrace(printStream); + printStream.print(this.prefix); + this.ex.printStackTrace(printStream); } } @@ -167,10 +172,11 @@ public void printStackTrace(java.io.PrintStream printStream) { * * @param printWriter printWriter */ + @Override public void printStackTrace(java.io.PrintWriter printWriter) { synchronized (printWriter) { - printWriter.print(prefix); - ex.printStackTrace(printWriter); + printWriter.print(this.prefix); + this.ex.printStackTrace(printWriter); } } @@ -180,6 +186,7 @@ public void printStackTrace(java.io.PrintWriter printWriter) { * * @return a Throwable */ + @Override public synchronized Throwable fillInStackTrace() { return this; } diff --git a/openpdf/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java b/openpdf/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java index bc9afe6da..eb1c47701 100755 --- a/openpdf/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java +++ b/openpdf/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java @@ -109,12 +109,14 @@ import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.tsp.MessageImprint; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.cert.ocsp.BasicOCSPResp; import org.bouncycastle.cert.ocsp.CertificateID; import org.bouncycastle.cert.ocsp.SingleResp; import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; import org.bouncycastle.jce.provider.X509CRLParser; +import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; import org.bouncycastle.operator.DigestCalculatorProvider; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.tsp.TimeStampToken; @@ -139,6 +141,10 @@ public class PdfPKCS7 { private static final Map digestNames = new HashMap<>(); private static final Map algorithmNames = new HashMap<>(); private static final Map allowedDigests = new HashMap<>(); + //for correct OID determination + private static final HashMap rsaOids = new HashMap<>(); + private static final HashMap dsaOids = new HashMap<>(); + private static final HashMap ecdsaOids = new HashMap<>(); static { digestNames.put("1.2.840.113549.2.5", "MD5"); @@ -182,13 +188,15 @@ public class PdfPKCS7 { algorithmNames.put("1.3.36.3.3.1.3", "RSA"); algorithmNames.put("1.3.36.3.3.1.2", "RSA"); algorithmNames.put("1.3.36.3.3.1.4", "RSA"); - algorithmNames.put(ID_ECDSA, "ECDSA"); - algorithmNames.put("1.2.840.10045.4.1", "ECDSA"); - algorithmNames.put("1.2.840.10045.4.3", "ECDSA"); - algorithmNames.put("1.2.840.10045.4.3.2", "ECDSA"); - algorithmNames.put("1.2.840.10045.4.3.3", "ECDSA"); - algorithmNames.put("1.2.840.10045.4.3.4", "ECDSA"); + algorithmNames.put("1.2.840.10045.2.1", "ECDSA"); + algorithmNames.put("1.2.840.10045.4.1", "ECDSA"); //SHA1 + algorithmNames.put("1.2.840.10045.4.3", "ECDSA"); //SHA2 + algorithmNames.put("1.2.840.10045.4.3.1", "ECDSA"); //SHA224 + algorithmNames.put("1.2.840.10045.4.3.2", "ECDSA"); //SHA256 + algorithmNames.put("1.2.840.10045.4.3.3", "ECDSA"); //SHA384 + algorithmNames.put("1.2.840.10045.4.3.4", "ECDSA"); //SHA512 algorithmNames.put("1.2.840.113549.1.1.10", "RSAandMGF1"); + allowedDigests.put("MD5", "1.2.840.113549.2.5"); allowedDigests.put("MD2", "1.2.840.113549.2.2"); allowedDigests.put("SHA1", "1.3.14.3.2.26"); @@ -209,6 +217,50 @@ public class PdfPKCS7 { allowedDigests.put("RIPEMD-160", "1.3.36.3.2.1"); allowedDigests.put("RIPEMD256", "1.3.36.3.2.3"); allowedDigests.put("RIPEMD-256", "1.3.36.3.2.3"); + + //all oids according to http://oidref.com/1.2.840.113549.1.1 + rsaOids.put("SHA256", "1.2.840.113549.1.1.11"); + //add a 2nd time with "-" due to different crypto providers (sun vs bc) + rsaOids.put("SHA-256", "1.2.840.113549.1.1.11"); + rsaOids.put("SHA384", "1.2.840.113549.1.1.12"); + rsaOids.put("SHA-384", "1.2.840.113549.1.1.12"); + rsaOids.put("SHA512", "1.2.840.113549.1.1.13"); + rsaOids.put("SHA-512", "1.2.840.113549.1.1.13"); + rsaOids.put("SHA224", "1.2.840.113549.1.1.14"); + rsaOids.put("SHA-224", "1.2.840.113549.1.1.14"); + rsaOids.put("SHA3-224", "2.16.840.1.101.3.4.3.13"); + rsaOids.put("SHA3-256", "2.16.840.1.101.3.4.3.14"); + rsaOids.put("SHA3-384", "2.16.840.1.101.3.4.3.15"); + rsaOids.put("SHA3-512", "2.16.840.1.101.3.4.3.16"); + + dsaOids.put("SHA224", "2.16.840.1.101.3.4.3.1"); + dsaOids.put("SHA-224", "2.16.840.1.101.3.4.3.1"); + dsaOids.put("SHA256", "2.16.840.1.101.3.4.3.2"); + dsaOids.put("SHA-256", "2.16.840.1.101.3.4.3.2"); + //https://oid-rep.orange-labs.fr/get/2.16.840.1.101.3.4.3.3 + dsaOids.put("SHA384", "2.16.840.1.101.3.4.3.3"); + dsaOids.put("SHA-384", "2.16.840.1.101.3.4.3.3"); + dsaOids.put("SHA512", "2.16.840.1.101.3.4.3.4"); + dsaOids.put("SHA-512", "2.16.840.1.101.3.4.3.4"); + //https://javadoc.io/doc/org.bouncycastle/bcprov-ext-jdk15on/1.55/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.html + dsaOids.put("SHA3-224", "2.16.840.1.101.3.4.3.5"); + dsaOids.put("SHA3-256", "2.16.840.1.101.3.4.3.6"); + dsaOids.put("SHA3-384", "2.16.840.1.101.3.4.3.7"); + dsaOids.put("SHA3-512", "2.16.840.1.101.3.4.3.8"); + + ecdsaOids.put("SHA1", "1.2.840.10045.4.1"); + ecdsaOids.put("SHA224", "1.2.840.10045.4.3.1"); + ecdsaOids.put("SHA-224", "1.2.840.10045.4.3.1"); + ecdsaOids.put("SHA256", "1.2.840.10045.4.3.2"); + ecdsaOids.put("SHA-256", "1.2.840.10045.4.3.2"); + ecdsaOids.put("SHA384", "1.2.840.10045.4.3.3"); + ecdsaOids.put("SHA-384", "1.2.840.10045.4.3.3"); + ecdsaOids.put("SHA512", "1.2.840.10045.4.3.4"); + ecdsaOids.put("SHA-512", "1.2.840.10045.4.3.4"); + ecdsaOids.put("SHA3-224", "2.16.840.1.101.3.4.3.9"); + ecdsaOids.put("SHA3-256", "2.16.840.1.101.3.4.3.10"); + ecdsaOids.put("SHA3-384", "2.16.840.1.101.3.4.3.11"); + ecdsaOids.put("SHA3-512", "2.16.840.1.101.3.4.3.12"); } private final List certs; @@ -511,21 +563,12 @@ public PdfPKCS7(PrivateKey privKey, Certificate[] certChain, CRL[] crlList, } if (privKey != null) { - // - // Now we have private key, find out what the digestEncryptionAlgorithm - // is. - // - digestEncryptionAlgorithm = privKey.getAlgorithm(); - if (digestEncryptionAlgorithm.equals("RSA")) { - digestEncryptionAlgorithm = ID_RSA; - } else if (digestEncryptionAlgorithm.equals("DSA")) { - digestEncryptionAlgorithm = ID_DSA; - } else if (digestEncryptionAlgorithm.equals("EC") || digestEncryptionAlgorithm.equals("ECDSA")) { - digestEncryptionAlgorithm = ID_ECDSA; - } else { - throw new NoSuchAlgorithmException( - MessageLocalization.getComposedMessage("unknown.key.algorithm.1", - digestEncryptionAlgorithm)); + // Now we have private key, find out what the digestEncryptionAlgorithm is. + String signingAlgorithm = privKey.getAlgorithm(); + this.digestEncryptionAlgorithm = getOidForAlgorithmAndHash(signingAlgorithm, hashAlgorithm); + + if (this.digestEncryptionAlgorithm == null) { + throw new ExceptionConverter(new NoSuchAlgorithmException("Unknown key algorithm " + this.digestEncryptionAlgorithm + " and hash " + hashAlgorithm)); } } if (hasRSAdata) { @@ -547,6 +590,54 @@ public PdfPKCS7(PrivateKey privKey, Certificate[] certChain, CRL[] crlList, sig.initSign(privKey); } } + + /** + * Determines an OID by signing and hash algorithm. If nothing is found null will be returned. + * + * @param signingAlgorithm - currently RSA, DSA and ECDSA are supported. + * @param hashAlgorithm like SHA1, SHA2 or SHA3 + */ + private static String getOidForAlgorithmAndHash(String signingAlgorithm, String hashAlgorithm) { + + String returnValue; + + switch (signingAlgorithm) { + case "RSA": returnValue = rsaOids.get(hashAlgorithm); + //fallback + if (returnValue == null) { + returnValue = ID_RSA; + } + break; + case "DSA": returnValue = dsaOids.get(hashAlgorithm); + //fallback + if (returnValue == null) { + returnValue = ID_DSA; + } + break; + case "EC": + case "ECDSA": returnValue = ecdsaOids.get(hashAlgorithm); + //fallback + if (returnValue == null) { + returnValue = ID_ECDSA; + } + break; + default: returnValue = null; + } + + if (returnValue == null) { + //nothing found so check BC + DefaultSignatureAlgorithmIdentifierFinder finder = new DefaultSignatureAlgorithmIdentifierFinder(); + try { + AlgorithmIdentifier id = finder.find(hashAlgorithm + "with" + signingAlgorithm); + returnValue = id.getAlgorithm().getId(); + } + catch (IllegalArgumentException iae) { + //it was just a try, return null then + } + } + + return returnValue; + } /** * Gets the digest name for a certain id @@ -1239,19 +1330,14 @@ public byte[] getEncodedPKCS1() { */ public void setExternalDigest(byte[] digest, byte[] RSAdata, String digestEncryptionAlgorithm) { - externalDigest = digest; - externalRSAdata = RSAdata; + this.externalDigest = digest; + this.externalRSAdata = RSAdata; if (digestEncryptionAlgorithm != null) { - if (digestEncryptionAlgorithm.equals("RSA")) { - this.digestEncryptionAlgorithm = ID_RSA; - } else if (digestEncryptionAlgorithm.equals("DSA")) { - this.digestEncryptionAlgorithm = ID_DSA; - } else if (digestEncryptionAlgorithm.equals("EC")) { - digestEncryptionAlgorithm = ID_ECDSA; - } else { - throw new ExceptionConverter(new NoSuchAlgorithmException( - MessageLocalization.getComposedMessage("unknown.key.algorithm.1", - digestEncryptionAlgorithm))); + String hashAlgorithm = getHashAlgorithm(); + this.digestEncryptionAlgorithm = PdfPKCS7.getOidForAlgorithmAndHash(digestEncryptionAlgorithm, hashAlgorithm); + + if (this.digestEncryptionAlgorithm == null) { + throw new ExceptionConverter(new NoSuchAlgorithmException("Unknown key algorithm " + digestEncryptionAlgorithm + " hash " + hashAlgorithm)); } } } @@ -1747,10 +1833,10 @@ public static class X509Name { * @param seq an ASN1 Sequence */ public X509Name(ASN1Sequence seq) { - Enumeration e = seq.getObjects(); + Enumeration e = seq.getObjects(); while (e.hasMoreElements()) { - ASN1Set set = (ASN1Set) e.nextElement(); + ASN1Set set = e.nextElement(); for (int i = 0; i < set.size(); i++) { ASN1Sequence s = (ASN1Sequence) set.getObjectAt(i);