diff --git a/jdk/src/java.base/share/classes/java/net/InetAddress.java b/jdk/src/java.base/share/classes/java/net/InetAddress.java index 95b38875d9c..d4815f486df 100644 --- a/jdk/src/java.base/share/classes/java/net/InetAddress.java +++ b/jdk/src/java.base/share/classes/java/net/InetAddress.java @@ -72,10 +72,13 @@ import sun.net.util.IPAddressUtil; * *

Address types

* - *
+ *
* + * + * + * * - * + * * - * + * * * - *
Description of unicast and multicast address types
Address TypeDescription
unicast
unicastAn identifier for a single interface. A packet sent to * a unicast address is delivered to the interface identified by * that address. @@ -94,12 +97,12 @@ import sun.net.util.IPAddressUtil; * IP address loops around and becomes IP input on the local * host. This address is often used when testing a * client.
multicast
multicastAn identifier for a set of interfaces (typically belonging * to different nodes). A packet sent to a multicast address is * delivered to all interfaces identified by that address.
+ * * *

IP address scope

* @@ -163,8 +166,7 @@ import sun.net.util.IPAddressUtil; *

Two Java security properties control the TTL values used for * positive and negative host name resolution caching: * - *

- *
+ *
*
networkaddress.cache.ttl
*
Indicates the caching policy for successful name lookups from * the name service. The value is specified as an integer to indicate @@ -183,7 +185,6 @@ import sun.net.util.IPAddressUtil; * A value of -1 indicates "cache forever". *
*
- *
* * @author Chris Warth * @see java.net.InetAddress#getByAddress(byte[]) diff --git a/jdk/src/java.base/share/classes/java/net/URI.java b/jdk/src/java.base/share/classes/java/net/URI.java index eab652cbbf5..5199a3dd2bb 100644 --- a/jdk/src/java.base/share/classes/java/net/URI.java +++ b/jdk/src/java.base/share/classes/java/net/URI.java @@ -132,23 +132,23 @@ import java.lang.NullPointerException; // for javadoc * *

All told, then, a URI instance has the following nine components: * - *

