Version in base suite: 21.0.11+10-1~deb13u2 Version in overlay suite: 21.0.12+8-1~deb13u1 Base version: openjdk-21_21.0.12+8-1~deb13u1 Target version: openjdk-21_21.0.12.1+1-1~deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/o/openjdk-21/openjdk-21_21.0.12+8-1~deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/o/openjdk-21/openjdk-21_21.0.12.1+1-1~deb13u1.dsc .jcheck/conf | 4 debian/changelog | 29 debian/copyright | 6 debian/copyright-generator/bundled-stanzas | 2 debian/copyright-generator/copyright-gen.py | 2 debian/copyright-generator/debian-stanzas | 2 debian/rules | 15 debian/tests/jtreg-autopkgtest.in | 2 debian/tests/jtreg-autopkgtest.sh | 2 debian/tests/problems.csv | 3 make/conf/version-numbers.conf | 4 src/java.base/share/classes/sun/net/www/http/HttpClient.java | 8 src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java | 16 src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java | 4 src/java.base/share/classes/sun/security/ssl/ServerHello.java | 10 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java | 25 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java | 14 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java | 17 test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java | 6 test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java | 487 +++++----- test/lib/jdk/test/lib/security/XMLUtils.java | 11 21 files changed, 433 insertions(+), 236 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpn6ctn6t1/openjdk-21_21.0.12+8-1~deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpn6ctn6t1/openjdk-21_21.0.12.1+1-1~deb13u1.dsc: no acceptable signature found diff -Nru openjdk-21-21.0.12+8/.jcheck/conf openjdk-21-21.0.12.1+1/.jcheck/conf --- openjdk-21-21.0.12+8/.jcheck/conf 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/.jcheck/conf 2026-08-07 11:31:48.000000000 +0000 @@ -1,7 +1,7 @@ [general] project=jdk-updates jbs=JDK -version=21.0.12 +version=21.0.12.1 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists @@ -9,7 +9,7 @@ [repository] tags=(?:jdk-(?:[1-9]([0-9]*)(?:\.(?:0|[1-9][0-9]*)){0,4})(?:\+(?:(?:[0-9]+))|(?:-ga)))|(?:jdk[4-9](?:u\d{1,3})?-(?:(?:b\d{2,3})|(?:ga)))|(?:hs\d\d(?:\.\d{1,2})?-b\d\d) -branches= +branches=.* [census] version=0 diff -Nru openjdk-21-21.0.12+8/debian/changelog openjdk-21-21.0.12.1+1/debian/changelog --- openjdk-21-21.0.12+8/debian/changelog 2026-08-08 15:22:18.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/changelog 2026-08-19 17:55:58.000000000 +0000 @@ -1,8 +1,33 @@ -openjdk-21 (21.0.12+8-1~deb13u1) trixie-security; urgency=medium +openjdk-21 (21.0.12.1+1-1~deb13u1) trixie-security; urgency=medium * Rebuild for trixie - -- Moritz Muehlenhoff Sat, 08 Aug 2026 17:22:18 +0200 + -- Moritz Muehlenhoff Wed, 19 Aug 2026 19:55:58 +0200 + +openjdk-21 (21.0.12.1+1-1) unstable; urgency=medium + + * OpenJDK 21.0.12.1 release, build 1. + - CVEs: + + CVE-2026-61308 + + CVE-2026-70907 + + CVE-2026-60589 + + -- Vladimir Petko Wed, 19 Aug 2026 09:45:20 +1200 + +openjdk-21 (21.0.12+8-2) unstable; urgency=medium + + [ Vladimir Petko ] + * d/t/jtreg-autopkgtest.in: Do not fail tests if ulimit can not be + set. + * d/t/problems.csv: Add test exclusions. + * d/rules: Enable S390x C2 compiler. + * d/rules: Collect hotspot error and replay logs after jtreg tests. + * d/copyright-generator: Fix license SPDX identifiers. + + [ Matthias Klose ] + * Build using GCC 16 on development releases. + + -- Vladimir Petko Sun, 16 Aug 2026 14:17:53 +1200 openjdk-21 (21.0.12+8-1) unstable; urgency=medium diff -Nru openjdk-21-21.0.12+8/debian/copyright openjdk-21-21.0.12.1+1/debian/copyright --- openjdk-21-21.0.12+8/debian/copyright 2026-07-22 00:30:06.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/copyright 2026-08-18 21:23:33.000000000 +0000 @@ -64,7 +64,7 @@ Portions Copyright (c) 1995 Colin Plumb Portions Copyright (c) 1997-2003 Eastman Kodak Company See other third party notices under the License section -License: GPL with Classpath exception +License: GPL2 with Classpath exception . --- begin of LICENSE --- . @@ -4470,7 +4470,7 @@ Files: googletest/* Copyright: Copyright 2008, Google Inc. -License: BSD-C3 +License: BSD-3-Clause Copyright 2008, Google Inc. All rights reserved. . @@ -4524,7 +4524,7 @@ Files: debian/dbg.py Copyright: Copyright 2016, Red Hat and individual contributors -License: LGPL +License: LGPL-2.1 # Copyright 2016, Red Hat and individual contributors # by the @authors tag. # diff -Nru openjdk-21-21.0.12+8/debian/copyright-generator/bundled-stanzas openjdk-21-21.0.12.1+1/debian/copyright-generator/bundled-stanzas --- openjdk-21-21.0.12+8/debian/copyright-generator/bundled-stanzas 2026-07-22 00:30:06.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/copyright-generator/bundled-stanzas 2026-08-18 20:47:27.000000000 +0000 @@ -1,6 +1,6 @@ Files: googletest/* Copyright: Copyright 2008, Google Inc. -License: BSD-C3 +License: BSD-3-Clause Copyright 2008, Google Inc. All rights reserved. . diff -Nru openjdk-21-21.0.12+8/debian/copyright-generator/copyright-gen.py openjdk-21-21.0.12.1+1/debian/copyright-generator/copyright-gen.py --- openjdk-21-21.0.12+8/debian/copyright-generator/copyright-gen.py 2026-07-22 00:30:06.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/copyright-generator/copyright-gen.py 2026-08-18 21:23:33.000000000 +0000 @@ -269,7 +269,7 @@ generate_header_stanza(); - licenses = f"""GPL with Classpath exception + licenses = f"""GPL2 with Classpath exception --- begin of LICENSE --- diff -Nru openjdk-21-21.0.12+8/debian/copyright-generator/debian-stanzas openjdk-21-21.0.12.1+1/debian/copyright-generator/debian-stanzas --- openjdk-21-21.0.12+8/debian/copyright-generator/debian-stanzas 2026-07-21 20:45:48.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/copyright-generator/debian-stanzas 2026-08-18 20:47:03.000000000 +0000 @@ -22,7 +22,7 @@ Files: debian/dbg.py Copyright: Copyright 2016, Red Hat and individual contributors -License: LGPL +License: LGPL-2.1 # Copyright 2016, Red Hat and individual contributors # by the @authors tag. # diff -Nru openjdk-21-21.0.12+8/debian/rules openjdk-21-21.0.12.1+1/debian/rules --- openjdk-21-21.0.12+8/debian/rules 2026-07-22 00:30:12.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/rules 2026-08-18 21:45:12.000000000 +0000 @@ -280,10 +280,14 @@ export CC = $(DEB_HOST_GNU_TYPE)-gcc-14 export CXX = $(DEB_HOST_GNU_TYPE)-g++-14 bd_gcc = g++-14 , -else +else ifneq (,$(filter $(distrel),questing resolute)) export CC = $(DEB_HOST_GNU_TYPE)-gcc-15 export CXX = $(DEB_HOST_GNU_TYPE)-g++-15 bd_gcc = g++-15 , +else + export CC = $(DEB_HOST_GNU_TYPE)-gcc-16 + export CXX = $(DEB_HOST_GNU_TYPE)-g++-16 + bd_gcc = g++-16 , endif # GCC 11 only @@ -442,11 +446,6 @@ ZERO_CONFIGURE_ARGS += --with-boot-jdk-jvmargs="-XX:ThreadStackSize=2240" endif -# Disable C2 compiler due to the bug in 21.0.5+11 update -ifneq (,$(filter $(DEB_HOST_ARCH), s390x)) - COMMON_CONFIGURE_ARGS += --with-boot-jdk-jvmargs="-XX:TieredStopAtLevel=1" -endif - ifeq ($(with_check),yes) COMMON_CONFIGURE_ARGS += --with-jtreg=/usr/share/jtreg else @@ -1330,7 +1329,7 @@ @echo "END jtreg-summary-$(VMNAME)" -for i in hotspot langtools jaxp jdk; do \ - find jtreg-test-output/$$i/JTwork/ -name '*.jtr'; \ + find jtreg-test-output/$$i/JTwork/ -name '*.jtr' -o -name 'hs_err_pid*.log' -o -name 'replay_pid*.log'; \ done | sort -u > jtreg-test-output/failed_tests-$(VMNAME).list; \ GZIP=-9vn tar --ignore-failed-read -C . -c -z -f jtreg-test-output/failed_tests-$(VMNAME).tar.gz -T jtreg-test-output/failed_tests-$(VMNAME).list GZIP=-9vn tar -C . -c -z -f jtreg-test-output/jtreport-$(VMNAME).tar.gz $$(find jtreg-test-output -name JTreport) @@ -2085,7 +2084,7 @@ dh_builddeb -a $(nodemo) $(nojrez) #$(bd_options) git_project = jdk21u -git_tag = jdk-21.0.12+8 +git_tag = jdk-21.0.12.1+1 package_version = $(shell echo $(PKGVERSION) | sed 's/-[^-][^-]*$$//') ifneq ($(is_upstream_release),yes) package_version := $(subst +,~,$(package_version)) diff -Nru openjdk-21-21.0.12+8/debian/tests/jtreg-autopkgtest.in openjdk-21-21.0.12.1+1/debian/tests/jtreg-autopkgtest.in --- openjdk-21-21.0.12+8/debian/tests/jtreg-autopkgtest.in 2026-07-22 00:30:06.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/tests/jtreg-autopkgtest.in 2026-08-18 21:23:33.000000000 +0000 @@ -12,6 +12,8 @@ exit 1 fi +ulimit -c unlimited || echo "Unable to set ulimit -c unlimited, coredump tests will fail" + host_arch="${DEB_HOST_ARCH:-$(dpkg --print-architecture)}" if [ -z "${JDK_TO_TEST+x}" ]; then diff -Nru openjdk-21-21.0.12+8/debian/tests/jtreg-autopkgtest.sh openjdk-21-21.0.12.1+1/debian/tests/jtreg-autopkgtest.sh --- openjdk-21-21.0.12+8/debian/tests/jtreg-autopkgtest.sh 2026-07-22 00:30:06.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/tests/jtreg-autopkgtest.sh 2026-08-18 21:23:33.000000000 +0000 @@ -12,6 +12,8 @@ exit 1 fi +ulimit -c unlimited || echo "Unable to set ulimit -c unlimited, coredump tests will fail" + host_arch="${DEB_HOST_ARCH:-$(dpkg --print-architecture)}" if [ -z "${JDK_TO_TEST+x}" ]; then diff -Nru openjdk-21-21.0.12+8/debian/tests/problems.csv openjdk-21-21.0.12.1+1/debian/tests/problems.csv --- openjdk-21-21.0.12+8/debian/tests/problems.csv 2026-07-16 04:29:37.000000000 +0000 +++ openjdk-21-21.0.12.1+1/debian/tests/problems.csv 2026-08-13 11:28:41.000000000 +0000 @@ -109,3 +109,6 @@ FAILED: serviceability/jvmti/GetModulesInfo/JvmtiGetAllModulesTest.java,JDK-8386138,Problemlisted upstream,:openjdk-27:,arch-all,:release-all FAILED: compiler/loopopts/superword/TestDoNotFilterNaNSummands.java,JDK-8370408,Known issue in s390x C2,:openjdk-25::openjdk-26::openjdk-27::openjdk-28:,s390x:,:release-all FAILED: compiler/loopopts/superword/TestMemorySegmentFilterSummands.java,JDK-8370408,Known issue in s390x C2,:openjdk-25::openjdk-26::openjdk-27::openjdk-28:,s390x:,:release-all +FAILED: serviceability/attach/AttachNegativePidTest.java,,Lingered App not found (CI),:openjdk-21:,armhf:,:jammy +FAILED: jdk/jfr/event/oldobject/TestDFSWithSmallStack.java,,Test fails with the small stack,:openjdk-21:,i386,:release-all +FAILED: compiler/loopopts/superword/TestIndependentPacksWithCyclicDependency.java,,Invalid test3 result: dataI[3]: 1116942242 != 7,:openjdk-21:,:armhf,:release-all diff -Nru openjdk-21-21.0.12+8/make/conf/version-numbers.conf openjdk-21-21.0.12.1+1/make/conf/version-numbers.conf --- openjdk-21-21.0.12+8/make/conf/version-numbers.conf 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/make/conf/version-numbers.conf 2026-08-07 11:31:48.000000000 +0000 @@ -29,11 +29,11 @@ DEFAULT_VERSION_FEATURE=21 DEFAULT_VERSION_INTERIM=0 DEFAULT_VERSION_UPDATE=12 -DEFAULT_VERSION_PATCH=0 +DEFAULT_VERSION_PATCH=1 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2026-07-21 +DEFAULT_VERSION_DATE=2026-08-18 DEFAULT_VERSION_CLASSFILE_MAJOR=65 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 diff -Nru openjdk-21-21.0.12+8/src/java.base/share/classes/sun/net/www/http/HttpClient.java openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/net/www/http/HttpClient.java --- openjdk-21-21.0.12+8/src/java.base/share/classes/sun/net/www/http/HttpClient.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/net/www/http/HttpClient.java 2026-08-07 11:31:48.000000000 +0000 @@ -27,6 +27,7 @@ import java.io.*; import java.net.*; +import java.net.Proxy.Type; import java.util.Locale; import java.util.Objects; import java.util.OptionalInt; @@ -183,6 +184,13 @@ return keepAliveTimeout; } + public Proxy getHttpProxy() { + if (proxy != null && proxy.type() == Type.HTTP) { + return proxy; + } + return null; + } + static String normalizeCBT(String s) { if (s == null || s.equals("never")) { return "never"; diff -Nru openjdk-21-21.0.12+8/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java --- openjdk-21-21.0.12+8/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java 2026-08-07 11:31:48.000000000 +0000 @@ -392,6 +392,9 @@ private boolean tryTransparentNTLMProxy = true; private boolean useProxyResponseCode = false; + // used when redirecting to compare current and previous proxies + private Proxy lastProxy; + /* Used by Windows specific code */ private Object authObj; @@ -1698,7 +1701,6 @@ // If the user has set either of these headers then do not remove them isUserServerAuth = requests.getKey("Authorization") != -1; isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1; - try { do { if (!checkReuseConnection()) @@ -1708,6 +1710,14 @@ return cachedInputStream; } + // we may need to remove proxy-authorization + Proxy p = http.getHttpProxy(); + // if we're not using a proxy or if the proxy to be used is not + // the same as the originally set one, then remove it + if (p == null || (lastProxy != null && !lastProxy.equals(p))) { + requests.remove("Proxy-Authorization"); + lastProxy = null; + } /* REMIND: This exists to fix the HttpsURLConnection subclass. * Hotjava needs to run on JDK1.1FCS. Do proper fix once a * proper solution for SSL can be found. @@ -1738,7 +1748,7 @@ disconnectInternal(); throw new IOException ("Invalid Http response"); } - if (respCode == HTTP_PROXY_AUTH) { + if (respCode == HTTP_PROXY_AUTH && tunnelState() != TunnelState.TUNNELING) { if (streaming()) { disconnectInternal(); throw new HttpRetryException ( @@ -2298,6 +2308,7 @@ if (respCode == HTTP_OK) { setTunnelState(TunnelState.TUNNELING); + savedRequests.remove("Proxy-Authorization"); break; } // we don't know how to deal with other response code @@ -2873,6 +2884,7 @@ { assert isLockHeldByCurrentThread(); + lastProxy = http.getHttpProxy(); disconnectInternal(); if (streaming()) { throw new HttpRetryException (RETRY_MSG3, stat, loc); diff -Nru openjdk-21-21.0.12+8/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java --- openjdk-21-21.0.12+8/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java 2026-08-07 11:31:48.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -56,7 +56,7 @@ private static final long DEFAULT_STATUS_RESP_DELAY = 5000L; final long statusRespTimeout; boolean acceptCliHelloFragments = false; - + boolean sentHRR = false; ServerHandshakeContext(SSLContextImpl sslContext, TransportContext conContext) throws IOException { diff -Nru openjdk-21-21.0.12+8/src/java.base/share/classes/sun/security/ssl/ServerHello.java openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/security/ssl/ServerHello.java --- openjdk-21-21.0.12+8/src/java.base/share/classes/sun/security/ssl/ServerHello.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.base/share/classes/sun/security/ssl/ServerHello.java 2026-08-07 11:31:48.000000000 +0000 @@ -744,6 +744,15 @@ public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { ServerHandshakeContext shc = (ServerHandshakeContext) context; + + + if (shc.sentHRR) { + throw shc.conContext.fatal( + Alert.HANDSHAKE_FAILURE, + "TLS 1.3 server MUST NOT send a second HelloRetryRequest " + + "in the same connection"); + } + ClientHelloMessage clientHello = (ClientHelloMessage) message; // negotiate the cipher suite. @@ -779,6 +788,7 @@ // Output the handshake message. hhrm.write(shc.handshakeOutput); shc.handshakeOutput.flush(); + shc.sentHRR = true; // In TLS1.3 middlebox compatibility mode the server sends a // dummy change_cipher_spec record immediately after its diff -Nru openjdk-21-21.0.12+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java openjdk-21-21.0.12.1+1/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java --- openjdk-21-21.0.12+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java 2026-08-07 11:31:48.000000000 +0000 @@ -51,4 +51,29 @@ */ public abstract boolean engineCanResolveURI(ResourceResolverContext context); + /** + * Returns the scheme for a URI. + * + * @param uri the URI + * @return the scheme, or {@code null} if none + */ + protected static final String scheme(String uri) { + if (uri == null) { + return null; + } + char[] uriChars = uri.toCharArray(); + // Similar to java.net.URI::parse. Find ':' before any of '/', '?', + // or '#', and treat the characters before it as scheme. + for (int i = 0; i < uriChars.length; i++) { + if (uriChars[i] == '/' || uriChars[i] == '?' || uriChars[i] == '#') { + return null; + } + if (uriChars[i] == ':') { + // No validation on the output since we only care if it's + // empty or equal to specific values. + return uri.substring(0, i); + } + } + return null; + } } diff -Nru openjdk-21-21.0.12+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java openjdk-21-21.0.12.1+1/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java --- openjdk-21-21.0.12+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java 2026-08-07 11:31:48.000000000 +0000 @@ -207,6 +207,8 @@ */ @Override public boolean engineCanResolveURI(ResourceResolverContext context) { + LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + if (context.uriToResolve == null) { LOG.debug("quick fail, uri == null"); return false; @@ -217,11 +219,15 @@ return false; } - LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + String uriToResolveScheme = scheme(context.uriToResolve); - if (context.uriToResolve.startsWith("http:") || - context.uriToResolve.startsWith("https:") || - context.baseUri != null && (context.baseUri.startsWith("http:") || context.baseUri.startsWith("https:"))) { + if (uriToResolveScheme == null) { + String baseUriScheme = scheme(context.baseUri); + if ("http".equals(baseUriScheme) || "https".equals(baseUriScheme)) { + LOG.debug("I state that I can resolve {}", context.uriToResolve); + return true; + } + } else if (uriToResolveScheme.equals("http") || uriToResolveScheme.equals("https")) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } diff -Nru openjdk-21-21.0.12+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java openjdk-21-21.0.12.1+1/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java --- openjdk-21-21.0.12+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java 2026-08-07 11:31:48.000000000 +0000 @@ -72,20 +72,23 @@ return false; } - if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#' || - context.uriToResolve.startsWith("http:") || context.uriToResolve.startsWith("https:")) { + if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#') { return false; } - try { - LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); + LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); - if (context.uriToResolve.startsWith("file:") || context.baseUri.startsWith("file:")) { + String uriToResolveScheme = scheme(context.uriToResolve); + + if (uriToResolveScheme == null) { + String baseUriScheme = scheme(context.baseUri); + if ("file".equals(baseUriScheme)) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } - } catch (Exception e) { - LOG.debug(e.getMessage(), e); + } else if (uriToResolveScheme.equals("file")) { + LOG.debug("I state that I can resolve {}", context.uriToResolve); + return true; } LOG.debug("But I can't"); diff -Nru openjdk-21-21.0.12+8/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java openjdk-21-21.0.12.1+1/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java --- openjdk-21-21.0.12+8/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java 2026-08-07 11:31:48.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -68,13 +68,11 @@ * @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST PROXY305 * @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST SERVER307 * @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER - * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY + * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes= HTTPSetAuthenticatorTest BASIC PROXY * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY305 * @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER307 * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER * @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307 - * - * @author danielfuchs */ public class HTTPSetAuthenticatorTest extends HTTPTest { diff -Nru openjdk-21-21.0.12+8/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java openjdk-21-21.0.12.1+1/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java --- openjdk-21-21.0.12+8/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java 2026-08-07 11:31:48.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -53,6 +53,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.HexFormat; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Random; @@ -60,6 +61,7 @@ import java.util.stream.Collectors; import javax.net.ssl.SSLContext; import sun.net.www.HeaderParser; +import sun.net.www.MessageHeader; /** * A simple HTTP server that supports Digest authentication. @@ -344,10 +346,11 @@ Objects.requireNonNull(auth); HttpServer impl = createHttpServer(protocol); + AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, algorithm); final HTTPTestServer server = new HTTPTestServer(impl, null, delegate); final HttpHandler hh = server.createHandler(schemeType, auth, authType); HttpContext ctxt = impl.createContext(path, hh); - server.configureAuthentication(ctxt, schemeType, auth, authType, algorithm); + server.configureAuthentication(ctxt, schemeType, authResponder, authType); impl.start(); return server; } @@ -363,12 +366,19 @@ Objects.requireNonNull(auth); HttpServer impl = createHttpServer(protocol); + AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, null); final HTTPTestServer server = protocol == HttpProtocolType.HTTPS - ? new HttpsProxyTunnel(impl, null, delegate) + ? new HttpsProxyTunnel(impl, null, delegate, authResponder) : new HTTPTestServer(impl, null, delegate); final HttpHandler hh = server.createHandler(schemeType, auth, authType); HttpContext ctxt = impl.createContext(path, hh); - server.configureAuthentication(ctxt, schemeType, auth, authType, null); + if (protocol == HttpProtocolType.HTTPS) { + server.configureAuthentication(ctxt, HttpSchemeType.NONE, + new NoAuthResponder(auth, HttpAuthType.SERVER), + HttpAuthType.SERVER); + } else { + server.configureAuthentication(ctxt, schemeType, authResponder, authType); + } impl.start(); return server; @@ -441,16 +451,16 @@ private void configureAuthentication(HttpContext ctxt, HttpSchemeType schemeType, - HttpTestAuthenticator auth, - HttpAuthType authType, String algorithm) { + AuthResponder authResponder, + HttpAuthType authType) { switch(schemeType) { case DIGEST: // DIGEST authentication is handled by the handler. - ctxt.getFilters().add(new HttpDigestFilter(auth, authType, algorithm)); + ctxt.getFilters().add(new HttpDigestFilter(authResponder)); break; case BASIC: // BASIC authentication is handled by the filter. - ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); + ctxt.getFilters().add(new HttpBasicFilter(authResponder)); break; case BASICSERVER: switch(authType) { @@ -458,14 +468,14 @@ // HttpServer can't support Proxy-type authentication // => we do as if BASIC had been specified, and we will // handle authentication in the handler. - ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); + ctxt.getFilters().add(new HttpBasicFilter(authResponder)); break; case SERVER: case SERVER307: // Basic authentication is handled by HttpServer // directly => the filter should not perform // authentication again. - setContextAuthenticator(ctxt, auth); - ctxt.getFilters().add(new HttpNoAuthFilter(authType)); + setContextAuthenticator(ctxt, authResponder.authenticator); + ctxt.getFilters().add(new HttpNoAuthFilter(authResponder)); break; default: throw new InternalError("Invalid combination scheme=" @@ -473,7 +483,7 @@ } case NONE: // No authentication at all. - ctxt.getFilters().add(new HttpNoAuthFilter(authType)); + ctxt.getFilters().add(new HttpNoAuthFilter(authResponder)); break; default: throw new InternalError("No such scheme: " + schemeType); @@ -485,38 +495,230 @@ return new Http3xxHandler(proxyURL, type, code300); } - // Abstract HTTP filter class. - private abstract static class AbstractHttpFilter extends Filter { - + private static abstract class AuthResponder { final HttpAuthType authType; + final HttpTestAuthenticator authenticator; final String type; - public AbstractHttpFilter(HttpAuthType authType, String type) { + + AuthResponder(HttpTestAuthenticator authenticator, + HttpAuthType authType, + String scheme) { + this.authenticator = authenticator; this.authType = authType; - this.type = type; + this.type = authType == HttpAuthType.PROXY + ? scheme + " Proxy" + : scheme + " Server"; } - String getLocation() { - return "Location"; - } - String getAuthenticate() { + final String authenticateHeader() { return authType == HttpAuthType.PROXY - ? "Proxy-Authenticate" : "WWW-Authenticate"; + ? "Proxy-Authenticate" + : "WWW-Authenticate"; } - String getAuthorization() { + final String authorizationHeader() { return authType == HttpAuthType.PROXY - ? "Proxy-Authorization" : "Authorization"; + ? "Proxy-Authorization" + : "Authorization"; } - int getUnauthorizedCode() { + int unauthorizedCode() { return authType == HttpAuthType.PROXY ? HttpURLConnection.HTTP_PROXY_AUTH : HttpURLConnection.HTTP_UNAUTHORIZED; } - String getKeepAlive() { - return "keep-alive"; - } - String getConnection() { + String unauthorizedString() { return authType == HttpAuthType.PROXY - ? "Proxy-Connection" : "Connection"; + ? "Proxy Authentication Required" + : "Unauthorized"; + } + String type() { return type;} + abstract String generateAuthenticateChallenge(); + abstract boolean isAuthentified(String method, Iterator authValues); + } + + private static final class BasicAuthResponder extends AuthResponder { + BasicAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) { + super(authenticator, authType, "Basic"); + } + + @Override + String generateAuthenticateChallenge() { + return "Basic realm=\"" + authenticator.getRealm() + "\""; + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + while(authValues.hasNext()) { + String a = authValues.next(); + System.out.println(type + ": processing " + a); + int sp = a.indexOf(' '); + if (sp < 0) return false; + String scheme = a.substring(0, sp); + if (!"Basic".equalsIgnoreCase(scheme)) { + System.out.println(type + ": Unsupported scheme '" + + scheme +"'"); + return false; + } + if (a.length() <= sp+1) { + System.out.println(type + ": value too short for '" + + scheme +"'"); + return false; + } + a = a.substring(sp+1); + return validate(a); + } + return false; + } + + boolean validate(String a) { + byte[] b = Base64.getDecoder().decode(a); + String userpass = new String (b); + int colon = userpass.indexOf (':'); + String uname = userpass.substring (0, colon); + String pass = userpass.substring (colon+1); + return authenticator.getUserName().equals(uname) && + new String(authenticator.getPassword(uname)).equals(pass); + } + + } + + private static final class DigestAuthResponder extends AuthResponder { + // This is a very basic DIGEST - used only for the purpose of testing + // the client implementation. Therefore we can get away with never + // updating the server nonce as it makes the implementation of the + // server side digest simpler. + private final byte[] nonce; + private final String ns; + private final String algorithm; + DigestAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType, String algorithm) { + super(authenticator, authType, "Digest"); + nonce = new byte[16]; + new Random(Instant.now().toEpochMilli()).nextBytes(nonce); + ns = new BigInteger(1, nonce).toString(16); + this.algorithm = (algorithm == null) ? "MD5" : algorithm; + } + + @Override + String generateAuthenticateChallenge() { + return "Digest realm=\"" + authenticator.getRealm() + "\"," + + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", " + + "\r\n nonce=\"" + ns +"\""; + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + while(authValues.hasNext()) { + String a = authValues.next(); + System.out.println(type + ": processing " + a); + int sp = a.indexOf(' '); + if (sp < 0) return false; + String scheme = a.substring(0, sp); + if (!"Digest".equalsIgnoreCase(scheme)) { + System.out.println(type + ": Unsupported scheme '" + scheme +"'"); + return false; + } + if (a.length() <= sp+1) { + System.out.println(type + ": value too short for '" + scheme +"'"); + return false; + } + a = a.substring(sp+1); + DigestResponse dgr = DigestResponse.create(a); + return validate(method, dgr); + } + return false; + } + + boolean validate(String reqMethod, DigestResponse dg) { + if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) { + System.out.println(type + ": Unsupported algorithm " + + dg.algorithm); + return false; + } + if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) { + System.out.println(type + ": Unsupported qop " + + dg.qop); + return false; + } + try { + if (!dg.nonce.equals(ns)) { + System.out.println(type + ": bad nonce returned by client: " + + nonce + " expected " + ns); + return false; + } + if (dg.response == null) { + System.out.println(type + ": missing digest response."); + return false; + } + char[] pa = authenticator.getPassword(dg.username); + return verify(reqMethod, dg, pa); + } catch(IllegalArgumentException | SecurityException + | NoSuchAlgorithmException e) { + System.out.println(type + ": " + e.getMessage()); + return false; + } + } + + boolean verify(String reqMethod, DigestResponse dg, char[] pw) + throws NoSuchAlgorithmException { + String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg); + if (!dg.response.equals(response)) { + System.out.println(type + ": bad response returned by client: " + + dg.response + " expected " + response); + return false; + } else { + System.out.println(type + ": verified response " + response); + } + return true; + } + + } + + private static final class NoAuthResponder extends AuthResponder { + NoAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) { + super(authenticator, authType, "NoAuth"); + } + + @Override + String generateAuthenticateChallenge() { + throw new InternalError("Should not reach here"); + } + + @Override + boolean isAuthentified(String method, Iterator authValues) { + return true; + } + } + + private static AuthResponder createAuthResponder(HttpSchemeType schemeType, + HttpTestAuthenticator authenticator, + HttpAuthType authType, + String algorithm) { + switch (schemeType) { + case BASIC, BASICSERVER: return new BasicAuthResponder(authenticator, authType); + case DIGEST: return new DigestAuthResponder(authenticator, authType, algorithm); + case NONE: return new NoAuthResponder(authenticator, authType); + default: throw new IllegalArgumentException( + "Unknown authentication scheme: " + schemeType); + } + } + + // Abstract HTTP filter class. + private abstract static class AbstractHttpFilter extends Filter { + + final AuthResponder authResponder; + final String type; + public AbstractHttpFilter(AuthResponder authResponder) { + this.authResponder = authResponder; + this.type = authResponder.type(); + } + + final String getAuthenticate() { + return authResponder.authenticateHeader(); + } + final String getAuthorization() { + return authResponder.authorizationHeader(); + } + final int getUnauthorizedCode() { + return authResponder.unauthorizedCode(); } protected abstract boolean isAuthentified(HttpExchange he) throws IOException; protected abstract void requestAuthentication(HttpExchange he) throws IOException; @@ -694,11 +896,10 @@ } - private class HttpNoAuthFilter extends AbstractHttpFilter { + private static final class HttpNoAuthFilter extends AbstractHttpFilter { - public HttpNoAuthFilter(HttpAuthType authType) { - super(authType, authType == HttpAuthType.SERVER - ? "NoAuth Server" : "NoAuth Proxy"); + public HttpNoAuthFilter(AuthResponder authResponder) { + super(authResponder); } @Override @@ -720,19 +921,15 @@ // An HTTP Filter that performs Basic authentication private class HttpBasicFilter extends AbstractHttpFilter { - - private final HttpTestAuthenticator auth; - public HttpBasicFilter(HttpTestAuthenticator auth, HttpAuthType authType) { - super(authType, authType == HttpAuthType.SERVER - ? "Basic Server" : "Basic Proxy"); - this.auth = auth; + public HttpBasicFilter(AuthResponder authResponder) { + super(authResponder); } @Override protected void requestAuthentication(HttpExchange he) throws IOException { - he.getResponseHeaders().add(getAuthenticate(), - "Basic realm=\"" + auth.getRealm() + "\""); + String challenge = authResponder.generateAuthenticateChallenge(); + he.getResponseHeaders().add(getAuthenticate(), challenge); System.out.println(type + ": Requesting Basic Authentication " + he.getResponseHeaders().getFirst(getAuthenticate())); } @@ -742,39 +939,12 @@ if (he.getRequestHeaders().containsKey(getAuthorization())) { List authorization = he.getRequestHeaders().get(getAuthorization()); - for (String a : authorization) { - System.out.println(type + ": processing " + a); - int sp = a.indexOf(' '); - if (sp < 0) return false; - String scheme = a.substring(0, sp); - if (!"Basic".equalsIgnoreCase(scheme)) { - System.out.println(type + ": Unsupported scheme '" - + scheme +"'"); - return false; - } - if (a.length() <= sp+1) { - System.out.println(type + ": value too short for '" - + scheme +"'"); - return false; - } - a = a.substring(sp+1); - return validate(a); - } - return false; + return authResponder.isAuthentified(he.getRequestMethod(), + authorization.iterator()); } return false; } - boolean validate(String a) { - byte[] b = Base64.getDecoder().decode(a); - String userpass = new String (b); - int colon = userpass.indexOf (':'); - String uname = userpass.substring (0, colon); - String pass = userpass.substring (colon+1); - return auth.getUserName().equals(uname) && - new String(auth.getPassword(uname)).equals(pass); - } - @Override public String description() { return "Filter for " + type; @@ -786,31 +956,14 @@ // An HTTP Filter that performs Digest authentication private class HttpDigestFilter extends AbstractHttpFilter { - // This is a very basic DIGEST - used only for the purpose of testing - // the client implementation. Therefore we can get away with never - // updating the server nonce as it makes the implementation of the - // server side digest simpler. - private final HttpTestAuthenticator auth; - private final byte[] nonce; - private final String ns; - private final String algorithm; - public HttpDigestFilter(HttpTestAuthenticator auth, HttpAuthType authType, String algorithm) { - super(authType, authType == HttpAuthType.SERVER - ? "Digest Server" : "Digest Proxy"); - this.auth = auth; - nonce = new byte[16]; - new Random(Instant.now().toEpochMilli()).nextBytes(nonce); - ns = new BigInteger(1, nonce).toString(16); - this.algorithm = (algorithm == null) ? "MD5" : algorithm; + public HttpDigestFilter(AuthResponder authResponder) { + super(authResponder); } @Override protected void requestAuthentication(HttpExchange he) throws IOException { - he.getResponseHeaders().add(getAuthenticate(), - "Digest realm=\"" + auth.getRealm() + "\"," - + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", " - + "\r\n nonce=\"" + ns +"\""); + he.getResponseHeaders().add(getAuthenticate(), authResponder.generateAuthenticateChallenge()); System.out.println(type + ": Requesting Digest Authentication " + he.getResponseHeaders().getFirst(getAuthenticate())); } @@ -819,71 +972,11 @@ protected boolean isAuthentified(HttpExchange he) { if (he.getRequestHeaders().containsKey(getAuthorization())) { List authorization = he.getRequestHeaders().get(getAuthorization()); - for (String a : authorization) { - System.out.println(type + ": processing " + a); - int sp = a.indexOf(' '); - if (sp < 0) return false; - String scheme = a.substring(0, sp); - if (!"Digest".equalsIgnoreCase(scheme)) { - System.out.println(type + ": Unsupported scheme '" + scheme +"'"); - return false; - } - if (a.length() <= sp+1) { - System.out.println(type + ": value too short for '" + scheme +"'"); - return false; - } - a = a.substring(sp+1); - DigestResponse dgr = DigestResponse.create(a); - return validate(he.getRequestMethod(), dgr); - } - return false; + return authResponder.isAuthentified(he.getRequestMethod(), authorization.iterator()); } return false; } - boolean validate(String reqMethod, DigestResponse dg) { - if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) { - System.out.println(type + ": Unsupported algorithm " - + dg.algorithm); - return false; - } - if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) { - System.out.println(type + ": Unsupported qop " - + dg.qop); - return false; - } - try { - if (!dg.nonce.equals(ns)) { - System.out.println(type + ": bad nonce returned by client: " - + nonce + " expected " + ns); - return false; - } - if (dg.response == null) { - System.out.println(type + ": missing digest response."); - return false; - } - char[] pa = auth.getPassword(dg.username); - return verify(reqMethod, dg, pa); - } catch(IllegalArgumentException | SecurityException - | NoSuchAlgorithmException e) { - System.out.println(type + ": " + e.getMessage()); - return false; - } - } - - boolean verify(String reqMethod, DigestResponse dg, char[] pw) - throws NoSuchAlgorithmException { - String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg); - if (!dg.response.equals(response)) { - System.out.println(type + ": bad response returned by client: " - + dg.response + " expected " + response); - return false; - } else { - System.out.println(type + ": verified response " + response); - } - return true; - } - @Override public String description() { return "Filter for DIGEST authentication"; @@ -979,22 +1072,23 @@ } } - // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden - // behind a fake proxy that only understands CONNECT requests. - // The fake proxy is just a server socket that intercept the - // CONNECT and then redirect streams to the real server. + // The HttpsProxyTunnel is a proxy that only understands + // CONNECT requests. It is only used for tunnelling, but + // supports Proxy Authentication with the help of an + // AuthResponder static class HttpsProxyTunnel extends HTTPTestServer implements Runnable { final ServerSocket ss; + final AuthResponder authResponder; private volatile boolean stop; public HttpsProxyTunnel(HttpServer server, HTTPTestServer target, - HttpHandler delegate) + HttpHandler delegate, AuthResponder authResponder) throws IOException { super(server, target, delegate); System.out.flush(); - System.err.println("WARNING: HttpsProxyTunnel is an experimental test class"); + this.authResponder = authResponder; ss = ServerSocketFactory.create(); start(); } @@ -1048,28 +1142,6 @@ return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort()); } - // This is a bit shaky. It doesn't handle continuation - // lines, but our client shouldn't send any. - // Read a line from the input stream, swallowing the final - // \r\n sequence. Stops at the first \n, doesn't complain - // if it wasn't preceded by '\r'. - // - String readLine(InputStream r) throws IOException { - StringBuilder b = new StringBuilder(); - int c; - while ((c = r.read()) != -1) { - if (c == '\n') break; - b.appendCodePoint(c); - } - if (b.length() == 0) { - return ""; - } - if (b.codePointAt(b.length() -1) == '\r') { - b.delete(b.length() -1, b.length()); - } - return b.toString(); - } - @Override public void run() { Socket clientConnection = null; @@ -1137,6 +1209,37 @@ } } + private boolean isAuthentified(MessageHeader request) { + String requestLine = request.getValue(0); + String method = requestLine.substring(0, requestLine.indexOf(' ')); + assert "CONNECT".equals(method); + return authResponder.isAuthentified(method, + request.multiValueIterator(authResponder.authorizationHeader())); + } + + private String challengeResponse() { + return "HTTP/1.1 " + authResponder.unauthorizedCode() + " " + + authResponder.unauthorizedString() + + "\r\nContent-Length: 0\r\n" + + authResponder.authenticateHeader() + ": " + + authResponder.generateAuthenticateChallenge() + + "\r\n\r\n"; + } + + private String okResponse() { + return "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + } + + private String badGatewayResponse() { + return "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n"; + } + + private void sendResponse(PrintWriter pw, String response) { + System.out.println("Tunnel: Sending " + response); + pw.print(response); + pw.flush(); + } + private void processRequestAndWaitToComplete(final Socket clientConnection) throws IOException, InterruptedException { final Socket targetConnection; @@ -1146,32 +1249,24 @@ clientConnection.getOutputStream(), "UTF-8"); PrintWriter pw = new PrintWriter(w); System.out.println("Tunnel: Reading request line"); - String requestLine = readLine(ccis); + MessageHeader request = new MessageHeader(ccis); + String requestLine = request.getValue(0); System.out.println("Tunnel: Request line: " + requestLine); - if (requestLine.startsWith("CONNECT ")) { - // We should probably check that the next word following - // CONNECT is the host:port of our HTTPS serverImpl. - // Some improvement for a followup! - - // Read all headers until we find the empty line that - // signals the end of all headers. - while(!requestLine.equals("")) { - System.out.println("Tunnel: Reading header: " - + (requestLine = readLine(ccis))); + if (requestLine != null && requestLine.startsWith("CONNECT ")) { + if (!isAuthentified(request)) { + sendResponse(pw, challengeResponse()); + return; } - targetConnection = new Socket( serverImpl.getAddress().getAddress(), serverImpl.getAddress().getPort()); // Then send the 200 OK response to the client - System.out.println("Tunnel: Sending " - + "HTTP/1.1 200 OK\r\n\r\n"); - pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); - pw.flush(); + sendResponse(pw, okResponse()); } else { // This should not happen. If it does then consider it a // client error and throw an IOException + sendResponse(pw, badGatewayResponse()); System.out.println("Tunnel: Throwing an IOException due to unexpected" + " request line: " + requestLine); throw new IOException("Client request error - Unexpected request line"); diff -Nru openjdk-21-21.0.12+8/test/lib/jdk/test/lib/security/XMLUtils.java openjdk-21-21.0.12.1+1/test/lib/jdk/test/lib/security/XMLUtils.java --- openjdk-21-21.0.12+8/test/lib/jdk/test/lib/security/XMLUtils.java 2026-07-13 06:55:55.000000000 +0000 +++ openjdk-21-21.0.12.1+1/test/lib/jdk/test/lib/security/XMLUtils.java 2026-08-07 11:31:48.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -187,6 +187,7 @@ public static class Signer { + private String baseURI = null; final PrivateKey privateKey; // signer key, never null X509Certificate cert; // certificate, optional @@ -253,6 +254,11 @@ return this; } + public Signer baseURI(String base) { + this.baseURI = base; + return this; + } + // Signs different sources // Signs an XML file in detached mode @@ -341,6 +347,9 @@ for (var e : props.entrySet()) { ctxt.setProperty(e.getKey(), e.getValue()); } + if (baseURI != null) { + ctxt.setBaseURI(baseURI); + } return ctxt; }