Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 3 additions & 62 deletions src/java.base/share/classes/sun/security/pkcs/PKCS7.java
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ public boolean isOldStyle() {
*
* @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
Expand All @@ -732,7 +732,7 @@ public boolean isOldStyle() {
* @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,
Expand All @@ -743,7 +743,7 @@ public static byte[] generateNewSignedData(
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;
Expand Down Expand Up @@ -837,65 +837,6 @@ private static byte[] constructToken(byte[] signature,
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>).
Expand Down
66 changes: 48 additions & 18 deletions src/java.base/share/classes/sun/security/pkcs/SignerInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,7 @@ SignerInfo verify(PKCS7 block, byte[] data)
// to form signing algorithm. See makeSigAlg for details.
String sigAlgName = makeSigAlg(
digestAlgorithmId,
digestEncryptionAlgorithmId,
authenticatedAttributes == null);
digestEncryptionAlgorithmId);

KnownOIDs oid = KnownOIDs.findMatch(sigAlgName);
if (oid != null) {
Expand All @@ -422,6 +421,12 @@ SignerInfo verify(PKCS7 block, byte[] data)
+ "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
Expand Down Expand Up @@ -471,26 +476,17 @@ SignerInfo verify(PKCS7 block, byte[] data)
}

/**
* 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) {
Expand All @@ -509,12 +505,12 @@ public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId,
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)) {
Expand All @@ -525,6 +521,40 @@ public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId,
throw new NoSuchAlgorithmException("Incompatible digest algorithm");
}
}
break;
case "HSS/LMS":
// RFC 8708 requires the same hash algorithm used as in the HSS/LMS algorithm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it is RFC 8554 that requires it, RFC 8708 just references RFC 8554

Copy link
Contributor Author

@wangweij wangweij Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant here is that, RFC 8708 requires that the hash algorithm used by the HSS/LMS signature (which should be a single one used in every corner of HSS/LMS, as required by RFC 8554) should be the same as the digestAlgorithm in the CMS SignerInfo object. See RFC 8708 Section 5:

digestAlgorithm MUST contain the one-way hash function used in the HSS/LMS tree.

https://www.rfc-editor.org/rfc/rfc8708.html#name-signed-data-conventions

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();
Expand Down
39 changes: 35 additions & 4 deletions src/java.base/share/classes/sun/security/util/KeyUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -405,5 +403,38 @@ public static byte[] trimZeroes(byte[] b) {
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);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -184,21 +184,20 @@ public static boolean isBlockOrSF(String s) {
/**
* 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";
};
}

/**
Expand Down
32 changes: 17 additions & 15 deletions src/java.base/share/classes/sun/security/util/SignatureUtil.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -212,12 +212,13 @@ public static class EdDSADigestAlgHolder {
* @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();
Expand All @@ -243,18 +244,18 @@ public static AlgorithmId getDigestAlgInPkcs7SignerInfo(
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;
}
Expand Down Expand Up @@ -484,14 +485,15 @@ public static void checkKeyAndSigAlgMatch(PrivateKey key, String sAlg) {
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
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ private void sign0(ZipFile zipFile, OutputStream os)
};
}
// 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1015,8 +1015,7 @@ void verifyJar(String jarName)
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();
Expand All @@ -1031,8 +1030,7 @@ void verifyJar(String jarName)
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(
Expand Down
2 changes: 1 addition & 1 deletion test/jdk/sun/security/pkcs/pkcs7/NewSigAlg.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ public static void main(String[] args) throws Exception {

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)));
}
}
Loading