+ *
* * - * + * * - * - * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * + * + * * - *
Describes the components of a URI:scheme,scheme-specific-part,authority,user-info,host,port,path,query,fragment
ComponentType
ComponentType
scheme{@code String}
scheme-specific-part    {@code String}
authority{@code String}
user-info{@code String}
host{@code String}
port{@code int}
path{@code String}
query{@code String}
fragment{@code String}
scheme{@code String}
scheme-specific-part{@code String}
authority{@code String}
user-info{@code String}
host{@code String}
port{@code int}
path{@code String}
query{@code String}
fragment{@code String}
+ * * * In a given instance any particular component is either undefined or * defined with a distinct value. Undefined string components are @@ -253,32 +253,35 @@ import java.lang.NullPointerException; // for javadoc * which are taken from that specification, are used below to describe these * constraints: * - *
+ *
* - * - * + * + * + * + * + * * - * + * * - * + * * - * + * * - * + * * - * + * * - * + * * - * + * * * - *
Describes categories alpha,digit,alphanum,unreserved,punct,reserved,escaped,and other
alpha
CategoryDescription
alphaThe US-ASCII alphabetic characters, * {@code 'A'} through {@code 'Z'} * and {@code 'a'} through {@code 'z'}
digit
digitThe US-ASCII decimal digit characters, * {@code '0'} through {@code '9'}
alphanum
alphanumAll alpha and digit characters
unreserved    
unreservedAll alphanum characters together with those in the string * {@code "_-!.~'()*"}
punct
punctThe characters in the string {@code ",;:$&+="}
reserved
reservedAll punct characters together with those in the string * {@code "?/[]@"}
escaped
escapedEscaped octets, that is, triplets consisting of the percent * character ({@code '%'}) followed by two hexadecimal digits * ({@code '0'}-{@code '9'}, {@code 'A'}-{@code 'F'}, and * {@code 'a'}-{@code 'f'})
other
otherThe Unicode characters that are not in the US-ASCII character set, * are not control characters (according to the {@link * java.lang.Character#isISOControl(char) Character.isISOControl} @@ -287,7 +290,7 @@ import java.lang.NullPointerException; // for javadoc * method)  (Deviation from RFC 2396, which is * limited to US-ASCII)
+ * * *

The set of all legal URI characters consists of * the unreserved, reserved, escaped, and other diff --git a/jdk/src/java.base/share/classes/java/net/URLConnection.java b/jdk/src/java.base/share/classes/java/net/URLConnection.java index e54a0b1a126..a5c30df3d4b 100644 --- a/jdk/src/java.base/share/classes/java/net/URLConnection.java +++ b/jdk/src/java.base/share/classes/java/net/URLConnection.java @@ -51,31 +51,16 @@ import sun.security.action.GetPropertyAction; * The abstract class {@code URLConnection} is the superclass * of all classes that represent a communications link between the * application and a URL. Instances of this class can be used both to - * read from and to write to the resource referenced by the URL. In - * general, creating a connection to a URL is a multistep process: - * - *

- * - * - * - * - * - * - * - * - * - *
Describes the process of creating a connection to a URL: openConnection() and connect() over time.
{@code openConnection()}{@code connect()}
Manipulate parameters that affect the connection to the remote - * resource.Interact with the resource; query header fields and - * contents.
- * ----------------------------> - *
time
+ * read from and to write to the resource referenced by the URL. * + *

+ * In general, creating a connection to a URL is a multistep process: *

    *
  1. The connection object is created by invoking the - * {@code openConnection} method on a URL. + * {@link URL#openConnection() openConnection} method on a URL. *
  2. The setup parameters and general request properties are manipulated. *
  3. The actual connection to the remote object is made, using the - * {@code connect} method. + * {@link #connect() connect} method. *
  4. The remote object becomes available. The header fields and the contents * of the remote object can be accessed. *
diff --git a/jdk/src/java.base/share/classes/java/net/URLPermission.java b/jdk/src/java.base/share/classes/java/net/URLPermission.java index 43a8bd43066..31441749659 100644 --- a/jdk/src/java.base/share/classes/java/net/URLPermission.java +++ b/jdk/src/java.base/share/classes/java/net/URLPermission.java @@ -72,22 +72,22 @@ import java.security.Permission; * separated by '/' characters. path may also be empty. The path is specified * in a similar way to the path in {@link java.io.FilePermission}. There are * three different ways as the following examples show: - * + *
* * - * + * * - * - * + * + * * * - * + * * * - * + * *
URL Examples
Example urlDescription
Example urlDescription
http://www.oracle.com/a/b/c.html
http://www.oracle.com/a/b/c.htmlA url which identifies a specific (single) resource
http://www.oracle.com/a/b/*
http://www.oracle.com/a/b/*The '*' character refers to all resources in the same "directory" - in * other words all resources with the same number of path components, and * which only differ in the final path component, represented by the '*'. *
http://www.oracle.com/a/b/-
http://www.oracle.com/a/b/-The '-' character refers to all resources recursively below the * preceding path (eg. http://www.oracle.com/a/b/c/d/e.html matches this * example). @@ -114,11 +114,12 @@ import java.security.Permission; * methods and permitted request headers of the permission (respectively). The two lists * are separated by a colon ':' character and elements of each list are comma separated. * Some examples are: - *
- *         "POST,GET,DELETE"
- *         "GET:X-Foo-Request,X-Bar-Request"
- *         "POST,GET:Header1,Header2"
- * 
+ *
    + *
  • "POST,GET,DELETE" + *
  • "GET:X-Foo-Request,X-Bar-Request" + *
  • "POST,GET:Header1,Header2" + *
+ *

* The first example specifies the methods: POST, GET and DELETE, but no request headers. * The second example specifies one request method and two headers. The third * example specifies two request methods, and two headers. @@ -253,16 +254,16 @@ public final class URLPermission extends Permission { * * * - * + * * - * - * - * - * - * - * - * - * + * + * + * + * + * + * + * + * * *
Examples of Path Matching
this's pathp's pathmatch
this's pathp's pathmatch
/a/b/a/byes
/a/b/*/a/b/cyes
/a/b/*/a/b/c/dno
/a/b/-/a/b/c/dyes
/a/b/-/a/b/c/d/eyes
/a/b/-/a/b/c/*yes
/a/b/*/a/b/c/-no
/a/b/a/byes
/a/b/*/a/b/cyes
/a/b/c/dno
/a/b/c/-no
/a/b/-/a/b/c/dyes
/a/b/c/d/eyes
/a/b/c/*yes
*/ diff --git a/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html b/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html index 003749ba7f2..acf653ebaed 100644 --- a/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -23,7 +23,7 @@ or visit www.oracle.com if you need additional information or have any questions. --> - + Networking Properties @@ -35,7 +35,7 @@ alter the mechanisms and behavior of the various classes of the java.net package. Some are checked only once at startup of the VM, and therefore are best set using the -D option of the java command, while others have a more dynamic nature and can also be changed using -the System.setProperty() API. +the System.setProperty() API. The purpose of this document is to list and detail all of these properties.

If there is no special note, a property value is checked every time it is used.

diff --git a/jdk/src/java.base/share/classes/java/security/DrbgParameters.java b/jdk/src/java.base/share/classes/java/security/DrbgParameters.java index 05723baa916..4dba498faf1 100644 --- a/jdk/src/java.base/share/classes/java/security/DrbgParameters.java +++ b/jdk/src/java.base/share/classes/java/security/DrbgParameters.java @@ -263,18 +263,18 @@ public class DrbgParameters { * Capability effective = ((DrbgParametes.Initiate) s.getParameters()) * .getCapability(); * - * + *
* * * - * - * + * + * * * - * - * - * - * + * + * + * + * * *
requested and effective capabilities
Requested ValuePossible Effective ValuesRequested ValuePossible Effective Values
NONENONE, RESEED_ONLY, PR_AND_RESEED
RESEED_ONLYRESEED_ONLY, PR_AND_RESEED
PR_AND_RESEEDPR_AND_RESEED
NONENONE, RESEED_ONLY, PR_AND_RESEED
RESEED_ONLYRESEED_ONLY, PR_AND_RESEED
PR_AND_RESEEDPR_AND_RESEED
*

diff --git a/jdk/src/java.base/share/classes/java/security/Provider.java b/jdk/src/java.base/share/classes/java/security/Provider.java index cf20bfec3ac..83ce4ff9aa2 100644 --- a/jdk/src/java.base/share/classes/java/security/Provider.java +++ b/jdk/src/java.base/share/classes/java/security/Provider.java @@ -61,19 +61,19 @@ import java.util.function.Function; * security framework. Services of this type cannot be added, removed, * or modified by applications. * The following attributes are automatically placed in each Provider object: - * + *
* * - * + * * - * - * + * + * * - * + * * - * + * * - * + * * * *
Attributes Automatically Placed in a Provider Object
NameValue
NameValue
{@code Provider.id name}
{@code Provider.id name}{@code String.valueOf(provider.getName())}
{@code Provider.id version}
{@code Provider.id version}{@code String.valueOf(provider.getVersionStr())}
{@code Provider.id info}
{@code Provider.id info}{@code String.valueOf(provider.getInfo())}
{@code Provider.id className}
{@code Provider.id className}{@code provider.getClass().getName()}
diff --git a/jdk/src/java.base/share/classes/java/security/cert/X509Extension.java b/jdk/src/java.base/share/classes/java/security/cert/X509Extension.java index b7533a2c697..b8aa25fb9ca 100644 --- a/jdk/src/java.base/share/classes/java/security/cert/X509Extension.java +++ b/jdk/src/java.base/share/classes/java/security/cert/X509Extension.java @@ -153,33 +153,33 @@ public interface X509Extension { * by periods. * *

For example:
- * + *
* * * - * - * + * + * * - * - * + * + * * - * + * * - * + * * - * + * * - * + * * - * + * * - * + * * - * + * * - * + * * - * + * * * *
Examples of OIDs and extension names
OID (Object Identifier)Extension Name
OID (Object Identifier)Extension Name
2.5.29.14
2.5.29.14SubjectKeyIdentifier
2.5.29.15
2.5.29.15KeyUsage
2.5.29.16
2.5.29.16PrivateKeyUsage
2.5.29.17
2.5.29.17SubjectAlternativeName
2.5.29.18
2.5.29.18IssuerAlternativeName
2.5.29.19
2.5.29.19BasicConstraints
2.5.29.30
2.5.29.30NameConstraints
2.5.29.33
2.5.29.33PolicyMappings
2.5.29.35
2.5.29.35AuthorityKeyIdentifier
2.5.29.36
2.5.29.36PolicyConstraints
diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java index 8c682a813b6..e618ef18a78 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -81,13 +81,24 @@ class ZipUtils { * Converts DOS time to Java time (number of milliseconds since epoch). */ public static long dosToJavaTime(long dtime) { - LocalDateTime ldt = LocalDateTime.of( - (int) (((dtime >> 25) & 0x7f) + 1980), - (int) ((dtime >> 21) & 0x0f), - (int) ((dtime >> 16) & 0x1f), - (int) ((dtime >> 11) & 0x1f), - (int) ((dtime >> 5) & 0x3f), - (int) ((dtime << 1) & 0x3e)); + int year; + int month; + int day; + int hour = (int) ((dtime >> 11) & 0x1f); + int minute = (int) ((dtime >> 5) & 0x3f); + int second = (int) ((dtime << 1) & 0x3e); + if ((dtime >> 16) == 0) { + // Interpret the 0 DOS date as 1979-11-30 for compatibility with + // other implementations. + year = 1979; + month = 11; + day = 30; + } else { + year = (int) (((dtime >> 25) & 0x7f) + 1980); + month = (int) ((dtime >> 21) & 0x0f); + day = (int) ((dtime >> 16) & 0x1f); + } + LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); } diff --git a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java index b765128cd85..3eec4282ba6 100644 --- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java @@ -1292,7 +1292,7 @@ public abstract class SSLEngine { * href="http://www.ietf.org/rfc/rfc7301.txt"> RFC 7301 , the * Application-Layer Protocol Negotiation (ALPN), can negotiate * application-level values between peers. - *

+ * * @implSpec * The implementation in this class throws * {@code UnsupportedOperationException} and performs no other action. @@ -1317,7 +1317,7 @@ public abstract class SSLEngine { * Like {@link #getHandshakeSession()}, * a connection may be in the middle of a handshake. The * application protocol may or may not yet be available. - *

+ * * @implSpec * The implementation in this class throws * {@code UnsupportedOperationException} and performs no other action. diff --git a/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java b/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java index 19b05f9de8a..1c3c947035e 100644 --- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java @@ -646,7 +646,7 @@ public class SSLParameters { * requested by the peer, the underlying protocol will determine what * action to take. (For example, ALPN will send a * {@code "no_application_protocol"} alert and terminate the connection.) - *

+ * * @implSpec * This method will make a copy of the {@code protocols} array. * diff --git a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java index 6f8e20d9538..ccaea95b167 100644 --- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSocket.java @@ -702,7 +702,7 @@ public abstract class SSLSocket extends Socket * href="http://www.ietf.org/rfc/rfc7301.txt"> RFC 7301 , the * Application-Layer Protocol Negotiation (ALPN), can negotiate * application-level values between peers. - *

+ * * @implSpec * The implementation in this class throws * {@code UnsupportedOperationException} and performs no other action. @@ -727,7 +727,7 @@ public abstract class SSLSocket extends Socket * Like {@link #getHandshakeSession()}, * a connection may be in the middle of a handshake. The * application protocol may or may not yet be available. - *

+ * * @implSpec * The implementation in this class throws * {@code UnsupportedOperationException} and performs no other action. diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java new file mode 100644 index 00000000000..07223e61f25 --- /dev/null +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.http; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; +import javax.net.ssl.SSLEngine; +import jdk.incubator.http.internal.common.ExceptionallyCloseable; + + +/** + * Asynchronous version of SSLConnection. + * + * There are two concrete implementations of this class: AsyncSSLConnection + * and AsyncSSLTunnelConnection. + * This abstraction is useful when downgrading from HTTP/2 to HTTP/1.1 over + * an SSL connection. See ExchangeImpl::get in the case where an ALPNException + * is thrown. + * + * Note: An AsyncSSLConnection wraps a PlainHttpConnection, while an + * AsyncSSLTunnelConnection wraps a PlainTunnelingConnection. + * If both these wrapped classes where made to inherit from a + * common abstraction then it might be possible to merge + * AsyncSSLConnection and AsyncSSLTunnelConnection back into + * a single class - and simply use different factory methods to + * create different wrappees, but this is left up for further cleanup. + * + */ +abstract class AbstractAsyncSSLConnection extends HttpConnection + implements AsyncConnection, ExceptionallyCloseable { + + + AbstractAsyncSSLConnection(InetSocketAddress addr, HttpClientImpl client) { + super(addr, client); + } + + abstract SSLEngine getEngine(); + abstract AsyncSSLDelegate sslDelegate(); + abstract HttpConnection plainConnection(); + abstract HttpConnection downgrade(); + + @Override + final boolean isSecure() { + return true; + } + + // Blocking read functions not used here + @Override + protected final ByteBuffer readImpl() throws IOException { + throw new UnsupportedOperationException("Not supported."); + } + + // whenReceivedResponse only used in HTTP/1.1 (Http1Exchange) + // AbstractAsyncSSLConnection is only used with HTTP/2 + @Override + final CompletableFuture whenReceivingResponse() { + throw new UnsupportedOperationException("Not supported."); + } + +} diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java index b1e6063951f..f6c095526ee 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java @@ -35,14 +35,12 @@ import java.util.function.Supplier; import javax.net.ssl.SSLEngine; import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.ExceptionallyCloseable; import jdk.incubator.http.internal.common.Utils; /** * Asynchronous version of SSLConnection. */ -class AsyncSSLConnection extends HttpConnection - implements AsyncConnection, ExceptionallyCloseable { +class AsyncSSLConnection extends AbstractAsyncSSLConnection { final AsyncSSLDelegate sslDelegate; final PlainHttpConnection plainConnection; @@ -61,15 +59,14 @@ class AsyncSSLConnection extends HttpConnection plainConnection.configureMode(mode); } - private CompletableFuture configureModeAsync(Void ignore) { - CompletableFuture cf = new CompletableFuture<>(); - try { - configureMode(Mode.ASYNC); - cf.complete(null); - } catch (Throwable t) { - cf.completeExceptionally(t); - } - return cf; + @Override + PlainHttpConnection plainConnection() { + return plainConnection; + } + + @Override + AsyncSSLDelegate sslDelegate() { + return sslDelegate; } @Override @@ -91,11 +88,6 @@ class AsyncSSLConnection extends HttpConnection return plainConnection.connected() && sslDelegate.connected(); } - @Override - boolean isSecure() { - return true; - } - @Override boolean isProxied() { return false; @@ -172,6 +164,7 @@ class AsyncSSLConnection extends HttpConnection plainConnection.channel().shutdownOutput(); } + @Override SSLEngine getEngine() { return sslDelegate.getEngine(); } @@ -184,18 +177,6 @@ class AsyncSSLConnection extends HttpConnection plainConnection.setAsyncCallbacks(sslDelegate::asyncReceive, errorReceiver, sslDelegate::getNetBuffer); } - // Blocking read functions not used here - - @Override - protected ByteBuffer readImpl() throws IOException { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - CompletableFuture whenReceivingResponse() { - throw new UnsupportedOperationException("Not supported."); - } - @Override public void startReading() { plainConnection.startReading(); @@ -206,4 +187,9 @@ class AsyncSSLConnection extends HttpConnection public void stopAsyncReading() { plainConnection.stopAsyncReading(); } + + @Override + SSLConnection downgrade() { + return new SSLConnection(this); + } } diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java new file mode 100644 index 00000000000..7afb87f5191 --- /dev/null +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2015, 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.http; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import jdk.incubator.http.internal.common.ByteBufferReference; +import jdk.incubator.http.internal.common.Utils; + +/** + * An SSL tunnel built on a Plain (CONNECT) TCP tunnel. + */ +class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection { + + final PlainTunnelingConnection plainConnection; + final AsyncSSLDelegate sslDelegate; + final String serverName; + + @Override + public void connect() throws IOException, InterruptedException { + plainConnection.connect(); + configureMode(Mode.ASYNC); + startReading(); + sslDelegate.connect(); + } + + @Override + boolean connected() { + return plainConnection.connected() && sslDelegate.connected(); + } + + @Override + public CompletableFuture connectAsync() { + throw new InternalError(); + } + + AsyncSSLTunnelConnection(InetSocketAddress addr, + HttpClientImpl client, + String[] alpn, + InetSocketAddress proxy) + { + super(addr, client); + this.serverName = Utils.getServerName(addr); + this.plainConnection = new PlainTunnelingConnection(addr, proxy, client); + this.sslDelegate = new AsyncSSLDelegate(plainConnection, client, alpn, serverName); + } + + @Override + synchronized void configureMode(Mode mode) throws IOException { + super.configureMode(mode); + plainConnection.configureMode(mode); + } + + @Override + SSLParameters sslParameters() { + return sslDelegate.getSSLParameters(); + } + + @Override + public String toString() { + return "AsyncSSLTunnelConnection: " + super.toString(); + } + + @Override + PlainTunnelingConnection plainConnection() { + return plainConnection; + } + + @Override + AsyncSSLDelegate sslDelegate() { + return sslDelegate; + } + + @Override + ConnectionPool.CacheKey cacheKey() { + return ConnectionPool.cacheKey(address, plainConnection.proxyAddr); + } + + @Override + long write(ByteBuffer[] buffers, int start, int number) throws IOException { + //debugPrint("Send", buffers, start, number); + ByteBuffer[] bufs = Utils.reduce(buffers, start, number); + long n = Utils.remaining(bufs); + sslDelegate.writeAsync(ByteBufferReference.toReferences(bufs)); + sslDelegate.flushAsync(); + return n; + } + + @Override + long write(ByteBuffer buffer) throws IOException { + //debugPrint("Send", buffer); + long n = buffer.remaining(); + sslDelegate.writeAsync(ByteBufferReference.toReferences(buffer)); + sslDelegate.flushAsync(); + return n; + } + + @Override + public void writeAsync(ByteBufferReference[] buffers) throws IOException { + sslDelegate.writeAsync(buffers); + } + + @Override + public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { + sslDelegate.writeAsyncUnordered(buffers); + } + + @Override + public void flushAsync() throws IOException { + sslDelegate.flushAsync(); + } + + @Override + public void close() { + Utils.close(sslDelegate, plainConnection.channel()); + } + + @Override + void shutdownInput() throws IOException { + plainConnection.channel().shutdownInput(); + } + + @Override + void shutdownOutput() throws IOException { + plainConnection.channel().shutdownOutput(); + } + + @Override + SocketChannel channel() { + return plainConnection.channel(); + } + + @Override + boolean isProxied() { + return true; + } + + @Override + public void setAsyncCallbacks(Consumer asyncReceiver, + Consumer errorReceiver, + Supplier readBufferSupplier) { + sslDelegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier); + plainConnection.setAsyncCallbacks(sslDelegate::asyncReceive, errorReceiver, sslDelegate::getNetBuffer); + } + + @Override + public void startReading() { + plainConnection.startReading(); + sslDelegate.startReading(); + } + + @Override + public void stopAsyncReading() { + plainConnection.stopAsyncReading(); + } + + @Override + public void enableCallback() { + sslDelegate.enableCallback(); + } + + @Override + public void closeExceptionally(Throwable cause) throws IOException { + Utils.close(cause, sslDelegate, plainConnection.channel()); + } + + @Override + SSLEngine getEngine() { + return sslDelegate.getEngine(); + } + + @Override + SSLTunnelConnection downgrade() { + return new SSLTunnelConnection(this); + } +} diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java index 27b6c1964f4..3d41179d903 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java @@ -82,9 +82,9 @@ abstract class ExchangeImpl { c = c2.getConnectionFor(request); } catch (Http2Connection.ALPNException e) { // failed to negotiate "h2" - AsyncSSLConnection as = e.getConnection(); + AbstractAsyncSSLConnection as = e.getConnection(); as.stopAsyncReading(); - SSLConnection sslc = new SSLConnection(as); + HttpConnection sslc = as.downgrade(); ExchangeImpl ex = new Http1Exchange<>(exchange, sslc); return ex; } diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java index 275325f8b29..8e42022c286 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java @@ -211,12 +211,13 @@ class Http2Connection { this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE)); this.windowUpdater = new ConnectionWindowUpdateSender(this, client.getReceiveBufferSize()); } - /** - * Case 1) Create from upgraded HTTP/1.1 connection. - * Is ready to use. Will not be SSL. exchange is the Exchange - * that initiated the connection, whose response will be delivered - * on a Stream. - */ + + /** + * Case 1) Create from upgraded HTTP/1.1 connection. + * Is ready to use. Will not be SSL. exchange is the Exchange + * that initiated the connection, whose response will be delivered + * on a Stream. + */ Http2Connection(HttpConnection connection, Http2ClientImpl client2, Exchange exchange, @@ -280,7 +281,7 @@ class Http2Connection { * Throws an IOException if h2 was not negotiated */ private void checkSSLConfig() throws IOException { - AsyncSSLConnection aconn = (AsyncSSLConnection)connection; + AbstractAsyncSSLConnection aconn = (AbstractAsyncSSLConnection)connection; SSLEngine engine = aconn.getEngine(); String alpn = engine.getApplicationProtocol(); if (alpn == null || !alpn.equals("h2")) { @@ -906,14 +907,14 @@ class Http2Connection { */ static final class ALPNException extends IOException { private static final long serialVersionUID = 23138275393635783L; - final AsyncSSLConnection connection; + final AbstractAsyncSSLConnection connection; - ALPNException(String msg, AsyncSSLConnection connection) { + ALPNException(String msg, AbstractAsyncSSLConnection connection) { super(msg); this.connection = connection; } - AsyncSSLConnection getConnection() { + AbstractAsyncSSLConnection getConnection() { return connection; } } diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java index a520390ba2d..d81d3950374 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java @@ -34,7 +34,6 @@ import java.nio.channels.SocketChannel; import java.util.concurrent.CompletableFuture; import jdk.incubator.http.internal.common.ByteBufferReference; -import jdk.incubator.http.internal.common.Utils; /** * Wraps socket channel layer and takes care of SSL also. @@ -136,7 +135,11 @@ abstract class HttpConnection implements Closeable { String[] alpn, boolean isHttp2, HttpClientImpl client) { if (proxy != null) { - return new SSLTunnelConnection(addr, client, proxy); + if (!isHttp2) { + return new SSLTunnelConnection(addr, client, proxy); + } else { + return new AsyncSSLTunnelConnection(addr, client, alpn, proxy); + } } else if (!isHttp2) { return new SSLConnection(addr, client, alpn); } else { diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java index d133df1e250..c167c53f2b3 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java @@ -34,12 +34,15 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Supplier; /** * A plain text socket tunnel through a proxy. Uses "CONNECT" but does not - * encrypt. Used by WebSocket. Subclassed in SSLTunnelConnection for encryption. + * encrypt. Used by WebSocket, as well as HTTP over SSL + Proxy. + * Wrapped in SSLTunnelConnection or AsyncSSLTunnelConnection for encryption. */ -class PlainTunnelingConnection extends HttpConnection { +class PlainTunnelingConnection extends HttpConnection implements AsyncConnection { final PlainHttpConnection delegate; protected final InetSocketAddress proxyAddr; @@ -116,17 +119,17 @@ class PlainTunnelingConnection extends HttpConnection { } @Override - void writeAsync(ByteBufferReference[] buffers) throws IOException { + public void writeAsync(ByteBufferReference[] buffers) throws IOException { delegate.writeAsync(buffers); } @Override - void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { + public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { delegate.writeAsyncUnordered(buffers); } @Override - void flushAsync() throws IOException { + public void flushAsync() throws IOException { delegate.flushAsync(); } @@ -165,4 +168,32 @@ class PlainTunnelingConnection extends HttpConnection { boolean isProxied() { return true; } + + @Override + public void setAsyncCallbacks(Consumer asyncReceiver, + Consumer errorReceiver, + Supplier readBufferSupplier) { + delegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier); + } + + @Override + public void startReading() { + delegate.startReading(); + } + + @Override + public void stopAsyncReading() { + delegate.stopAsyncReading(); + } + + @Override + public void enableCallback() { + delegate.enableCallback(); + } + + @Override + synchronized void configureMode(Mode mode) throws IOException { + super.configureMode(mode); + delegate.configureMode(mode); + } } diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java index 790d98b7fa0..9eb6a37e250 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java @@ -77,8 +77,8 @@ class SSLConnection extends HttpConnection { */ SSLConnection(AsyncSSLConnection c) { super(c.address, c.client); - this.delegate = c.plainConnection; - AsyncSSLDelegate adel = c.sslDelegate; + this.delegate = c.plainConnection(); + AsyncSSLDelegate adel = c.sslDelegate(); this.sslDelegate = new SSLDelegate(adel.engine, delegate.channel(), client, adel.serverName); this.alpn = adel.alpn; this.serverName = adel.serverName; diff --git a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java index fcd71728b5b..d5cade109b7 100644 --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java @@ -85,6 +85,19 @@ class SSLTunnelConnection extends HttpConnection { delegate = new PlainTunnelingConnection(addr, proxy, client); } + /** + * Create an SSLTunnelConnection from an existing connected AsyncSSLTunnelConnection. + * Used when downgrading from HTTP/2 to HTTP/1.1 + */ + SSLTunnelConnection(AsyncSSLTunnelConnection c) { + super(c.address, c.client); + this.delegate = c.plainConnection(); + AsyncSSLDelegate adel = c.sslDelegate(); + this.sslDelegate = new SSLDelegate(adel.engine, delegate.channel(), client, adel.serverName); + this.serverName = adel.serverName; + connected = c.connected(); + } + @Override SSLParameters sslParameters() { return sslDelegate.getSSLParameters(); diff --git a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java index b87a63d87cb..3c45090dbe2 100644 --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipUtils.java @@ -106,13 +106,24 @@ class ZipUtils { * Converts DOS time to Java time (number of milliseconds since epoch). */ public static long dosToJavaTime(long dtime) { - LocalDateTime ldt = LocalDateTime.of( - (int) (((dtime >> 25) & 0x7f) + 1980), - (int) ((dtime >> 21) & 0x0f), - (int) ((dtime >> 16) & 0x1f), - (int) ((dtime >> 11) & 0x1f), - (int) ((dtime >> 5) & 0x3f), - (int) ((dtime << 1) & 0x3e)); + int year; + int month; + int day; + int hour = (int) ((dtime >> 11) & 0x1f); + int minute = (int) ((dtime >> 5) & 0x3f); + int second = (int) ((dtime << 1) & 0x3e); + if ((dtime >> 16) == 0) { + // Interpret the 0 DOS date as 1979-11-30 for compatibility with + // other implementations. + year = 1979; + month = 11; + day = 30; + } else { + year = (int) (((dtime >> 25) & 0x7f) + 1980); + month = (int) ((dtime >> 21) & 0x0f); + day = (int) ((dtime >> 16) & 0x1f); + } + LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second); return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond( ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS); } diff --git a/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.java b/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.java index 0db61d6db50..fc3945a7827 100644 --- a/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.java +++ b/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.java @@ -21,17 +21,6 @@ * questions. */ -/** - * @test - * @key headful - * @bug 6193279 - * @summary REGRESSION: AppletViewer throws IOException when path is encoded URL - * @author Dmitry Cherepanov: area=appletviewer - * @run compile IOExceptionIfEncodedURLTest.java - * @run main IOExceptionIfEncodedURLTest - * @run shell IOExceptionIfEncodedURLTest.sh - */ - import java.applet.Applet; import sun.net.www.ParseUtil; import java.io.File; diff --git a/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.sh b/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.sh index 619b57e0632..ddc86922a83 100644 --- a/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.sh +++ b/jdk/test/java/awt/appletviewer/IOExceptionIfEncodedURLTest/IOExceptionIfEncodedURLTest.sh @@ -25,7 +25,7 @@ # # @test IOExceptionIfEncodedURLTest.sh # @key headful -# @bug 6193279 6619458 8137087 +# @bug 6193279 6619458 8137087 8186259 # @summary REGRESSION: AppletViewer throws IOException when path is encoded URL # @author Dmitry Cherepanov: area=appletviewer # @modules java.base/sun.net.www diff --git a/jdk/test/java/net/httpclient/ProxyTest.java b/jdk/test/java/net/httpclient/ProxyTest.java index 8dc90dfb1a1..f6590d89f17 100644 --- a/jdk/test/java/net/httpclient/ProxyTest.java +++ b/jdk/test/java/net/httpclient/ProxyTest.java @@ -56,9 +56,12 @@ import jdk.testlibrary.SimpleSSLContext; /** * @test - * @bug 8185852 - * @summary verifies that passing a proxy with an unresolved address does - * not cause java.nio.channels.UnresolvedAddressException + * @bug 8185852 8181422 + * @summary Verifies that passing a proxy with an unresolved address does + * not cause java.nio.channels.UnresolvedAddressException. + * Verifies that downgrading from HTTP/2 to HTTP/1.1 works through + * an SSL Tunnel connection when the client is HTTP/2 and the server + * and proxy are HTTP/1.1 * @modules jdk.incubator.httpclient * @library /lib/testlibrary/ * @build jdk.testlibrary.SimpleSSLContext ProxyTest @@ -111,7 +114,7 @@ public class ProxyTest { server.start(); try { test(server, HttpClient.Version.HTTP_1_1); - // test(server, HttpClient.Version.HTTP_2); + test(server, HttpClient.Version.HTTP_2); } finally { server.stop(0); System.out.println("Server stopped"); diff --git a/jdk/test/java/net/httpclient/http2/ProxyTest2.java b/jdk/test/java/net/httpclient/http2/ProxyTest2.java new file mode 100644 index 00000000000..3a06bd304e1 --- /dev/null +++ b/jdk/test/java/net/httpclient/http2/ProxyTest2.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsParameters; +import com.sun.net.httpserver.HttpsServer; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.testlibrary.SimpleSSLContext; +import java.util.concurrent.*; + +/** + * @test + * @bug 8181422 + * @summary Verifies that you can access an HTTP/2 server over HTTPS by + * tunnelling through an HTTP/1.1 proxy. + * @modules jdk.incubator.httpclient + * @library /lib/testlibrary server + * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common + * jdk.incubator.httpclient/jdk.incubator.http.internal.frame + * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack + * @build jdk.testlibrary.SimpleSSLContext ProxyTest2 + * @run main/othervm ProxyTest2 + * @author danielfuchs + */ +public class ProxyTest2 { + + static { + try { + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + SSLContext.setDefault(new SimpleSSLContext().get()); + } catch (IOException ex) { + throw new ExceptionInInitializerError(ex); + } + } + + static final String RESPONSE = "

Hello World!"; + static final String PATH = "/foo/"; + + static Http2TestServer createHttpsServer(ExecutorService exec) throws Exception { + Http2TestServer server = new Http2TestServer(true, 0, exec, SSLContext.getDefault()); + server.addHandler(new Http2Handler() { + @Override + public void handle(Http2TestExchange he) throws IOException { + he.getResponseHeaders().addHeader("encoding", "UTF-8"); + he.sendResponseHeaders(200, RESPONSE.length()); + he.getResponseBody().write(RESPONSE.getBytes(StandardCharsets.UTF_8)); + he.close(); + } + }, PATH); + + return server; + } + + public static void main(String[] args) + throws Exception + { + ExecutorService exec = Executors.newCachedThreadPool(); + Http2TestServer server = createHttpsServer(exec); + server.start(); + try { + // Http2TestServer over HTTPS does not support HTTP/1.1 + // => only test with a HTTP/2 client + test(server, HttpClient.Version.HTTP_2); + } finally { + server.stop(); + exec.shutdown(); + System.out.println("Server stopped"); + } + } + + public static void test(Http2TestServer server, HttpClient.Version version) + throws Exception + { + System.out.println("Server is: " + server.getAddress().toString()); + URI uri = new URI("https://localhost:" + server.getAddress().getPort() + PATH + "x"); + TunnelingProxy proxy = new TunnelingProxy(server); + proxy.start(); + try { + System.out.println("Proxy started"); + Proxy p = new Proxy(Proxy.Type.HTTP, + InetSocketAddress.createUnresolved("localhost", proxy.getAddress().getPort())); + System.out.println("Setting up request with HttpClient for version: " + + version.name() + "URI=" + uri); + ProxySelector ps = ProxySelector.of( + InetSocketAddress.createUnresolved("localhost", proxy.getAddress().getPort())); + HttpClient client = HttpClient.newBuilder() + .version(version) + .proxy(ps) + .build(); + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + .GET() + .build(); + + System.out.println("Sending request with HttpClient"); + HttpResponse response + = client.send(request, HttpResponse.BodyHandler.asString()); + System.out.println("Got response"); + String resp = response.body(); + System.out.println("Received: " + resp); + if (!RESPONSE.equals(resp)) { + throw new AssertionError("Unexpected response"); + } + } finally { + System.out.println("Stopping proxy"); + proxy.stop(); + System.out.println("Proxy stopped"); + } + } + + static class TunnelingProxy { + final Thread accept; + final ServerSocket ss; + final boolean DEBUG = false; + final Http2TestServer serverImpl; + TunnelingProxy(Http2TestServer serverImpl) throws IOException { + this.serverImpl = serverImpl; + ss = new ServerSocket(); + accept = new Thread(this::accept); + } + + void start() throws IOException { + ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + accept.start(); + } + + // Pipe the input stream to the output stream. + private synchronized Thread pipe(InputStream is, OutputStream os, char tag) { + return new Thread("TunnelPipe("+tag+")") { + @Override + public void run() { + try { + try { + int c; + while ((c = is.read()) != -1) { + os.write(c); + os.flush(); + // if DEBUG prints a + or a - for each transferred + // character. + if (DEBUG) System.out.print(tag); + } + is.close(); + } finally { + os.close(); + } + } catch (IOException ex) { + if (DEBUG) ex.printStackTrace(System.out); + } + } + }; + } + + public InetSocketAddress getAddress() { + 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.codePointAt(b.length() -1) == '\r') { + b.delete(b.length() -1, b.length()); + } + return b.toString(); + } + + public void accept() { + Socket clientConnection = null; + try { + while (true) { + System.out.println("Tunnel: Waiting for client"); + Socket previous = clientConnection; + try { + clientConnection = ss.accept(); + } catch (IOException io) { + if (DEBUG) io.printStackTrace(System.out); + break; + } finally { + // we have only 1 client at a time, so it is safe + // to close the previous connection here + if (previous != null) previous.close(); + } + System.out.println("Tunnel: Client accepted"); + Socket targetConnection = null; + InputStream ccis = clientConnection.getInputStream(); + OutputStream ccos = clientConnection.getOutputStream(); + Writer w = new OutputStreamWriter(ccos, "UTF-8"); + PrintWriter pw = new PrintWriter(w); + System.out.println("Tunnel: Reading request line"); + String requestLine = readLine(ccis); + System.out.println("Tunnel: Request status 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))); + } + + // Open target connection + 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(); + } else { + // This should not happen. + throw new IOException("Tunnel: Unexpected status line: " + + requestLine); + } + + // Pipe the input stream of the client connection to the + // output stream of the target connection and conversely. + // Now the client and target will just talk to each other. + System.out.println("Tunnel: Starting tunnel pipes"); + Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+'); + Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-'); + t1.start(); + t2.start(); + + // We have only 1 client... wait until it has finished before + // accepting a new connection request. + // System.out.println("Tunnel: Waiting for pipes to close"); + t1.join(); + t2.join(); + System.out.println("Tunnel: Done - waiting for next client"); + } + } catch (Throwable ex) { + try { + ss.close(); + } catch (IOException ex1) { + ex.addSuppressed(ex1); + } + ex.printStackTrace(System.err); + } + } + + void stop() throws IOException { + ss.close(); + } + + } + + static class Configurator extends HttpsConfigurator { + public Configurator(SSLContext ctx) { + super(ctx); + } + + @Override + public void configure (HttpsParameters params) { + params.setSSLParameters (getSSLContext().getSupportedSSLParameters()); + } + } + +} diff --git a/jdk/test/java/net/httpclient/http2/server/Http2TestServer.java b/jdk/test/java/net/httpclient/http2/server/Http2TestServer.java index 4a56e9beb4f..6889cf7aec8 100644 --- a/jdk/test/java/net/httpclient/http2/server/Http2TestServer.java +++ b/jdk/test/java/net/httpclient/http2/server/Http2TestServer.java @@ -201,7 +201,17 @@ public class Http2TestServer implements AutoCloseable { InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress(); Http2TestServerConnection c = new Http2TestServerConnection(this, socket); connections.put(addr, c); - c.run(); + try { + c.run(); + } catch(Throwable e) { + // we should not reach here, but if we do + // the connection might not have been closed + // and if so then the client might wait + // forever. + connections.remove(addr, c); + c.close(); + throw e; + } } } catch (Throwable e) { if (!stopping) { diff --git a/jdk/test/java/net/httpclient/http2/server/Http2TestServerConnection.java b/jdk/test/java/net/httpclient/http2/server/Http2TestServerConnection.java index 57698291ef1..5645c200692 100644 --- a/jdk/test/java/net/httpclient/http2/server/Http2TestServerConnection.java +++ b/jdk/test/java/net/httpclient/http2/server/Http2TestServerConnection.java @@ -133,10 +133,10 @@ public class Http2TestServerConnection { } void close() { + stopping = true; streams.forEach((i, q) -> { q.close(); }); - stopping = true; try { socket.close(); // TODO: put a reset on each stream @@ -557,7 +557,14 @@ public class Http2TestServerConnection { void writeLoop() { try { while (!stopping) { - Http2Frame frame = outputQ.take(); + Http2Frame frame; + try { + frame = outputQ.take(); + } catch(IOException x) { + if (stopping && x.getCause() instanceof InterruptedException) { + break; + } else throw x; + } if (frame instanceof ResponseHeaders) { ResponseHeaders rh = (ResponseHeaders)frame; HeadersFrame hf = new HeadersFrame(rh.streamid(), rh.getFlags(), encodeHeaders(rh.headers)); diff --git a/jdk/test/java/util/zip/ZipFile/ZeroDate.java b/jdk/test/java/util/zip/ZipFile/ZeroDate.java new file mode 100644 index 00000000000..aef545002e0 --- /dev/null +++ b/jdk/test/java/util/zip/ZipFile/ZeroDate.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static java.util.zip.ZipFile.CENOFF; +import static java.util.zip.ZipFile.CENTIM; +import static java.util.zip.ZipFile.ENDHDR; +import static java.util.zip.ZipFile.ENDOFF; +import static java.util.zip.ZipFile.LOCTIM; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +/* @test + * @bug 8184940 + * @summary JDK 9 rejects zip files where the modified day or month is 0 + * @author Liam Miller-Cushon + */ +public class ZeroDate { + + public static void main(String[] args) throws Exception { + // create a zip file, and read it in as a byte array + Path path = Files.createTempFile("bad", ".zip"); + try (OutputStream os = Files.newOutputStream(path); + ZipOutputStream zos = new ZipOutputStream(os)) { + ZipEntry e = new ZipEntry("x"); + zos.putNextEntry(e); + zos.write((int) 'x'); + } + int len = (int) Files.size(path); + byte[] data = new byte[len]; + try (InputStream is = Files.newInputStream(path)) { + is.read(data); + } + Files.delete(path); + + // year, month, day are zero + testDate(data.clone(), 0, LocalDate.of(1979, 11, 30)); + // only year is zero + testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5)); + } + + private static void testDate(byte[] data, int date, LocalDate expected) throws IOException { + // set the datetime + int endpos = data.length - ENDHDR; + int cenpos = u16(data, endpos + ENDOFF); + int locpos = u16(data, cenpos + CENOFF); + writeU32(data, cenpos + CENTIM, date); + writeU32(data, locpos + LOCTIM, date); + + // ensure that the archive is still readable, and the date is 1979-11-30 + Path path = Files.createTempFile("out", ".zip"); + try (OutputStream os = Files.newOutputStream(path)) { + os.write(data); + } + try (ZipFile zf = new ZipFile(path.toFile())) { + ZipEntry ze = zf.entries().nextElement(); + Instant actualInstant = ze.getLastModifiedTime().toInstant(); + Instant expectedInstant = + expected.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + if (!actualInstant.equals(expectedInstant)) { + throw new AssertionError( + String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); + } + } finally { + Files.delete(path); + } + } + + static int u8(byte[] data, int offset) { + return data[offset] & 0xff; + } + + static int u16(byte[] data, int offset) { + return u8(data, offset) + (u8(data, offset + 1) << 8); + } + + private static void writeU32(byte[] data, int pos, int value) { + data[pos] = (byte) (value & 0xff); + data[pos + 1] = (byte) ((value >> 8) & 0xff); + data[pos + 2] = (byte) ((value >> 16) & 0xff); + data[pos + 3] = (byte) ((value >> 24) & 0xff); + } +} diff --git a/jdk/test/jdk/nio/zipfs/ZeroDate.java b/jdk/test/jdk/nio/zipfs/ZeroDate.java new file mode 100644 index 00000000000..1a65c9b39b6 --- /dev/null +++ b/jdk/test/jdk/nio/zipfs/ZeroDate.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static java.util.zip.ZipFile.CENOFF; +import static java.util.zip.ZipFile.CENTIM; +import static java.util.zip.ZipFile.ENDHDR; +import static java.util.zip.ZipFile.ENDOFF; +import static java.util.zip.ZipFile.LOCTIM; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Collections; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/* @test + * @bug 8184940 + * @summary JDK 9 rejects zip files where the modified day or month is 0 + * @author Liam Miller-Cushon + */ +public class ZeroDate { + + public static void main(String[] args) throws Exception { + // create a zip file, and read it in as a byte array + Path path = Files.createTempFile("bad", ".zip"); + try (OutputStream os = Files.newOutputStream(path); + ZipOutputStream zos = new ZipOutputStream(os)) { + ZipEntry e = new ZipEntry("x"); + zos.putNextEntry(e); + zos.write((int) 'x'); + } + int len = (int) Files.size(path); + byte[] data = new byte[len]; + try (InputStream is = Files.newInputStream(path)) { + is.read(data); + } + Files.delete(path); + + // year, month, day are zero + testDate(data.clone(), 0, LocalDate.of(1979, 11, 30)); + // only year is zero + testDate(data.clone(), 0 << 25 | 4 << 21 | 5 << 16, LocalDate.of(1980, 4, 5)); + } + + private static void testDate(byte[] data, int date, LocalDate expected) throws IOException { + // set the datetime + int endpos = data.length - ENDHDR; + int cenpos = u16(data, endpos + ENDOFF); + int locpos = u16(data, cenpos + CENOFF); + writeU32(data, cenpos + CENTIM, date); + writeU32(data, locpos + LOCTIM, date); + + // ensure that the archive is still readable, and the date is 1979-11-30 + Path path = Files.createTempFile("out", ".zip"); + try (OutputStream os = Files.newOutputStream(path)) { + os.write(data); + } + URI uri = URI.create("jar:file://" + path.toAbsolutePath()); + try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path entry = fs.getPath("x"); + Instant actualInstant = + Files.readAttributes(entry, BasicFileAttributes.class) + .lastModifiedTime() + .toInstant(); + Instant expectedInstant = + expected.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(); + if (!actualInstant.equals(expectedInstant)) { + throw new AssertionError( + String.format("actual: %s, expected: %s", actualInstant, expectedInstant)); + } + } finally { + Files.delete(path); + } + } + + static int u8(byte[] data, int offset) { + return data[offset] & 0xff; + } + + static int u16(byte[] data, int offset) { + return u8(data, offset) + (u8(data, offset + 1) << 8); + } + + private static void writeU32(byte[] data, int pos, int value) { + data[pos] = (byte) (value & 0xff); + data[pos + 1] = (byte) ((value >> 8) & 0xff); + data[pos + 2] = (byte) ((value >> 16) & 0xff); + data[pos + 3] = (byte) ((value >> 24) & 0xff); + } +} diff --git a/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java b/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java index 38d37966f23..535d32d5310 100644 --- a/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java +++ b/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, 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 @@ -55,6 +55,9 @@ public class SmallPrimeExponentP { CertAndKeyGen ckg = new CertAndKeyGen("RSA", "SHA1withRSA"); ckg.setRandom(new MySecureRandom(seed)); + String alias = "anything"; + int count = 0; + boolean see63 = false; boolean see65 = false; while (!see63 || !see65) { @@ -78,12 +81,19 @@ public class SmallPrimeExponentP { see65 = true; } } - ks.setKeyEntry("anything", k, null, new X509Certificate[]{ + ks.setKeyEntry(alias, k, null, new X509Certificate[]{ ckg.getSelfCertificate(new X500Name("CN=Me"), 1000) }); + count++; } } - ks.store(null, null); + + // Because of JDK-8185844, it has to reload the key store after + // deleting an entry. + for (int i = 0; i < count; i++) { + ks.deleteEntry(alias); + ks.load(null, null); + } } static class MySecureRandom extends SecureRandom { diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java b/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java new file mode 100644 index 00000000000..a285a0257c5 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java @@ -0,0 +1,1183 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary This test is used to verify the compatibility on jarsigner cross + * different JDK releases. It also can be used to check jar signing (w/ + * and w/o TSA) and verifying on some specific key algorithms and digest + * algorithms. + * Note that, this is a manual test. For more details about the test and + * its usages, please look through README. + * + * @modules java.base/sun.security.pkcs + * java.base/sun.security.timestamp + * java.base/sun.security.tools.keytool + * java.base/sun.security.util + * java.base/sun.security.x509 + * @library /test/lib /lib/testlibrary ../warnings + * @compile -source 1.6 -target 1.6 JdkUtils.java + * @run main/manual/othervm Compatibility + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.util.JarUtils; + +public class Compatibility { + + private static final String TEST_JAR_NAME = "test.jar"; + + private static final String TEST_SRC = System.getProperty("test.src"); + private static final String TEST_CLASSES = System.getProperty("test.classes"); + private static final String TEST_JDK = System.getProperty("test.jdk"); + private static final String TEST_JARSIGNER = jarsignerPath(TEST_JDK); + + private static final String PROXY_HOST = System.getProperty("proxyHost"); + private static final String PROXY_PORT = System.getProperty("proxyPort", "80"); + + // An alternative security properties file. + // The test provides a default one, which only contains two lines: + // jdk.certpath.disabledAlgorithms=MD2, MD5 + // jdk.jar.disabledAlgorithms=MD2, MD5 + private static final String JAVA_SECURITY = System.getProperty( + "javaSecurityFile", TEST_SRC + "/java.security"); + + private static final String PASSWORD = "testpass"; + private static final String KEYSTORE = "testKeystore"; + + private static final String RSA = "RSA"; + private static final String DSA = "DSA"; + private static final String EC = "EC"; + private static final String[] KEY_ALGORITHMS = new String[] { + RSA, + DSA, + EC}; + + private static final String SHA1 = "SHA-1"; + private static final String SHA256 = "SHA-256"; + private static final String SHA512 = "SHA-512"; + private static final String DEFAULT = "DEFAULT"; + private static final String[] DIGEST_ALGORITHMS = new String[] { + SHA1, + SHA256, + SHA512, + DEFAULT}; + + private static final boolean[] EXPIRED = new boolean[] { + false, + true}; + + private static final Calendar CALENDAR = Calendar.getInstance(); + private static final DateFormat DATE_FORMAT + = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + + // The certificate validity period in minutes. The default value is 1440 + // minutes, namely 1 day. + private static final int CERT_VALIDITY + = Integer.valueOf(System.getProperty("certValidity", "1440")); + static { + if (CERT_VALIDITY < 1 || CERT_VALIDITY > 1440) { + throw new RuntimeException( + "certValidity if out of range [1, 1440]: " + CERT_VALIDITY); + } + } + + // If true, an additional verifying will be triggered after all of + // valid certificates expire. The default value is false. + public static final boolean DELAY_VERIFY + = Boolean.valueOf(System.getProperty("delayVerify", "false")); + + private static long lastCertStartTime; + + private static DetailsOutputStream detailsOutput; + + public static void main(String[] args) throws Throwable { + // Backups stdout and stderr. + PrintStream origStdOut = System.out; + PrintStream origStdErr = System.err; + + detailsOutput = new DetailsOutputStream(); + + // Redirects the system output to a custom one. + PrintStream printStream = new PrintStream(detailsOutput); + System.setOut(printStream); + System.setErr(printStream); + + List tsaList = tsaInfoList(); + if (tsaList.size() == 0) { + throw new RuntimeException("TSA service is mandatory."); + } + + List jdkInfoList = jdkInfoList(); + List certList = createCertificates(jdkInfoList); + createJar(); + List signItems = test(jdkInfoList, tsaList, certList); + + boolean failed = generateReport(tsaList, signItems); + + // Restores the original stdout and stderr. + System.setOut(origStdOut); + System.setErr(origStdErr); + + if (failed) { + throw new RuntimeException("At least one test case failed. " + + "Please check the failed row(s) in report.html " + + "or failedReport.html."); + } + } + + // Creates a jar file that contains an empty file. + private static void createJar() throws IOException { + String testFile = "test"; + new File(testFile).createNewFile(); + JarUtils.createJar(TEST_JAR_NAME, testFile); + } + + // Creates a key store that includes a set of valid/expired certificates + // with various algorithms. + private static List createCertificates(List jdkInfoList) + throws Throwable { + List certList = new ArrayList(); + Set expiredCertFilter = new HashSet(); + + for(JdkInfo jdkInfo : jdkInfoList) { + for(String keyAlgorithm : KEY_ALGORITHMS) { + for(String digestAlgorithm : DIGEST_ALGORITHMS) { + for(int keySize : keySizes(keyAlgorithm)) { + for(boolean expired : EXPIRED) { + // It creates only one expired certificate for one + // key algorithm. + if (expired + && !expiredCertFilter.add(keyAlgorithm)) { + continue; + } + + CertInfo certInfo = new CertInfo( + jdkInfo.version, + keyAlgorithm, + digestAlgorithm, + keySize, + expired); + if (!certList.contains(certInfo)) { + String alias = createCertificate( + jdkInfo.jdkPath, certInfo); + if (alias != null) { + certList.add(certInfo); + } + } + } + } + } + } + } + + return certList; + } + + // Creates/Updates a key store that adds a certificate with specific algorithm. + private static String createCertificate(String jdkPath, CertInfo certInfo) + throws Throwable { + String alias = certInfo.alias(); + + List arguments = new ArrayList(); + arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY); + arguments.add("-v"); + arguments.add("-storetype"); + arguments.add("jks"); + arguments.add("-genkey"); + arguments.add("-keyalg"); + arguments.add(certInfo.keyAlgorithm); + String sigalg = sigalg(certInfo.digestAlgorithm, certInfo.keyAlgorithm); + if (sigalg != null) { + arguments.add("-sigalg"); + arguments.add(sigalg); + } + if (certInfo.keySize != 0) { + arguments.add("-keysize"); + arguments.add(certInfo.keySize + ""); + } + arguments.add("-dname"); + arguments.add("CN=Test"); + arguments.add("-alias"); + arguments.add(alias); + arguments.add("-keypass"); + arguments.add(PASSWORD); + arguments.add("-storepass"); + arguments.add(PASSWORD); + + arguments.add("-startdate"); + arguments.add(startDate(certInfo.expired)); + arguments.add("-validity"); + arguments.add("1"); + arguments.add("-keystore"); + arguments.add(KEYSTORE); + + OutputAnalyzer outputAnalyzer = execTool( + jdkPath + "/bin/keytool", + arguments.toArray(new String[arguments.size()])); + if (outputAnalyzer.getExitValue() == 0 + && !outputAnalyzer.getOutput().matches("[Ee]xception")) { + return alias; + } else { + return null; + } + } + + private static String sigalg(String digestAlgorithm, String keyAlgorithm) { + if (digestAlgorithm == DEFAULT) { + return null; + } + + String keyName = keyAlgorithm == EC ? "ECDSA" : keyAlgorithm; + return digestAlgorithm.replace("-", "") + "with" + keyName; + } + + // The validity period of a certificate always be 1 day. For creating an + // expired certificate, the start date is the time before 1 day, then the + // certificate expires immediately. And for creating a valid certificate, + // the start date is the time before (1 day - CERT_VALIDITY minutes), then + // the certificate will expires in CERT_VALIDITY minutes. + private static String startDate(boolean expiredCert) { + CALENDAR.setTime(new Date()); + CALENDAR.add(Calendar.DAY_OF_MONTH, -1); + if (!expiredCert) { + CALENDAR.add(Calendar.MINUTE, CERT_VALIDITY); + } + Date startDate = CALENDAR.getTime(); + lastCertStartTime = startDate.getTime(); + return DATE_FORMAT.format(startDate); + } + + // Retrieves JDK info from the file which is specified by property jdkListFile, + // or from property jdkList if jdkListFile is not available. + private static List jdkInfoList() throws Throwable { + String[] jdkList = list("jdkList"); + if (jdkList.length == 0) { + jdkList = new String[] { TEST_JDK }; + } + + List jdkInfoList = new ArrayList(); + for (String jdkPath : jdkList) { + JdkInfo jdkInfo = new JdkInfo(jdkPath); + // The JDK version must be unique. + if (!jdkInfoList.contains(jdkInfo)) { + jdkInfoList.add(jdkInfo); + } else { + System.out.println("The JDK version is duplicate: " + jdkPath); + } + } + return jdkInfoList; + } + + // Retrieves TSA info from the file which is specified by property tsaListFile, + // or from property tsaList if tsaListFile is not available. + private static List tsaInfoList() throws IOException { + String[] tsaList = list("tsaList"); + + List tsaInfoList = new ArrayList(); + for (int i = 0; i < tsaList.length; i++) { + String[] values = tsaList[i].split(";digests="); + + String[] digests = new String[0]; + if (values.length == 2) { + digests = values[1].split(","); + } + + TsaInfo bufTsa = new TsaInfo(i, values[0]); + + for (String digest : digests) { + bufTsa.addDigest(digest); + } + + tsaInfoList.add(bufTsa); + } + + return tsaInfoList; + } + + private static String[] list(String listProp) + throws IOException { + String listFileProp = listProp + "File"; + String listFile = System.getProperty(listFileProp); + if (!isEmpty(listFile)) { + System.out.println(listFileProp + "=" + listFile); + List list = new ArrayList(); + BufferedReader reader = new BufferedReader( + new FileReader(listFile)); + String line; + while ((line = reader.readLine()) != null) { + String item = line.trim(); + if (!item.isEmpty()) { + list.add(item); + } + } + reader.close(); + return list.toArray(new String[list.size()]); + } + + String list = System.getProperty(listProp); + System.out.println(listProp + "=" + list); + return !isEmpty(list) ? list.split("#") : new String[0]; + } + + private static boolean isEmpty(String str) { + return str == null || str.isEmpty(); + } + + // A JDK (signer) signs a jar with a variety of algorithms, and then all of + // JDKs (verifiers), including the signer itself, try to verify the signed + // jars respectively. + private static List test(List jdkInfoList, + List tsaInfoList, List certList) + throws Throwable { + detailsOutput.transferPhase(); + List signItems = signing(jdkInfoList, tsaInfoList, certList); + + detailsOutput.transferPhase(); + for (SignItem signItem : signItems) { + for (JdkInfo verifierInfo : jdkInfoList) { + // JDK 6 doesn't support EC + if (!verifierInfo.isJdk6() + || signItem.certInfo.keyAlgorithm != EC) { + verifying(signItem, VerifyItem.build(verifierInfo)); + } + } + } + + if (DELAY_VERIFY) { + detailsOutput.transferPhase(); + System.out.print("Waiting for delay verifying"); + long lastCertExpirationTime = lastCertStartTime + 24 * 60 * 60 * 1000; + while (System.currentTimeMillis() < lastCertExpirationTime) { + TimeUnit.SECONDS.sleep(30); + System.out.print("."); + } + System.out.println(); + + System.out.println("Delay verifying starts"); + for (SignItem signItem : signItems) { + for (VerifyItem verifyItem : signItem.verifyItems) { + verifying(signItem, verifyItem); + } + } + } + + detailsOutput.transferPhase(); + + return signItems; + } + + private static List signing(List jdkInfos, + List tsaList, List certList) throws Throwable { + List signItems = new ArrayList(); + + Set signFilter = new HashSet(); + + for (JdkInfo signerInfo : jdkInfos) { + for (String keyAlgorithm : KEY_ALGORITHMS) { + // JDK 6 doesn't support EC + if (signerInfo.isJdk6() && keyAlgorithm == EC) { + continue; + } + + for (String digestAlgorithm : DIGEST_ALGORITHMS) { + String sigalg = sigalg(digestAlgorithm, keyAlgorithm); + // If the signature algorithm is not supported by the JDK, + // it cannot try to sign jar with this algorithm. + if (sigalg != null && !signerInfo.isSupportedSigalg(sigalg)) { + continue; + } + + // If the JDK doesn't support option -tsadigestalg, the + // associated cases just be ignored. + if (digestAlgorithm != DEFAULT + && !signerInfo.supportsTsadigestalg) { + continue; + } + + for (int keySize : keySizes(keyAlgorithm)) { + for (boolean expired : EXPIRED) { + CertInfo certInfo = new CertInfo( + signerInfo.version, + keyAlgorithm, + digestAlgorithm, + keySize, + expired); + if (!certList.contains(certInfo)) { + continue; + } + + String tsadigestalg = digestAlgorithm != DEFAULT + ? digestAlgorithm + : null; + + for (TsaInfo tsaInfo : tsaList) { + // It has to ignore the digest algorithm, which + // is not supported by the TSA server. + if(!tsaInfo.isDigestSupported(tsadigestalg)) { + continue; + } + + String tsaUrl = tsaInfo.tsaUrl; + if (TsaFilter.filter( + signerInfo.version, + digestAlgorithm, + expired, + tsaInfo.index)) { + tsaUrl = null; + } + + String signedJar = "JDK_" + + signerInfo.version + "-CERT_" + + certInfo + + (tsaUrl == null + ? "" + : "-TSA_" + tsaInfo.index); + + // It has to ignore the same jar signing. + if (!signFilter.add(signedJar)) { + continue; + } + + SignItem signItem = SignItem.build() + .certInfo(certInfo) + .version(signerInfo.version) + .signatureAlgorithm(sigalg) + .tsaDigestAlgorithm( + tsaUrl == null + ? null + : tsadigestalg) + .tsaIndex( + tsaUrl == null + ? -1 + : tsaInfo.index) + .signedJar(signedJar); + String signingId = signingId(signItem); + detailsOutput.writeAnchorName(signingId, + "Signing: " + signingId); + + OutputAnalyzer signOA = signJar( + signerInfo.jarsignerPath, + sigalg, + tsadigestalg, + tsaUrl, + certInfo.alias(), + signedJar); + Status signingStatus = signingStatus(signOA); + signItem.status(signingStatus); + + if (signingStatus != Status.ERROR) { + // Using the testing JDK, which is specified + // by jtreg option "-jdk", to verify the + // signed jar and extract the signature + // algorithm and timestamp digest algorithm. + String output = verifyJar(TEST_JARSIGNER, + signedJar).getOutput(); + signItem.extractedSignatureAlgorithm( + extract(output, + " *Signature algorithm.*", + ".*: |,.*")); + signItem.extractedTsaDigestAlgorithm( + extract(output, + " *Timestamp digest algorithm.*", + ".*: ")); + } + + signItems.add(signItem); + } + } + } + } + } + } + + return signItems; + } + + private static void verifying(SignItem signItem, VerifyItem verifyItem) + throws Throwable { + boolean delayVerify = verifyItem.status == Status.NONE; + String verifyingId = verifyingId(signItem, verifyItem, !delayVerify); + detailsOutput.writeAnchorName(verifyingId, "Verifying: " + verifyingId); + + OutputAnalyzer verifyOA = verifyJar(verifyItem.jdkInfo.jarsignerPath, + signItem.signedJar); + Status verifyingStatus = verifyingStatus(verifyOA); + + // It checks if the default timestamp digest algorithm is SHA-256. + if (verifyingStatus != Status.ERROR + && signItem.tsaDigestAlgorithm == null) { + verifyingStatus = signItem.extractedTsaDigestAlgorithm != null + && !signItem.extractedTsaDigestAlgorithm.matches("SHA-?256") + ? Status.ERROR + : verifyingStatus; + if (verifyingStatus == Status.ERROR) { + System.out.println("The default tsa digest is not SHA-256: " + + signItem.extractedTsaDigestAlgorithm); + } + } + + if (delayVerify) { + signItem.addVerifyItem(verifyItem.status(verifyingStatus)); + } else { + verifyItem.delayStatus(verifyingStatus); + } + } + + // Return key sizes according to the specified key algorithm. + private static int[] keySizes(String keyAlgorithm) { + if (keyAlgorithm == RSA || keyAlgorithm == DSA) { + return new int[] { 1024, 2048, 0 }; + } else if (keyAlgorithm == EC) { + return new int[] { 384, 571, 0 }; + } + + return null; + } + + // Determines the status of signing. + private static Status signingStatus(OutputAnalyzer outputAnalyzer) { + if (outputAnalyzer.getExitValue() == 0) { + if (outputAnalyzer.getOutput().contains(Test.WARNING)) { + return Status.WARNING; + } else { + return Status.NORMAL; + } + } else { + return Status.ERROR; + } + } + + // Determines the status of verifying. + private static Status verifyingStatus(OutputAnalyzer outputAnalyzer) { + if (outputAnalyzer.getExitValue() == 0) { + String output = outputAnalyzer.getOutput(); + if (!output.contains(Test.JAR_VERIFIED)) { + return Status.ERROR; + } else if (output.contains(Test.WARNING)) { + return Status.WARNING; + } else { + return Status.NORMAL; + } + } else { + return Status.ERROR; + } + } + + // Extracts string from text by specified patterns. + private static String extract(String text, String linePattern, + String replacePattern) { + Matcher lineMatcher = Pattern.compile(linePattern).matcher(text); + if (lineMatcher.find()) { + String line = lineMatcher.group(0); + return line.replaceAll(replacePattern, ""); + } else { + return null; + } + } + + // Using specified jarsigner to sign the pre-created jar with specified + // algorithms. + private static OutputAnalyzer signJar(String jarsignerPath, String sigalg, + String tsadigestalg, String tsa, String alias, String signedJar) + throws Throwable { + List arguments = new ArrayList(); + + if (PROXY_HOST != null && PROXY_PORT != null) { + arguments.add("-J-Dhttp.proxyHost=" + PROXY_HOST); + arguments.add("-J-Dhttp.proxyPort=" + PROXY_PORT); + arguments.add("-J-Dhttps.proxyHost=" + PROXY_HOST); + arguments.add("-J-Dhttps.proxyPort=" + PROXY_PORT); + } + arguments.add("-J-Djava.security.properties=" + JAVA_SECURITY); + arguments.add("-debug"); + arguments.add("-verbose"); + if (sigalg != null) { + arguments.add("-sigalg"); + arguments.add(sigalg); + } + if (tsa != null) { + arguments.add("-tsa"); + arguments.add(tsa); + } + if (tsadigestalg != null) { + arguments.add("-tsadigestalg"); + arguments.add(tsadigestalg); + } + arguments.add("-keystore"); + arguments.add(KEYSTORE); + arguments.add("-storepass"); + arguments.add(PASSWORD); + arguments.add("-signedjar"); + arguments.add(signedJar + ".jar"); + arguments.add(TEST_JAR_NAME); + arguments.add(alias); + + OutputAnalyzer outputAnalyzer = execTool( + jarsignerPath, + arguments.toArray(new String[arguments.size()])); + return outputAnalyzer; + } + + // Using specified jarsigner to verify the signed jar. + private static OutputAnalyzer verifyJar(String jarsignerPath, + String signedJar) throws Throwable { + OutputAnalyzer outputAnalyzer = execTool( + jarsignerPath, + "-J-Djava.security.properties=" + JAVA_SECURITY, + "-debug", + "-verbose", + "-certs", + "-keystore", KEYSTORE, + "-verify", signedJar + ".jar"); + return outputAnalyzer; + } + + // Generates the test result report. + private static boolean generateReport(List tsaList, + List signItems) throws IOException { + System.out.println("Report is being generated..."); + + StringBuilder report = new StringBuilder(); + report.append(HtmlHelper.startHtml()); + report.append(HtmlHelper.startPre()); + // Generates TSA URLs + report.append("TSA list:\n"); + for(TsaInfo tsaInfo : tsaList) { + report.append( + String.format("%d=%s%n", tsaInfo.index, tsaInfo.tsaUrl)); + } + report.append(HtmlHelper.endPre()); + + report.append(HtmlHelper.startTable()); + // Generates report headers. + List headers = new ArrayList(); + headers.add("[Certificate]"); + headers.add("[Signer JDK]"); + headers.add("[Signature Algorithm]"); + headers.add("[TSA Digest]"); + headers.add("[TSA]"); + headers.add("[Signing Status]"); + headers.add("[Verifier JDK]"); + headers.add("[Verifying Status]"); + if (DELAY_VERIFY) { + headers.add("[Delay Verifying Status]"); + } + headers.add("[Failed]"); + report.append(HtmlHelper.htmlRow( + headers.toArray(new String[headers.size()]))); + + StringBuilder failedReport = new StringBuilder(report.toString()); + + boolean failed = false; + + // Generates report rows. + for (SignItem signItem : signItems) { + for (VerifyItem verifyItem : signItem.verifyItems) { + String reportRow = reportRow(signItem, verifyItem); + report.append(reportRow); + boolean isFailedCase = isFailed(signItem, verifyItem); + if (isFailedCase) { + failedReport.append(reportRow); + } + failed = failed || isFailedCase; + } + } + + report.append(HtmlHelper.endTable()); + report.append(HtmlHelper.endHtml()); + generateFile("report.html", report.toString()); + if (failed) { + failedReport.append(HtmlHelper.endTable()); + failedReport.append(HtmlHelper.endPre()); + failedReport.append(HtmlHelper.endHtml()); + generateFile("failedReport.html", failedReport.toString()); + } + + System.out.println("Report is generated."); + return failed; + } + + private static void generateFile(String path, String content) + throws IOException { + FileWriter writer = new FileWriter(new File(path)); + writer.write(content); + writer.close(); + } + + private static String jarsignerPath(String jdkPath) { + return jdkPath + "/bin/jarsigner"; + } + + // Executes the specified function on JdkUtils by the specified JDK. + private static String execJdkUtils(String jdkPath, String method, + String... args) throws Throwable { + String[] cmd = new String[args.length + 5]; + cmd[0] = jdkPath + "/bin/java"; + cmd[1] = "-cp"; + cmd[2] = TEST_CLASSES; + cmd[3] = JdkUtils.class.getName(); + cmd[4] = method; + System.arraycopy(args, 0, cmd, 5, args.length); + return ProcessTools.executeCommand(cmd).getOutput(); + } + + // Executes the specified JDK tools, such as keytool and jarsigner, and + // ensures the output is in US English. + private static OutputAnalyzer execTool(String toolPath, String... args) + throws Throwable { + String[] cmd = new String[args.length + 4]; + cmd[0] = toolPath; + cmd[1] = "-J-Duser.language=en"; + cmd[2] = "-J-Duser.country=US"; + cmd[3] = "-J-Djava.security.egd=file:/dev/./urandom"; + System.arraycopy(args, 0, cmd, 4, args.length); + return ProcessTools.executeCommand(cmd); + } + + private static class JdkInfo { + + private final String jdkPath; + private final String jarsignerPath; + private final String version; + private final boolean supportsTsadigestalg; + + private Map sigalgMap = new HashMap(); + + private JdkInfo(String jdkPath) throws Throwable { + this.jdkPath = jdkPath; + version = execJdkUtils(jdkPath, JdkUtils.M_JAVA_RUNTIME_VERSION); + if (version == null || version.trim().isEmpty()) { + throw new RuntimeException( + "Cannot determine the JDK version: " + jdkPath); + } + jarsignerPath = jarsignerPath(jdkPath); + supportsTsadigestalg = execTool(jarsignerPath, "-help") + .getOutput().contains("-tsadigestalg"); + } + + private boolean isSupportedSigalg(String sigalg) throws Throwable { + if (!sigalgMap.containsKey(sigalg)) { + boolean isSupported = "true".equalsIgnoreCase( + execJdkUtils( + jdkPath, + JdkUtils.M_IS_SUPPORTED_SIGALG, + sigalg)); + sigalgMap.put(sigalg, isSupported); + } + + return sigalgMap.get(sigalg); + } + + private boolean isJdk6() { + return version.startsWith("1.6"); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + JdkInfo other = (JdkInfo) obj; + if (version == null) { + if (other.version != null) + return false; + } else if (!version.equals(other.version)) + return false; + return true; + } + } + + private static class TsaInfo { + + private final int index; + private final String tsaUrl; + private Set digestList = new HashSet(); + + private TsaInfo(int index, String tsa) { + this.index = index; + this.tsaUrl = tsa; + } + + private void addDigest(String digest) { + if (!ignore(digest)) { + digestList.add(digest); + } + } + + private static boolean ignore(String digest) { + return !SHA1.equalsIgnoreCase(digest) + && !SHA256.equalsIgnoreCase(digest) + && !SHA512.equalsIgnoreCase(digest); + } + + private boolean isDigestSupported(String digest) { + return digest == null || digestList.isEmpty() + || digestList.contains(digest); + } + } + + private static class CertInfo { + + private final String jdkVersion; + private final String keyAlgorithm; + private final String digestAlgorithm; + private final int keySize; + private final boolean expired; + + private CertInfo(String jdkVersion, String keyAlgorithm, + String digestAlgorithm, int keySize, boolean expired) { + this.jdkVersion = jdkVersion; + this.keyAlgorithm = keyAlgorithm; + this.digestAlgorithm = digestAlgorithm; + this.keySize = keySize; + this.expired = expired; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((digestAlgorithm == null) ? 0 : digestAlgorithm.hashCode()); + result = prime * result + (expired ? 1231 : 1237); + result = prime * result + + ((jdkVersion == null) ? 0 : jdkVersion.hashCode()); + result = prime * result + + ((keyAlgorithm == null) ? 0 : keyAlgorithm.hashCode()); + result = prime * result + keySize; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CertInfo other = (CertInfo) obj; + if (digestAlgorithm == null) { + if (other.digestAlgorithm != null) + return false; + } else if (!digestAlgorithm.equals(other.digestAlgorithm)) + return false; + if (expired != other.expired) + return false; + if (jdkVersion == null) { + if (other.jdkVersion != null) + return false; + } else if (!jdkVersion.equals(other.jdkVersion)) + return false; + if (keyAlgorithm == null) { + if (other.keyAlgorithm != null) + return false; + } else if (!keyAlgorithm.equals(other.keyAlgorithm)) + return false; + if (keySize != other.keySize) + return false; + return true; + } + + private String alias() { + return jdkVersion + "_" + toString(); + } + + @Override + public String toString() { + return keyAlgorithm + "_" + digestAlgorithm + + (keySize == 0 ? "" : "_" + keySize) + + (expired ? "_Expired" : ""); + } + } + + // It does only one timestamping for the same JDK, digest algorithm and + // TSA service with an arbitrary valid/expired certificate. + private static class TsaFilter { + + private static final Set SET = new HashSet(); + + private static boolean filter(String signerVersion, + String digestAlgorithm, boolean expiredCert, int tsaIndex) { + return !SET.add(new Condition(signerVersion, digestAlgorithm, + expiredCert, tsaIndex)); + } + + private static class Condition { + + private final String signerVersion; + private final String digestAlgorithm; + private final boolean expiredCert; + private final int tsaIndex; + + private Condition(String signerVersion, String digestAlgorithm, + boolean expiredCert, int tsaIndex) { + this.signerVersion = signerVersion; + this.digestAlgorithm = digestAlgorithm; + this.expiredCert = expiredCert; + this.tsaIndex = tsaIndex; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((digestAlgorithm == null) ? 0 : digestAlgorithm.hashCode()); + result = prime * result + (expiredCert ? 1231 : 1237); + result = prime * result + + ((signerVersion == null) ? 0 : signerVersion.hashCode()); + result = prime * result + tsaIndex; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Condition other = (Condition) obj; + if (digestAlgorithm == null) { + if (other.digestAlgorithm != null) + return false; + } else if (!digestAlgorithm.equals(other.digestAlgorithm)) + return false; + if (expiredCert != other.expiredCert) + return false; + if (signerVersion == null) { + if (other.signerVersion != null) + return false; + } else if (!signerVersion.equals(other.signerVersion)) + return false; + if (tsaIndex != other.tsaIndex) + return false; + return true; + } + }} + + private static enum Status { + + // No action due to pre-action fails. + NONE, + + // jar is signed/verified with error + ERROR, + + // jar is signed/verified with warning + WARNING, + + // jar is signed/verified without any warning and error + NORMAL + } + + private static class SignItem { + + private CertInfo certInfo; + private String version; + private String signatureAlgorithm; + // Signature algorithm that is extracted from verification output. + private String extractedSignatureAlgorithm; + private String tsaDigestAlgorithm; + // TSA digest algorithm that is extracted from verification output. + private String extractedTsaDigestAlgorithm; + private int tsaIndex; + private Status status; + private String signedJar; + + private List verifyItems = new ArrayList(); + + private static SignItem build() { + return new SignItem(); + } + + private SignItem certInfo(CertInfo certInfo) { + this.certInfo = certInfo; + return this; + } + + private SignItem version(String version) { + this.version = version; + return this; + } + + private SignItem signatureAlgorithm(String signatureAlgorithm) { + this.signatureAlgorithm = signatureAlgorithm; + return this; + } + + private SignItem extractedSignatureAlgorithm( + String extractedSignatureAlgorithm) { + this.extractedSignatureAlgorithm = extractedSignatureAlgorithm; + return this; + } + + private SignItem tsaDigestAlgorithm(String tsaDigestAlgorithm) { + this.tsaDigestAlgorithm = tsaDigestAlgorithm; + return this; + } + + private SignItem extractedTsaDigestAlgorithm( + String extractedTsaDigestAlgorithm) { + this.extractedTsaDigestAlgorithm = extractedTsaDigestAlgorithm; + return this; + } + + private SignItem tsaIndex(int tsaIndex) { + this.tsaIndex = tsaIndex; + return this; + } + + private SignItem status(Status status) { + this.status = status; + return this; + } + + private SignItem signedJar(String signedJar) { + this.signedJar = signedJar; + return this; + } + + private void addVerifyItem(VerifyItem verifyItem) { + verifyItems.add(verifyItem); + } + } + + private static class VerifyItem { + + private JdkInfo jdkInfo; + private Status status = Status.NONE; + private Status delayStatus = Status.NONE; + + private static VerifyItem build(JdkInfo jdkInfo) { + VerifyItem verifyItem = new VerifyItem(); + verifyItem.jdkInfo = jdkInfo; + return verifyItem; + } + + private VerifyItem status(Status status) { + this.status = status; + return this; + } + + private VerifyItem delayStatus(Status status) { + this.delayStatus = status; + return this; + } + } + + // The identifier for a specific signing. + private static String signingId(SignItem signItem) { + return signItem.signedJar; + } + + // The identifier for a specific verifying. + private static String verifyingId(SignItem signItem, VerifyItem verifyItem, + boolean delayVerify) { + return "S_" + signingId(signItem) + "-" + (delayVerify ? "DV" : "V") + + "_" + verifyItem.jdkInfo.version; + } + + private static String reportRow(SignItem signItem, VerifyItem verifyItem) { + List values = new ArrayList(); + values.add(signItem.certInfo.toString()); + values.add(signItem.version); + values.add(null2Default(signItem.signatureAlgorithm, + signItem.extractedSignatureAlgorithm)); + values.add(signItem.tsaIndex == -1 + ? "" + : null2Default(signItem.tsaDigestAlgorithm, + signItem.extractedTsaDigestAlgorithm)); + values.add(signItem.tsaIndex == -1 ? "" : signItem.tsaIndex + ""); + values.add(HtmlHelper.anchorLink( + PhaseOutputStream.fileName(PhaseOutputStream.Phase.SIGNING), + signingId(signItem), + signItem.status.toString())); + values.add(verifyItem.jdkInfo.version); + values.add(HtmlHelper.anchorLink( + PhaseOutputStream.fileName(PhaseOutputStream.Phase.VERIFYING), + verifyingId(signItem, verifyItem, false), + verifyItem.status.toString())); + if (DELAY_VERIFY) { + values.add(HtmlHelper.anchorLink( + PhaseOutputStream.fileName( + PhaseOutputStream.Phase.DELAY_VERIFYING), + verifyingId(signItem, verifyItem, true), + verifyItem.delayStatus.toString())); + } + values.add(isFailed(signItem, verifyItem) ? "X" : ""); + return HtmlHelper.htmlRow(values.toArray(new String[values.size()])); + } + + private static boolean isFailed(SignItem signItem, + VerifyItem verifyItem) { + return signItem.status == Status.ERROR + || verifyItem.status == Status.ERROR + || verifyItem.delayStatus == Status.ERROR; + } + + // If a value is null, then displays the default value or N/A. + private static String null2Default(String value, String defaultValue) { + return value == null + ? DEFAULT + "(" + (defaultValue == null + ? "N/A" + : defaultValue) + ")" + : value; + } +} diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/DetailsOutputStream.java b/jdk/test/sun/security/tools/jarsigner/compatibility/DetailsOutputStream.java new file mode 100644 index 00000000000..f125322c37b --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/DetailsOutputStream.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +/* + * A custom output stream that redirects the testing outputs to a file, called + * details.out. It also calls another output stream to save some outputs to + * other files. + */ +public class DetailsOutputStream extends FileOutputStream { + + private PhaseOutputStream phaseOutputStream = new PhaseOutputStream(); + + public DetailsOutputStream() throws FileNotFoundException { + super("details.out", true); + } + + public void transferPhase() throws IOException { + if (phaseOutputStream.isCorePhase()) { + phaseOutputStream.write(HtmlHelper.endHtml()); + phaseOutputStream.write(HtmlHelper.endPre()); + } + + phaseOutputStream.transfer(); + + if (phaseOutputStream.isCorePhase()) { + phaseOutputStream.write(HtmlHelper.startHtml()); + phaseOutputStream.write(HtmlHelper.startPre()); + } + } + + @Override + public void write(byte[] b) throws IOException { + super.write(b); + phaseOutputStream.write(b); + } + + @Override + public void write(int b) throws IOException { + super.write(b); + phaseOutputStream.write(b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + super.write(b, off, len); + phaseOutputStream.write(b, off, len); + } + + public void writeAnchorName(String name, String text) throws IOException { + super.write((text).getBytes()); + super.write('\n'); + phaseOutputStream.write(HtmlHelper.anchorName(name, text)); + phaseOutputStream.write('\n'); + } +} diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/HtmlHelper.java b/jdk/test/sun/security/tools/jarsigner/compatibility/HtmlHelper.java new file mode 100644 index 00000000000..d00cf02f0d5 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/HtmlHelper.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * A helper that is used for creating HTML elements. + */ +public class HtmlHelper { + + private static final String STYLE + = "style=\"font-family: Courier New; " + + "font-size: 12px; " + + "white-space: pre-wrap\""; + + public static String htmlRow(String... values) { + StringBuilder row = new StringBuilder(); + row.append(startTr()); + for (String value : values) { + row.append(startTd()); + row.append(value); + row.append(endTd()); + } + row.append(endTr()); + return row.toString(); + } + + public static String startHtml() { + return startTag("html"); + } + + public static String endHtml() { + return endTag("html"); + } + + public static String startPre() { + return startTag("pre " + STYLE); + } + + public static String endPre() { + return endTag("pre"); + } + + public static String startTable() { + return startTag("table " + STYLE); + } + + public static String endTable() { + return endTag("table"); + } + + public static String startTr() { + return startTag("tr"); + } + + public static String endTr() { + return endTag("tr"); + } + + public static String startTd() { + return startTag("td"); + } + + public static String endTd() { + return endTag("td"); + } + + public static String startTag(String tag) { + return "<" + tag + ">"; + } + + public static String endTag(String tag) { + return ""; + } + + public static String anchorName(String name, String text) { + return "" + text + ""; + } + + public static String anchorLink(String file, String anchorName, + String text) { + return "" + text + ""; + } +} diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/JdkUtils.java b/jdk/test/sun/security/tools/jarsigner/compatibility/JdkUtils.java new file mode 100644 index 00000000000..cd990161b92 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/JdkUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.NoSuchAlgorithmException; +import java.security.Signature; + +/* + * This class is used for returning some specific JDK information. + */ +public class JdkUtils { + + static final String M_JAVA_RUNTIME_VERSION = "javaRuntimeVersion"; + static final String M_IS_SUPPORTED_SIGALG = "isSupportedSigalg"; + + // Returns the JDK build version. + static String javaRuntimeVersion() { + return System.getProperty("java.runtime.version"); + } + + // Checks if the specified signature algorithm is supported by the JDK. + static boolean isSupportedSigalg(String sigalg) { + boolean isSupported = false; + try { + isSupported = Signature.getInstance(sigalg) != null; + } catch (NoSuchAlgorithmException e) { } + + if (!isSupported) { + System.out.println(sigalg + " is not supported yet."); + } + + return isSupported; + } + + public static void main(String[] args) { + if (M_JAVA_RUNTIME_VERSION.equals(args[0])) { + System.out.print(javaRuntimeVersion()); + } else if (M_IS_SUPPORTED_SIGALG.equals(args[0])) { + System.out.print(isSupportedSigalg(args[1])); + } + } +} diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/PhaseOutputStream.java b/jdk/test/sun/security/tools/jarsigner/compatibility/PhaseOutputStream.java new file mode 100644 index 00000000000..f99822ca1f8 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/PhaseOutputStream.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/* + * A custom output stream that saves the testing details to different files + * according to the current testing phase. + */ +public class PhaseOutputStream extends OutputStream { + + public enum Phase { + PRE_SIGNING, // before jar signing + SIGNING, // jar signing + VERIFYING, // jar verifying + DELAY_VERIFYING, // jar verifying after certificates expire + POST_VERIFYING; // after jar verifying + } + + private OutputStream signingOut = null; + private OutputStream verifyingOut = null; + private OutputStream delayVerifyingOut = null; + + private Phase currentPhase = Phase.PRE_SIGNING; + + public void transfer() { + switch (currentPhase) { + case PRE_SIGNING: + currentPhase = Phase.SIGNING; + break; + case SIGNING: + currentPhase = Phase.VERIFYING; + break; + case VERIFYING: + currentPhase = Compatibility.DELAY_VERIFY + ? Phase.DELAY_VERIFYING + : Phase.POST_VERIFYING; + break; + case DELAY_VERIFYING: + currentPhase = Phase.POST_VERIFYING; + break; + case POST_VERIFYING: + currentPhase = Phase.POST_VERIFYING; + break; + } + } + + // The core phases are SIGNING, VERIFYING and DELAY_VERIFYING. + public boolean isCorePhase() { + return currentPhase != PhaseOutputStream.Phase.PRE_SIGNING + && currentPhase != PhaseOutputStream.Phase.POST_VERIFYING; + } + + public Phase currentPhase() { + return currentPhase; + } + + @Override + public void write(int b) throws IOException { + OutputStream output = phaseOut(); + if (output != null) { + output.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + OutputStream output = phaseOut(); + if (output != null) { + output.write(b); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + OutputStream output = phaseOut(); + if (output != null) { + output.write(b, off, len); + } + } + + public void write(String str) throws IOException { + write(str.getBytes()); + } + + private OutputStream phaseOut() throws FileNotFoundException { + switch (currentPhase) { + case SIGNING: + return signingOut == null + ? signingOut = createOutput(Phase.SIGNING) + : signingOut; + case VERIFYING: + return verifyingOut == null + ? verifyingOut = createOutput(Phase.VERIFYING) + : verifyingOut; + case DELAY_VERIFYING: + return delayVerifyingOut == null + ? delayVerifyingOut = createOutput(Phase.DELAY_VERIFYING) + : delayVerifyingOut; + default: + return null; + } + } + + @Override + public void flush() throws IOException { + flush(signingOut); + flush(verifyingOut); + flush(delayVerifyingOut); + } + + private void flush(OutputStream output) throws IOException { + if (output != null) { + output.flush(); + } + } + + @Override + public void close() throws IOException { + close(signingOut); + close(verifyingOut); + close(delayVerifyingOut); + } + + private void close(OutputStream output) throws IOException { + if (output != null) { + output.close(); + } + } + + private static OutputStream createOutput(Phase phase) + throws FileNotFoundException { + return new FileOutputStream(fileName(phase), true); + } + + public static String fileName(Phase phase) { + return phase.name() + ".html"; + } +} diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/README b/jdk/test/sun/security/tools/jarsigner/compatibility/README new file mode 100644 index 00000000000..7868788c2e6 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/README @@ -0,0 +1,215 @@ +# Copyright (c) 2017, 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 +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +##### Summary ##### +This test is used to verify the compatibility on jarsigner cross different JDK +releases. It also can be used to check jar signing (w/ and w/o TSA) and verifying +on some specific key algorithms and digest algorithms. + +##### Output ##### +The test will generate a report, at JTwork/scratch/report.html, to display the +key parameters for signing and the status of signing and verifying. And it will +generate another report, at JTwork/scratch/failedReport.html, to collect all of +failed cases. + +Please note that, the test may output a great deal of logs if the jdk list and +TSA list are big, and that would lead to jtreg output overflow. So, it redirects +stdout and stderr to file JTwork/scratch/details.out. + +##### Report Columns ##### +Certificate + Certificate identifier. The identifier consists of specific attributes of + the certificate. Generally, the naming convention is: + KeyAlgorithm_DigestAlgorithm_[KeySize][_Expired] + +Signer JDK + The JDK version that signs jar. + +Signature Algorithm + The signature algorithm used by signing. + +TSA Digest + The timestamp digest algorithm used by signing. + +TSA + TSA URL index. All of TSA URLs and their indices can be found at the top + of this report. + +Signing Status + Signing process result status. The status are the followings: + [1]NONE, no action. + [2]NORMAL, no any error and warning. + [3]WARNING, no any error but some warnings raise. + [4]ERROR, some errors raise. + +Verifier JDK + The JDK version that verifies signed jars. + +Verifying Status + Verifying process result status. The status are the same as those for + "Status of Signing". + +Delay Verifying Status + Delay verifying process result status. The status are the same as those + for "Status of Signing". + +Failed + It highlights which case fails. The failed cases (rows) are marked with + letter X. + +##### Usages ##### +jtreg [-options] \ + -jdk: + [-DproxyHost= \ + -DproxyPort= \ + -DtsaListFile= \ + -DtsaList= \ + -DjdkListFile= \ + -DjdkList= \ + -DjavaSecurityFile= \ + -DdelayVerify= \ + -DcertValidity=<[1, 1440]>] \ + /jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java + +Besides the common jtreg options, like -jdk, this test introduces a set of +properties for receiving users' inputs and making the test more flexible. These +properties are: +proxyHost= + This property indicates proxy host. + +proxyPort= + This property indicates proxy port. The default value is 80. + +tsaListFile= + This property indicates a local file, which contains a set of TSA URLs and + the supported digest algorithms (by optional parameter digests). The format + of the file content looks like the below, + http://path/to/tsa1 + http://path/to/tsa2;digests=SHA-1,SHA-256 + https://path/to/tsa3 + ... + + If a TSA line does not list the supported digest algorithms, that means + the TSA supports SHA-1, SHA-256 and SHA-512. Because the test only focus + on SHA-1, SHA-256 and SHA-512. So, if other digest algorithms, like SHA-224 + and SHA-384, are listed, they just be ignored. + +tsaList= + This property directly lists a set of TSAs in command. "#" is the delimiter. + Note that, if both of tsaListFile and tsaList are specified, only property + jdkListFile is selected. If neither of tsaListFile and tsaList is specified, + the test will fails immediately. + +jdkListFile= + This property indicates a local file, which contains a set of local JDK + paths. The style of the file content looks like the below, + /path/to/jdk1 + /path/to/jdk2 + /path/to/jdk3 + ... + +jdkList= + This property directly lists a set of local JDK paths in command. "#" is + the delimiter. + Note that, if both of jdkListFile and jdkList are specified, only property + jdkListFile is selected. If neither of jdkListFile nor jdkList is specified, + the testing JDK, which is specified by jtreg option -jdk will be used as + the only one JDK in the JDK list. + +javaSecurityFile= + This property indicates an alternative java security properties file. The + default file is the path of file java.scurity that is distributed with + this test. + +delayVerify= + This property indicates if doing an additional verifying after all of valid + certificates expire. The default value is false. + +certValidity=<[1, 1440]> + This property indicates the remaining validity period in minutes for valid + certificates. The value range is [1, 1440]. The default value is 1440. + Note that, if delayVerify is false, this property doesn't take effect. + +The testing JDK, which is specified by jtreg option "-jdk", should include the +fix for JDK-8163304. Otherwise, the signature algorithm and timestamp digest +algorithm cannot be extracted from verification output. And this JDK should +support as many as possible signature algorithms. Anyway the latest JDK build +is always recommended. + +##### Examples ##### +$ cat /path/to/jdkList +/path/to/jdk6u171-b05 +/path/to/jdk7u161-b05 +/path/to/jdk8u144-b01 +/path/to/jdk9-179 + +$ cat /path/to/tsaList +http://timestamp.comodoca.com/rfc3161 +http://sha256timestamp.ws.symantec.com/sha256/timestamp +http://tsa.starfieldtech.com +http://timestamp.entrust.net/TSS/RFC3161sha1TS;digests=SHA-1,SHA-256 +http://timestamp.entrust.net/TSS/RFC3161sha2TS;digests=SHA-1,SHA-256 +http://rfc3161timestamp.globalsign.com/advanced;digests=SHA-256,SHA-512 +http://rfc3161timestamp.globalsign.com/standard +http://timestamp.globalsign.com/scripts/timstamp.dll +http://timestamp.globalsign.com/?signature=sha2;digests=SHA-256,SHA-512 +http://timestamp.digicert.com +http://time.certum.pl +http://tsa.swisssign.net +http://zeitstempel.dfn.de +https://tsp.iaik.tugraz.at/tsp/TspRequest + +$ jtreg -va -nr -timeout:100 \ + -jdk:/path/to/latest/jdk \ + -DproxyHost= -DproxyPort= \ + -DjdkListFile=/path/to/jdkList \ + -DtsaListFile=/path/to/tsaList \ + -DdelayVerify=true -DcertValidity=60 \ + /jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java + +The above is a comprehensive usage example. File "jdkList" lists the paths of +testing JDK builds, and file "tsaList" lists the URLs of TSA services. Some TSAs, +like http://timestamp.entrust.net/TSS/RFC3161sha1TS, specify the supported digest +algorithms. Other TSAs, which don't specify parameter digests, are regarded to +support SHA-1, SHA-256 and SHA-512. The test uses a proxy to access TSA services. +And it enables delay verifying and set the certificate validity period to 60 +minutes. So, after the first verification is done, the test will wait for all +of valid certificates expire and then does verification again. + +If don't want to provide such JDK list and TSA list files, the test allows to +specify JDKs and TSAs (via properties jdkList and tsaList respectively) in the +command directly, like the below style, +$ jtreg -va -nr -timeout:100 \ + -jdk:/path/to/latest/jdk \ + -DproxyHost= -DproxyPort= \ + -DjdkList=/path/to/jdk6u171-b05#/path/to/jdk7u161-b05#/path/to/jdk8u144-b01#/path/to/jdk9-179 \ + -DtsaList=http://timestamp.comodoca.com/rfc3161#http://timestamp.entrust.net/TSS/RFC3161sha1TS;digests=SHA-1,SHA-256 \ + -DdelayVerify=true -DcertValidity=60 \ + /jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java + +Furthermore, here introduces one of the simplest usages. It doesn't specify any +JDK list, so the testing JDK, which is specified by jtreg option "-jdk", will +be tested. And it doesn't apply delay verifying, and no proxy is used, and use +only one TSA. Now, the command is pretty simple and looks like the followings, +$ jtreg -va -nr -timeout:100 \ + -jdk:/path/to/latest/jdk \ + -DtsaList=http://timestamp.comodoca.com/rfc3161 \ + /jdk/test/sun/security/tools/jarsigner/compatibility/Compatibility.java \ No newline at end of file diff --git a/jdk/test/sun/security/tools/jarsigner/compatibility/java.security b/jdk/test/sun/security/tools/jarsigner/compatibility/java.security new file mode 100644 index 00000000000..b8623c1f3db --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/compatibility/java.security @@ -0,0 +1,2 @@ +jdk.certpath.disabledAlgorithms=MD2, MD5 +jdk.jar.disabledAlgorithms=MD2, MD5 \ No newline at end of file