diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java index 82d1a4d0bd9..23925869b9f 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java @@ -720,7 +720,7 @@ public class PKCS7 { * * @param sigalg signature algorithm to be used * @param sigProvider (optional) provider - * @param privateKey signer's private ky + * @param privateKey signer's private key * @param signerChain signer's certificate chain * @param content the content to sign * @param internalsf whether the content should be included in output @@ -732,7 +732,7 @@ public class PKCS7 { * @throws IOException should not happen here, all byte array * @throws NoSuchAlgorithmException if siglag is bad */ - public static byte[] generateNewSignedData( + public static byte[] generateSignedData( String sigalg, Provider sigProvider, PrivateKey privateKey, X509Certificate[] signerChain, byte[] content, boolean internalsf, boolean directsign, @@ -743,7 +743,7 @@ public class PKCS7 { Signature signer = SignatureUtil.fromKey(sigalg, privateKey, sigProvider); AlgorithmId digAlgID = SignatureUtil.getDigestAlgInPkcs7SignerInfo( - signer, sigalg, privateKey, directsign); + signer, sigalg, privateKey, signerChain[0].getPublicKey(), directsign); AlgorithmId sigAlgID = SignatureUtil.fromSignature(signer, privateKey); PKCS9Attributes authAttrs = null; @@ -837,65 +837,6 @@ public class PKCS7 { return p7out.toByteArray(); } - /** - * Assembles a PKCS #7 signed data message that optionally includes a - * signature timestamp. - * - * @param signature the signature bytes - * @param signerChain the signer's X.509 certificate chain - * @param content the content that is signed; specify null to not include - * it in the PKCS7 data - * @param signatureAlgorithm the name of the signature algorithm - * @param tsaURI the URI of the Timestamping Authority; or null if no - * timestamp is requested - * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a - * numerical object identifier; or null if we leave the TSA server - * to choose one. This argument is only used when tsaURI is provided - * @return the bytes of the encoded PKCS #7 signed data message - * @throws NoSuchAlgorithmException The exception is thrown if the signature - * algorithm is unrecognised. - * @throws CertificateException The exception is thrown if an error occurs - * while processing the signer's certificate or the TSA's - * certificate. - * @throws IOException The exception is thrown if an error occurs while - * generating the signature timestamp or while generating the signed - * data message. - */ - @Deprecated(since="16", forRemoval=true) - public static byte[] generateSignedData(byte[] signature, - X509Certificate[] signerChain, - byte[] content, - String signatureAlgorithm, - URI tsaURI, - String tSAPolicyID, - String tSADigestAlg) - throws CertificateException, IOException, NoSuchAlgorithmException - { - - // Generate the timestamp token - PKCS9Attributes unauthAttrs = null; - if (tsaURI != null) { - // Timestamp the signature - HttpTimestamper tsa = new HttpTimestamper(tsaURI); - byte[] tsToken = generateTimestampToken( - tsa, tSAPolicyID, tSADigestAlg, signature); - - // Insert the timestamp token into the PKCS #7 signer info element - // (as an unsigned attribute) - unauthAttrs = - new PKCS9Attributes(new PKCS9Attribute[]{ - new PKCS9Attribute( - PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID, - tsToken)}); - } - - return constructToken(signature, signerChain, content, - null, - unauthAttrs, - AlgorithmId.get(SignatureUtil.extractDigestAlgFromDwithE(signatureAlgorithm)), - AlgorithmId.get(signatureAlgorithm)); - } - /** * Examine the certificate for a Subject Information Access extension * (<a href="https://tools.ietf.org/html/rfc5280">RFC 5280</a>). diff --git a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java index 39cd90c56ab..7b4742ea92a 100644 --- a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java +++ b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java @@ -398,8 +398,7 @@ public class SignerInfo implements DerEncoder { // to form signing algorithm. See makeSigAlg for details. String sigAlgName = makeSigAlg( digestAlgorithmId, - digestEncryptionAlgorithmId, - authenticatedAttributes == null); + digestEncryptionAlgorithmId); KnownOIDs oid = KnownOIDs.findMatch(sigAlgName); if (oid != null) { @@ -422,6 +421,12 @@ public class SignerInfo implements DerEncoder { + "critical extension(s)"); } + algorithmsConformanceCheck( + digestAlgorithmId, + digestEncryptionAlgorithmId, + key, + authenticatedAttributes == null); + // Make sure that if the usage of the key in the certificate is // restricted, it can be used for digital signatures. // XXX We may want to check for additional extensions in the @@ -471,26 +476,17 @@ public class SignerInfo implements DerEncoder { } /** - * Derives the signature algorithm name from the digest algorithm - * and the encryption algorithm inside a PKCS7 SignerInfo. - * - * The digest algorithm is in the form "DIG", and the encryption - * algorithm can be in any of the 3 forms: - * - * 1. Old style key algorithm like RSA, DSA, EC, this method returns - * DIGwithKEY. - * 2. New style signature algorithm in the form of HASHwithKEY, this - * method returns DIGwithKEY. Please note this is not HASHwithKEY. - * 3. Modern signature algorithm like RSASSA-PSS and EdDSA, this method - * returns the signature algorithm itself but ensures digAlgId is - * compatible with the algorithm as described in RFC 4056 and 8419. + * Checks if the digest algorithm and encryption algorithm combination + * inside a PKCS7 SignerInfo is legal. * * @param digAlgId the digest algorithm * @param encAlgId the encryption algorithm + * @param key the public key for verification * @param directSign whether the signature is calculated on the content * directly. This makes difference for Ed448. */ - public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId, + private static void algorithmsConformanceCheck( + AlgorithmId digAlgId, AlgorithmId encAlgId, PublicKey key, boolean directSign) throws NoSuchAlgorithmException { String encAlg = encAlgId.getName(); switch (encAlg) { @@ -509,12 +505,12 @@ public class SignerInfo implements DerEncoder { if (!AlgorithmId.get(spec.getDigestAlgorithm()).equals(digAlgId)) { throw new NoSuchAlgorithmException("Incompatible digest algorithm"); } - return encAlg; + break; case "Ed25519": if (!digAlgId.equals(SignatureUtil.EdDSADigestAlgHolder.sha512)) { throw new NoSuchAlgorithmException("Incompatible digest algorithm"); } - return encAlg; + break; case "Ed448": if (directSign) { if (!digAlgId.equals(SignatureUtil.EdDSADigestAlgHolder.shake256)) { @@ -525,6 +521,40 @@ public class SignerInfo implements DerEncoder { throw new NoSuchAlgorithmException("Incompatible digest algorithm"); } } + break; + case "HSS/LMS": + // RFC 8708 requires the same hash algorithm used as in the HSS/LMS algorithm + if (!digAlgId.equals(AlgorithmId.get(KeyUtil.hashAlgFromHSS(key)))) { + throw new NoSuchAlgorithmException("Incompatible digest algorithm"); + } + break; + } + } + + /** + * Derives the signature algorithm name from the digest algorithm + * and the encryption algorithm inside a PKCS7 SignerInfo. + * + * The digest algorithm is in the form "DIG", and the encryption + * algorithm can be in any of the 3 forms: + * + * 1. Old style key algorithm like RSA, DSA, EC, this method returns + * DIGwithKEY. + * 2. New style signature algorithm in the form of HASHwithKEY, this + * method returns DIGwithKEY. Please note this is not HASHwithKEY. + * 3. Modern signature algorithm like RSASSA-PSS and EdDSA, this method + * returns the signature algorithm itself. + * + * @param digAlgId the digest algorithm + * @param encAlgId the encryption algorithm + */ + public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId) { + String encAlg = encAlgId.getName(); + switch (encAlg) { + case "RSASSA-PSS": + case "Ed25519": + case "Ed448": + case "HSS/LMS": return encAlg; default: String digAlg = digAlgId.getName(); diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index e17ca2aaaeb..059467779b9 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -25,11 +25,9 @@ package sun.security.util; +import java.io.IOException; import java.math.BigInteger; -import java.security.AlgorithmParameters; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.SecureRandom; +import java.security.*; import java.security.interfaces.*; import java.security.spec.*; import java.util.Arrays; @@ -405,5 +403,38 @@ public final class KeyUtil { return t; } + /** + * Finds the hash algorithm from an HSS/LMS public key. + * + * @param publicKey the HSS/LMS public key + * @return the hash algorithm + * @throws NoSuchAlgorithmException if key is from an unknown configuration + */ + public static String hashAlgFromHSS(PublicKey publicKey) + throws NoSuchAlgorithmException { + try { + DerValue val = new DerValue(publicKey.getEncoded()); + val.data.getDerValue(); + byte[] rawKey = new DerValue(val.data.getBitString()).getOctetString(); + // According to https://www.rfc-editor.org/rfc/rfc8554.html: + // Section 6.1: HSS public key is u32str(L) || pub[0], where pub[0] + // is the LMS public key for the top-level tree. + // Section 5.3: LMS public key is u32str(type) || u32str(otstype) || I || T[1] + // Section 8: type is the numeric identifier for an LMS specification. + // This RFC defines 5 SHA-256 based types, value from 5 to 9. + if (rawKey.length < 8) { + throw new NoSuchAlgorithmException("Cannot decode public key"); + } + int num = ((rawKey[4] & 0xff) << 24) + ((rawKey[5] & 0xff) << 16) + + ((rawKey[6] & 0xff) << 8) + (rawKey[7] & 0xff); + return switch (num) { + // RFC 8554 only supports SHA_256 hash algorithm + case 5, 6, 7, 8, 9 -> "SHA-256"; + default -> throw new NoSuchAlgorithmException("Unknown LMS type: " + num); + }; + } catch (IOException e) { + throw new NoSuchAlgorithmException("Cannot decode public key", e); + } + } } diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index e16ae803b96..1e204816bd6 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -184,21 +184,20 @@ public class SignatureFileVerifier { /** * Returns the signed JAR block file extension for a key. * + * Returns "DSA" for unknown algorithms. This is safe because the + * signature verification process does not require the extension to + * match the key algorithm as long as it is "RSA", "DSA", or "EC." + * * @param key the key used to sign the JAR file * @return the extension * @see #isBlockOrSF(String) */ public static String getBlockExtension(PrivateKey key) { - String keyAlgorithm = key.getAlgorithm().toUpperCase(Locale.ENGLISH); - if (keyAlgorithm.equals("RSASSA-PSS")) { - return "RSA"; - } else if (keyAlgorithm.equals("EDDSA") - || keyAlgorithm.equals("ED25519") - || keyAlgorithm.equals("ED448")) { - return "EC"; - } else { - return keyAlgorithm; - } + return switch (key.getAlgorithm().toUpperCase(Locale.ENGLISH)) { + case "RSA", "RSASSA-PSS" -> "RSA"; + case "EC", "EDDSA", "ED25519", "ED448" -> "EC"; + default -> "DSA"; + }; } /** diff --git a/src/java.base/share/classes/sun/security/util/SignatureUtil.java b/src/java.base/share/classes/sun/security/util/SignatureUtil.java index 61d8be3ca8c..3d415814ba4 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureUtil.java +++ b/src/java.base/share/classes/sun/security/util/SignatureUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -212,12 +212,13 @@ public class SignatureUtil { * @param signer Signature object that tells you RSASSA-PSS params * @param sigalg Signature algorithm * @param privateKey key tells you EdDSA params + * @param publicKey key tells you HSS/LMS hash algorithm * @param directsign Ed448 uses different digest algs depending on this * @return the digest algId * @throws NoSuchAlgorithmException */ public static AlgorithmId getDigestAlgInPkcs7SignerInfo( - Signature signer, String sigalg, PrivateKey privateKey, boolean directsign) + Signature signer, String sigalg, PrivateKey privateKey, PublicKey publicKey, boolean directsign) throws NoSuchAlgorithmException { AlgorithmId digAlgID; String kAlg = privateKey.getAlgorithm(); @@ -243,18 +244,18 @@ public class SignatureUtil { default: throw new AssertionError("Unknown curve name: " + kAlg); } - } else { - if (sigalg.equalsIgnoreCase("RSASSA-PSS")) { - try { - digAlgID = AlgorithmId.get(signer.getParameters() - .getParameterSpec(PSSParameterSpec.class) - .getDigestAlgorithm()); - } catch (InvalidParameterSpecException e) { - throw new AssertionError("Should not happen", e); - } - } else { - digAlgID = AlgorithmId.get(extractDigestAlgFromDwithE(sigalg)); + } else if (sigalg.equalsIgnoreCase("RSASSA-PSS")) { + try { + digAlgID = AlgorithmId.get(signer.getParameters() + .getParameterSpec(PSSParameterSpec.class) + .getDigestAlgorithm()); + } catch (InvalidParameterSpecException e) { + throw new AssertionError("Should not happen", e); } + } else if (sigalg.equalsIgnoreCase("HSS/LMS")) { + digAlgID = AlgorithmId.get(KeyUtil.hashAlgFromHSS(publicKey)); + } else { + digAlgID = AlgorithmId.get(extractDigestAlgFromDwithE(sigalg)); } return digAlgID; } @@ -484,14 +485,15 @@ public class SignatureUtil { public static String getDefaultSigAlgForKey(PrivateKey k) { String kAlg = k.getAlgorithm().toUpperCase(Locale.ENGLISH); return switch (kAlg) { + case "DH", "XDH", "X25519", "X448" -> null; case "DSA" -> "SHA256withDSA"; case "RSA" -> ifcFfcStrength(KeyUtil.getKeySize(k)) + "withRSA"; case "EC" -> ecStrength(KeyUtil.getKeySize(k)) + "withECDSA"; case "EDDSA" -> k instanceof EdECPrivateKey ? ((EdECPrivateKey) k).getParams().getName() : kAlg; - case "RSASSA-PSS", "ED25519", "ED448" -> kAlg; - default -> null; + default -> kAlg; // All modern signature algorithms, + // RSASSA-PSS, ED25519, ED448, HSS/LMS, etc }; } diff --git a/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java b/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java index 4f85adb7528..5dbaf6041ee 100644 --- a/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java +++ b/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java @@ -845,7 +845,7 @@ public final class JarSigner { }; } // We now create authAttrs in block data, so "direct == false". - block = PKCS7.generateNewSignedData(sigalg, sigProvider, privateKey, certChain, + block = PKCS7.generateSignedData(sigalg, sigProvider, privateKey, certChain, content, internalsf, false, timestamper); String sfFilename = sf.getMetaName(); diff --git a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java index ed2c9be8543..e011145632d 100644 --- a/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java @@ -1015,8 +1015,7 @@ public class Main { String digestAlg = digestMap.get(s); String sigAlg = SignerInfo.makeSigAlg( si.getDigestAlgorithmId(), - si.getDigestEncryptionAlgorithmId(), - si.getAuthenticatedAttributes() == null); + si.getDigestEncryptionAlgorithmId()); AlgorithmId encAlgId = si.getDigestEncryptionAlgorithmId(); AlgorithmParameters sigAlgParams = encAlgId.getParameters(); PublicKey key = signer.getPublicKey(); @@ -1031,8 +1030,7 @@ public class Main { String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName(); String tsSigAlg = SignerInfo.makeSigAlg( tsSi.getDigestAlgorithmId(), - tsSi.getDigestEncryptionAlgorithmId(), - tsSi.getAuthenticatedAttributes() == null); + tsSi.getDigestEncryptionAlgorithmId()); AlgorithmId tsEncAlgId = tsSi.getDigestEncryptionAlgorithmId(); AlgorithmParameters tsSigAlgParams = tsEncAlgId.getParameters(); Calendar c = Calendar.getInstance( diff --git a/test/jdk/sun/security/pkcs/pkcs7/NewSigAlg.java b/test/jdk/sun/security/pkcs/pkcs7/NewSigAlg.java index 098dd9caaa4..926c2c0c65e 100644 --- a/test/jdk/sun/security/pkcs/pkcs7/NewSigAlg.java +++ b/test/jdk/sun/security/pkcs/pkcs7/NewSigAlg.java @@ -46,6 +46,6 @@ public class NewSigAlg { static void test(String d, String e, String s) throws Exception { Asserts.assertEQ(s, SignerInfo.makeSigAlg( - AlgorithmId.get(d), AlgorithmId.get(s), false)); + AlgorithmId.get(d), AlgorithmId.get(s))); } } diff --git a/test/jdk/sun/security/pkcs/pkcs7/TwoHash.java b/test/jdk/sun/security/pkcs/pkcs7/TwoHash.java index 6aaf7825c26..4ebedd21928 100644 --- a/test/jdk/sun/security/pkcs/pkcs7/TwoHash.java +++ b/test/jdk/sun/security/pkcs/pkcs7/TwoHash.java @@ -45,7 +45,7 @@ public class TwoHash { CertAndKeyGen cak = new CertAndKeyGen("EC", "SHA512withECDSA"); cak.generate("secp256r1"); - byte[] signature = PKCS7.generateNewSignedData( + byte[] signature = PKCS7.generateSignedData( "SHA256withECDSA", null, cak.getPrivateKey(), diff --git a/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java b/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java index a3647552557..22d630f3aa8 100644 --- a/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java +++ b/test/lib/jdk/test/lib/security/timestamp/TsaSigner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -219,7 +219,8 @@ public class TsaSigner { new X500Name(issuerName), signerEntry.cert.getSerialNumber(), SignatureUtil.getDigestAlgInPkcs7SignerInfo( - signature, sigAlgo, signerEntry.privateKey, false), + signature, sigAlgo, signerEntry.privateKey, + signerEntry.cert.getPublicKey(), false), AlgorithmId.get(sigAlgo), signature.sign());