Skip to content

Commit 0a60b0f

Browse files
committed
8302233: HSS/LMS: keytool and jarsigner changes
Reviewed-by: mullan
1 parent 7ad7005 commit 0a60b0f

File tree

10 files changed

+120
-118
lines changed

10 files changed

+120
-118
lines changed

src/java.base/share/classes/sun/security/pkcs/PKCS7.java

Lines changed: 3 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ public boolean isOldStyle() {
720720
*
721721
* @param sigalg signature algorithm to be used
722722
* @param sigProvider (optional) provider
723-
* @param privateKey signer's private ky
723+
* @param privateKey signer's private key
724724
* @param signerChain signer's certificate chain
725725
* @param content the content to sign
726726
* @param internalsf whether the content should be included in output
@@ -732,7 +732,7 @@ public boolean isOldStyle() {
732732
* @throws IOException should not happen here, all byte array
733733
* @throws NoSuchAlgorithmException if siglag is bad
734734
*/
735-
public static byte[] generateNewSignedData(
735+
public static byte[] generateSignedData(
736736
String sigalg, Provider sigProvider,
737737
PrivateKey privateKey, X509Certificate[] signerChain,
738738
byte[] content, boolean internalsf, boolean directsign,
@@ -743,7 +743,7 @@ public static byte[] generateNewSignedData(
743743
Signature signer = SignatureUtil.fromKey(sigalg, privateKey, sigProvider);
744744

745745
AlgorithmId digAlgID = SignatureUtil.getDigestAlgInPkcs7SignerInfo(
746-
signer, sigalg, privateKey, directsign);
746+
signer, sigalg, privateKey, signerChain[0].getPublicKey(), directsign);
747747
AlgorithmId sigAlgID = SignatureUtil.fromSignature(signer, privateKey);
748748

749749
PKCS9Attributes authAttrs = null;
@@ -837,65 +837,6 @@ private static byte[] constructToken(byte[] signature,
837837
return p7out.toByteArray();
838838
}
839839

840-
/**
841-
* Assembles a PKCS #7 signed data message that optionally includes a
842-
* signature timestamp.
843-
*
844-
* @param signature the signature bytes
845-
* @param signerChain the signer's X.509 certificate chain
846-
* @param content the content that is signed; specify null to not include
847-
* it in the PKCS7 data
848-
* @param signatureAlgorithm the name of the signature algorithm
849-
* @param tsaURI the URI of the Timestamping Authority; or null if no
850-
* timestamp is requested
851-
* @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
852-
* numerical object identifier; or null if we leave the TSA server
853-
* to choose one. This argument is only used when tsaURI is provided
854-
* @return the bytes of the encoded PKCS #7 signed data message
855-
* @throws NoSuchAlgorithmException The exception is thrown if the signature
856-
* algorithm is unrecognised.
857-
* @throws CertificateException The exception is thrown if an error occurs
858-
* while processing the signer's certificate or the TSA's
859-
* certificate.
860-
* @throws IOException The exception is thrown if an error occurs while
861-
* generating the signature timestamp or while generating the signed
862-
* data message.
863-
*/
864-
@Deprecated(since="16", forRemoval=true)
865-
public static byte[] generateSignedData(byte[] signature,
866-
X509Certificate[] signerChain,
867-
byte[] content,
868-
String signatureAlgorithm,
869-
URI tsaURI,
870-
String tSAPolicyID,
871-
String tSADigestAlg)
872-
throws CertificateException, IOException, NoSuchAlgorithmException
873-
{
874-
875-
// Generate the timestamp token
876-
PKCS9Attributes unauthAttrs = null;
877-
if (tsaURI != null) {
878-
// Timestamp the signature
879-
HttpTimestamper tsa = new HttpTimestamper(tsaURI);
880-
byte[] tsToken = generateTimestampToken(
881-
tsa, tSAPolicyID, tSADigestAlg, signature);
882-
883-
// Insert the timestamp token into the PKCS #7 signer info element
884-
// (as an unsigned attribute)
885-
unauthAttrs =
886-
new PKCS9Attributes(new PKCS9Attribute[]{
887-
new PKCS9Attribute(
888-
PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID,
889-
tsToken)});
890-
}
891-
892-
return constructToken(signature, signerChain, content,
893-
null,
894-
unauthAttrs,
895-
AlgorithmId.get(SignatureUtil.extractDigestAlgFromDwithE(signatureAlgorithm)),
896-
AlgorithmId.get(signatureAlgorithm));
897-
}
898-
899840
/**
900841
* Examine the certificate for a Subject Information Access extension
901842
* (<a href="https://tools.ietf.org/html/rfc5280">RFC 5280</a>).

src/java.base/share/classes/sun/security/pkcs/SignerInfo.java

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,7 @@ SignerInfo verify(PKCS7 block, byte[] data)
398398
// to form signing algorithm. See makeSigAlg for details.
399399
String sigAlgName = makeSigAlg(
400400
digestAlgorithmId,
401-
digestEncryptionAlgorithmId,
402-
authenticatedAttributes == null);
401+
digestEncryptionAlgorithmId);
403402

404403
KnownOIDs oid = KnownOIDs.findMatch(sigAlgName);
405404
if (oid != null) {
@@ -422,6 +421,12 @@ SignerInfo verify(PKCS7 block, byte[] data)
422421
+ "critical extension(s)");
423422
}
424423

424+
algorithmsConformanceCheck(
425+
digestAlgorithmId,
426+
digestEncryptionAlgorithmId,
427+
key,
428+
authenticatedAttributes == null);
429+
425430
// Make sure that if the usage of the key in the certificate is
426431
// restricted, it can be used for digital signatures.
427432
// XXX We may want to check for additional extensions in the
@@ -471,26 +476,17 @@ SignerInfo verify(PKCS7 block, byte[] data)
471476
}
472477

473478
/**
474-
* Derives the signature algorithm name from the digest algorithm
475-
* and the encryption algorithm inside a PKCS7 SignerInfo.
476-
*
477-
* The digest algorithm is in the form "DIG", and the encryption
478-
* algorithm can be in any of the 3 forms:
479-
*
480-
* 1. Old style key algorithm like RSA, DSA, EC, this method returns
481-
* DIGwithKEY.
482-
* 2. New style signature algorithm in the form of HASHwithKEY, this
483-
* method returns DIGwithKEY. Please note this is not HASHwithKEY.
484-
* 3. Modern signature algorithm like RSASSA-PSS and EdDSA, this method
485-
* returns the signature algorithm itself but ensures digAlgId is
486-
* compatible with the algorithm as described in RFC 4056 and 8419.
479+
* Checks if the digest algorithm and encryption algorithm combination
480+
* inside a PKCS7 SignerInfo is legal.
487481
*
488482
* @param digAlgId the digest algorithm
489483
* @param encAlgId the encryption algorithm
484+
* @param key the public key for verification
490485
* @param directSign whether the signature is calculated on the content
491486
* directly. This makes difference for Ed448.
492487
*/
493-
public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId,
488+
private static void algorithmsConformanceCheck(
489+
AlgorithmId digAlgId, AlgorithmId encAlgId, PublicKey key,
494490
boolean directSign) throws NoSuchAlgorithmException {
495491
String encAlg = encAlgId.getName();
496492
switch (encAlg) {
@@ -509,12 +505,12 @@ public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId,
509505
if (!AlgorithmId.get(spec.getDigestAlgorithm()).equals(digAlgId)) {
510506
throw new NoSuchAlgorithmException("Incompatible digest algorithm");
511507
}
512-
return encAlg;
508+
break;
513509
case "Ed25519":
514510
if (!digAlgId.equals(SignatureUtil.EdDSADigestAlgHolder.sha512)) {
515511
throw new NoSuchAlgorithmException("Incompatible digest algorithm");
516512
}
517-
return encAlg;
513+
break;
518514
case "Ed448":
519515
if (directSign) {
520516
if (!digAlgId.equals(SignatureUtil.EdDSADigestAlgHolder.shake256)) {
@@ -525,6 +521,40 @@ public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId,
525521
throw new NoSuchAlgorithmException("Incompatible digest algorithm");
526522
}
527523
}
524+
break;
525+
case "HSS/LMS":
526+
// RFC 8708 requires the same hash algorithm used as in the HSS/LMS algorithm
527+
if (!digAlgId.equals(AlgorithmId.get(KeyUtil.hashAlgFromHSS(key)))) {
528+
throw new NoSuchAlgorithmException("Incompatible digest algorithm");
529+
}
530+
break;
531+
}
532+
}
533+
534+
/**
535+
* Derives the signature algorithm name from the digest algorithm
536+
* and the encryption algorithm inside a PKCS7 SignerInfo.
537+
*
538+
* The digest algorithm is in the form "DIG", and the encryption
539+
* algorithm can be in any of the 3 forms:
540+
*
541+
* 1. Old style key algorithm like RSA, DSA, EC, this method returns
542+
* DIGwithKEY.
543+
* 2. New style signature algorithm in the form of HASHwithKEY, this
544+
* method returns DIGwithKEY. Please note this is not HASHwithKEY.
545+
* 3. Modern signature algorithm like RSASSA-PSS and EdDSA, this method
546+
* returns the signature algorithm itself.
547+
*
548+
* @param digAlgId the digest algorithm
549+
* @param encAlgId the encryption algorithm
550+
*/
551+
public static String makeSigAlg(AlgorithmId digAlgId, AlgorithmId encAlgId) {
552+
String encAlg = encAlgId.getName();
553+
switch (encAlg) {
554+
case "RSASSA-PSS":
555+
case "Ed25519":
556+
case "Ed448":
557+
case "HSS/LMS":
528558
return encAlg;
529559
default:
530560
String digAlg = digAlgId.getName();

src/java.base/share/classes/sun/security/util/KeyUtil.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@
2525

2626
package sun.security.util;
2727

28+
import java.io.IOException;
2829
import java.math.BigInteger;
29-
import java.security.AlgorithmParameters;
30-
import java.security.InvalidKeyException;
31-
import java.security.Key;
32-
import java.security.SecureRandom;
30+
import java.security.*;
3331
import java.security.interfaces.*;
3432
import java.security.spec.*;
3533
import java.util.Arrays;
@@ -405,5 +403,38 @@ public static byte[] trimZeroes(byte[] b) {
405403
return t;
406404
}
407405

406+
/**
407+
* Finds the hash algorithm from an HSS/LMS public key.
408+
*
409+
* @param publicKey the HSS/LMS public key
410+
* @return the hash algorithm
411+
* @throws NoSuchAlgorithmException if key is from an unknown configuration
412+
*/
413+
public static String hashAlgFromHSS(PublicKey publicKey)
414+
throws NoSuchAlgorithmException {
415+
try {
416+
DerValue val = new DerValue(publicKey.getEncoded());
417+
val.data.getDerValue();
418+
byte[] rawKey = new DerValue(val.data.getBitString()).getOctetString();
419+
// According to https://www.rfc-editor.org/rfc/rfc8554.html:
420+
// Section 6.1: HSS public key is u32str(L) || pub[0], where pub[0]
421+
// is the LMS public key for the top-level tree.
422+
// Section 5.3: LMS public key is u32str(type) || u32str(otstype) || I || T[1]
423+
// Section 8: type is the numeric identifier for an LMS specification.
424+
// This RFC defines 5 SHA-256 based types, value from 5 to 9.
425+
if (rawKey.length < 8) {
426+
throw new NoSuchAlgorithmException("Cannot decode public key");
427+
}
428+
int num = ((rawKey[4] & 0xff) << 24) + ((rawKey[5] & 0xff) << 16)
429+
+ ((rawKey[6] & 0xff) << 8) + (rawKey[7] & 0xff);
430+
return switch (num) {
431+
// RFC 8554 only supports SHA_256 hash algorithm
432+
case 5, 6, 7, 8, 9 -> "SHA-256";
433+
default -> throw new NoSuchAlgorithmException("Unknown LMS type: " + num);
434+
};
435+
} catch (IOException e) {
436+
throw new NoSuchAlgorithmException("Cannot decode public key", e);
437+
}
438+
}
408439
}
409440

src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -184,21 +184,20 @@ public static boolean isBlockOrSF(String s) {
184184
/**
185185
* Returns the signed JAR block file extension for a key.
186186
*
187+
* Returns "DSA" for unknown algorithms. This is safe because the
188+
* signature verification process does not require the extension to
189+
* match the key algorithm as long as it is "RSA", "DSA", or "EC."
190+
*
187191
* @param key the key used to sign the JAR file
188192
* @return the extension
189193
* @see #isBlockOrSF(String)
190194
*/
191195
public static String getBlockExtension(PrivateKey key) {
192-
String keyAlgorithm = key.getAlgorithm().toUpperCase(Locale.ENGLISH);
193-
if (keyAlgorithm.equals("RSASSA-PSS")) {
194-
return "RSA";
195-
} else if (keyAlgorithm.equals("EDDSA")
196-
|| keyAlgorithm.equals("ED25519")
197-
|| keyAlgorithm.equals("ED448")) {
198-
return "EC";
199-
} else {
200-
return keyAlgorithm;
201-
}
196+
return switch (key.getAlgorithm().toUpperCase(Locale.ENGLISH)) {
197+
case "RSA", "RSASSA-PSS" -> "RSA";
198+
case "EC", "EDDSA", "ED25519", "ED448" -> "EC";
199+
default -> "DSA";
200+
};
202201
}
203202

204203
/**

src/java.base/share/classes/sun/security/util/SignatureUtil.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -212,12 +212,13 @@ public static class EdDSADigestAlgHolder {
212212
* @param signer Signature object that tells you RSASSA-PSS params
213213
* @param sigalg Signature algorithm
214214
* @param privateKey key tells you EdDSA params
215+
* @param publicKey key tells you HSS/LMS hash algorithm
215216
* @param directsign Ed448 uses different digest algs depending on this
216217
* @return the digest algId
217218
* @throws NoSuchAlgorithmException
218219
*/
219220
public static AlgorithmId getDigestAlgInPkcs7SignerInfo(
220-
Signature signer, String sigalg, PrivateKey privateKey, boolean directsign)
221+
Signature signer, String sigalg, PrivateKey privateKey, PublicKey publicKey, boolean directsign)
221222
throws NoSuchAlgorithmException {
222223
AlgorithmId digAlgID;
223224
String kAlg = privateKey.getAlgorithm();
@@ -243,18 +244,18 @@ public static AlgorithmId getDigestAlgInPkcs7SignerInfo(
243244
default:
244245
throw new AssertionError("Unknown curve name: " + kAlg);
245246
}
246-
} else {
247-
if (sigalg.equalsIgnoreCase("RSASSA-PSS")) {
248-
try {
249-
digAlgID = AlgorithmId.get(signer.getParameters()
250-
.getParameterSpec(PSSParameterSpec.class)
251-
.getDigestAlgorithm());
252-
} catch (InvalidParameterSpecException e) {
253-
throw new AssertionError("Should not happen", e);
254-
}
255-
} else {
256-
digAlgID = AlgorithmId.get(extractDigestAlgFromDwithE(sigalg));
247+
} else if (sigalg.equalsIgnoreCase("RSASSA-PSS")) {
248+
try {
249+
digAlgID = AlgorithmId.get(signer.getParameters()
250+
.getParameterSpec(PSSParameterSpec.class)
251+
.getDigestAlgorithm());
252+
} catch (InvalidParameterSpecException e) {
253+
throw new AssertionError("Should not happen", e);
257254
}
255+
} else if (sigalg.equalsIgnoreCase("HSS/LMS")) {
256+
digAlgID = AlgorithmId.get(KeyUtil.hashAlgFromHSS(publicKey));
257+
} else {
258+
digAlgID = AlgorithmId.get(extractDigestAlgFromDwithE(sigalg));
258259
}
259260
return digAlgID;
260261
}
@@ -484,14 +485,15 @@ public static void checkKeyAndSigAlgMatch(PrivateKey key, String sAlg) {
484485
public static String getDefaultSigAlgForKey(PrivateKey k) {
485486
String kAlg = k.getAlgorithm().toUpperCase(Locale.ENGLISH);
486487
return switch (kAlg) {
488+
case "DH", "XDH", "X25519", "X448" -> null;
487489
case "DSA" -> "SHA256withDSA";
488490
case "RSA" -> ifcFfcStrength(KeyUtil.getKeySize(k)) + "withRSA";
489491
case "EC" -> ecStrength(KeyUtil.getKeySize(k)) + "withECDSA";
490492
case "EDDSA" -> k instanceof EdECPrivateKey
491493
? ((EdECPrivateKey) k).getParams().getName()
492494
: kAlg;
493-
case "RSASSA-PSS", "ED25519", "ED448" -> kAlg;
494-
default -> null;
495+
default -> kAlg; // All modern signature algorithms,
496+
// RSASSA-PSS, ED25519, ED448, HSS/LMS, etc
495497
};
496498
}
497499

src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ private void sign0(ZipFile zipFile, OutputStream os)
845845
};
846846
}
847847
// We now create authAttrs in block data, so "direct == false".
848-
block = PKCS7.generateNewSignedData(sigalg, sigProvider, privateKey, certChain,
848+
block = PKCS7.generateSignedData(sigalg, sigProvider, privateKey, certChain,
849849
content, internalsf, false, timestamper);
850850

851851
String sfFilename = sf.getMetaName();

src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,8 +1015,7 @@ void verifyJar(String jarName)
10151015
String digestAlg = digestMap.get(s);
10161016
String sigAlg = SignerInfo.makeSigAlg(
10171017
si.getDigestAlgorithmId(),
1018-
si.getDigestEncryptionAlgorithmId(),
1019-
si.getAuthenticatedAttributes() == null);
1018+
si.getDigestEncryptionAlgorithmId());
10201019
AlgorithmId encAlgId = si.getDigestEncryptionAlgorithmId();
10211020
AlgorithmParameters sigAlgParams = encAlgId.getParameters();
10221021
PublicKey key = signer.getPublicKey();
@@ -1031,8 +1030,7 @@ void verifyJar(String jarName)
10311030
String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
10321031
String tsSigAlg = SignerInfo.makeSigAlg(
10331032
tsSi.getDigestAlgorithmId(),
1034-
tsSi.getDigestEncryptionAlgorithmId(),
1035-
tsSi.getAuthenticatedAttributes() == null);
1033+
tsSi.getDigestEncryptionAlgorithmId());
10361034
AlgorithmId tsEncAlgId = tsSi.getDigestEncryptionAlgorithmId();
10371035
AlgorithmParameters tsSigAlgParams = tsEncAlgId.getParameters();
10381036
Calendar c = Calendar.getInstance(

test/jdk/sun/security/pkcs/pkcs7/NewSigAlg.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ public static void main(String[] args) throws Exception {
4646

4747
static void test(String d, String e, String s) throws Exception {
4848
Asserts.assertEQ(s, SignerInfo.makeSigAlg(
49-
AlgorithmId.get(d), AlgorithmId.get(s), false));
49+
AlgorithmId.get(d), AlgorithmId.get(s)));
5050
}
5151
}

0 commit comments

Comments
 (0)