8367008: Algorithm identifiers for HmacSHA* should always have NULL as params

Reviewed-by: weijun
This commit is contained in:
Koushik Thirupattur 2025-10-22 21:00:18 +00:00 committed by Weijun Wang
parent d8ebe38759
commit 4377e7c9e8
3 changed files with 111 additions and 66 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2025, 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
@ -127,10 +127,35 @@ public class AlgorithmId implements Serializable, DerEncoder {
public AlgorithmId(ObjectIdentifier oid, DerValue params)
throws IOException {
this.algid = oid;
if (params != null) {
encodedParams = params.toByteArray();
decodeParams();
if (params == null) {
this.encodedParams = null;
this.algParams = null;
return;
}
/*
* If the parameters field explicitly contains an ASN.1 NULL, treat it as
* "no parameters" rather than storing a literal NULL encoding.
*
* This canonicalization ensures consistent encoding/decoding behavior:
* - Algorithms that omit parameters and those that encode explicit NULL
* are treated equivalently (encodedParams == null).
*/
if (params.tag == DerValue.tag_Null) {
if (params.length() != 0) {
throw new IOException("Invalid ASN.1 NULL in AlgorithmId parameters: " +
"non-zero length");
}
// Canonicalize to "no parameters" representation for consistency
this.encodedParams = null;
this.algParams = null;
return;
}
// Normal case: non-NULL params -> store and decode
this.encodedParams = params.toByteArray();
decodeParams();
}
protected void decodeParams() throws IOException {
@ -163,38 +188,10 @@ public class AlgorithmId implements Serializable, DerEncoder {
bytes.putOID(algid);
if (encodedParams == null) {
// MessageDigest algorithms usually have a NULL parameters even
// if most RFCs suggested absent.
// RSA key and signature algorithms requires the NULL parameters
// to be present, see A.1 and A.2.4 of RFC 8017.
if (algid.equals(RSAEncryption_oid)
|| algid.equals(MD2_oid)
|| algid.equals(MD5_oid)
|| algid.equals(SHA_oid)
|| algid.equals(SHA224_oid)
|| algid.equals(SHA256_oid)
|| algid.equals(SHA384_oid)
|| algid.equals(SHA512_oid)
|| algid.equals(SHA512_224_oid)
|| algid.equals(SHA512_256_oid)
|| algid.equals(SHA3_224_oid)
|| algid.equals(SHA3_256_oid)
|| algid.equals(SHA3_384_oid)
|| algid.equals(SHA3_512_oid)
|| algid.equals(SHA1withRSA_oid)
|| algid.equals(SHA224withRSA_oid)
|| algid.equals(SHA256withRSA_oid)
|| algid.equals(SHA384withRSA_oid)
|| algid.equals(SHA512withRSA_oid)
|| algid.equals(SHA512$224withRSA_oid)
|| algid.equals(SHA512$256withRSA_oid)
|| algid.equals(MD2withRSA_oid)
|| algid.equals(MD5withRSA_oid)
|| algid.equals(SHA3_224withRSA_oid)
|| algid.equals(SHA3_256withRSA_oid)
|| algid.equals(SHA3_384withRSA_oid)
|| algid.equals(SHA3_512withRSA_oid)) {
if (OIDS_REQUIRING_NULL.contains(algid.toString())) {
bytes.putNull();
} else {
// Parameters omitted
}
} else {
bytes.writeBytes(encodedParams);
@ -646,30 +643,54 @@ public class AlgorithmId implements Serializable, DerEncoder {
public static final ObjectIdentifier MGF1_oid =
ObjectIdentifier.of(KnownOIDs.MGF1);
public static final ObjectIdentifier SHA1withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA1withRSA);
public static final ObjectIdentifier SHA224withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA224withRSA);
public static final ObjectIdentifier SHA256withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA256withRSA);
public static final ObjectIdentifier SHA384withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA384withRSA);
public static final ObjectIdentifier SHA512withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA512withRSA);
public static final ObjectIdentifier SHA512$224withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA512$224withRSA);
public static final ObjectIdentifier SHA512$256withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA512$256withRSA);
public static final ObjectIdentifier MD2withRSA_oid =
ObjectIdentifier.of(KnownOIDs.MD2withRSA);
public static final ObjectIdentifier MD5withRSA_oid =
ObjectIdentifier.of(KnownOIDs.MD5withRSA);
public static final ObjectIdentifier SHA3_224withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA3_224withRSA);
public static final ObjectIdentifier SHA3_256withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA3_256withRSA);
public static final ObjectIdentifier SHA3_384withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA3_384withRSA);
public static final ObjectIdentifier SHA3_512withRSA_oid =
ObjectIdentifier.of(KnownOIDs.SHA3_512withRSA);
/* Set of OIDs that must explicitly encode a NULL parameter in AlgorithmIdentifier.
* References:
- RFC 8017 (PKCS #1) §A.1, §A.2.4: RSA key and signature algorithms
- RFC 9879 (HMAC) §4: HMAC algorithm identifiers
- RFC 9688 (HMAC with SHA-3) §4.3: HMAC-SHA3 algorithms MUST omit parameters
*/
private static final Set<String> OIDS_REQUIRING_NULL = Set.of(
// MessageDigest algorithms usually have a NULL parameters even
// if most RFCs suggested absent.
KnownOIDs.MD2.value(),
KnownOIDs.MD5.value(),
KnownOIDs.SHA_1.value(),
KnownOIDs.SHA_224.value(),
KnownOIDs.SHA_256.value(),
KnownOIDs.SHA_384.value(),
KnownOIDs.SHA_512.value(),
KnownOIDs.SHA_512$224.value(),
KnownOIDs.SHA_512$256.value(),
KnownOIDs.SHA3_224.value(),
KnownOIDs.SHA3_256.value(),
KnownOIDs.SHA3_384.value(),
KnownOIDs.SHA3_512.value(),
//--- RSA key and signature algorithms (RFC 8017 §A.1, §A.2.4)
KnownOIDs.RSA.value(),
KnownOIDs.SHA1withRSA.value(),
KnownOIDs.SHA224withRSA.value(),
KnownOIDs.SHA256withRSA.value(),
KnownOIDs.SHA384withRSA.value(),
KnownOIDs.SHA512withRSA.value(),
KnownOIDs.SHA512$224withRSA.value(),
KnownOIDs.SHA512$256withRSA.value(),
KnownOIDs.MD2withRSA.value(),
KnownOIDs.MD5withRSA.value(),
KnownOIDs.SHA3_224withRSA.value(),
KnownOIDs.SHA3_256withRSA.value(),
KnownOIDs.SHA3_384withRSA.value(),
KnownOIDs.SHA3_512withRSA.value(),
// HMACs per RFC 9879 (Section 4): these require explicit NULL parameters
// Note: HMAC-SHA3 algorithms (RFC 9688 §4.3) MUST omit parameters,
// so they are intentionally excluded from this list.
KnownOIDs.HmacSHA1.value(),
KnownOIDs.HmacSHA224.value(),
KnownOIDs.HmacSHA256.value(),
KnownOIDs.HmacSHA384.value(),
KnownOIDs.HmacSHA512.value(),
KnownOIDs.HmacSHA512$224.value(),
KnownOIDs.HmacSHA512$256.value()
);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2025, 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
@ -24,16 +24,19 @@
/*
* @test
* @author Gary Ellison
* @bug 4170635 8258247
* @bug 4170635 8258247 8367008
* @library /test/lib
* @summary Verify equals()/hashCode() contract honored
* @modules java.base/sun.security.x509 java.base/sun.security.util
*/
import java.io.*;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import jdk.test.lib.Asserts;
import sun.security.util.DerValue;
import sun.security.x509.*;
@ -97,5 +100,20 @@ public class AlgorithmIdEqualsHashCode {
} else {
System.out.println("PASSED equals() test");
}
// Construct an AlgorithmId with explicit DER NULL parameters
DerValue explicitNullParams = new DerValue(DerValue.tag_Null, new byte[0]);
AlgorithmId aiNullParams = new AlgorithmId(AlgorithmId.SHA256_oid,
explicitNullParams);
// The constructor should canonicalize this to "no parameters"
Asserts.assertTrue(aiNullParams.getEncodedParams() == null);
AlgorithmId aiNormal = AlgorithmId.get("SHA-256");
Asserts.assertEquals(aiNullParams, aiNormal);
Asserts.assertEquals(aiNullParams.hashCode(), aiNormal.hashCode());
// Test invalid ASN.1 NULL (non-zero length)
DerValue invalidNull = new DerValue(DerValue.tag_Null, new byte[]{0x00});
Asserts.assertThrows(IOException.class,
() -> new AlgorithmId(AlgorithmId.SHA256_oid, invalidNull));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -67,6 +67,13 @@ public class NullParams {
test("SHA3-256withRSA", true);
test("SHA3-384withRSA", true);
test("SHA3-512withRSA", true);
test("HmacSHA1", true);
test("HmacSHA224", true);
test("HmacSHA256", true);
test("HmacSHA384", true);
test("HmacSHA512", true);
test("HmacSHA512/224", true);
test("HmacSHA512/256", true);
// Full old list: must be absent
test("SHA1withECDSA", false);
@ -83,7 +90,6 @@ public class NullParams {
// Others
test("DSA", false);
test("SHA1withDSA", false);
test("HmacSHA1", false);
if (failed) {
throw new RuntimeException("At least one failed");