mirror of https://github.com/jwtk/jjwt.git
854 jdk17 visibility (#855)
* Closes #854. - Replaced `ByteArrayInputStream` reflection with new `BytesInputStream` implementation. The reflection is what required `--add-opens java.base/java.io=jjwt.api` on JDK 17+. - Refactored `KeysBridge` to perform our own key length logic instead of delegating to `sun.security.util.KeyUtil`. The reflection is what required `--add-opens java.base/sun.security.util=jjwt.api` on JDK 17+ - Removed `AddOpens.java` due to above refactoring (no longer needed). - Returned a test-only `--add-opens` for `sun.security.util` for 3 test cases (added to `test.addOpens` maven property)
This commit is contained in:
parent
fad6e2737d
commit
a7d3d3197c
|
@ -64,7 +64,6 @@ import io.jsonwebtoken.security.UnsupportedKeyException;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -588,18 +587,18 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
||||||
InputStream payloadStream = null; // not needed unless b64 is enabled
|
InputStream payloadStream = null; // not needed unless b64 is enabled
|
||||||
if (this.encodePayload) {
|
if (this.encodePayload) {
|
||||||
encodeAndWrite("JWS Payload", payload, jws);
|
encodeAndWrite("JWS Payload", payload, jws);
|
||||||
signingInput = new ByteArrayInputStream(jws.toByteArray());
|
signingInput = Streams.of(jws.toByteArray());
|
||||||
} else { // b64
|
} else { // b64
|
||||||
|
|
||||||
// First, ensure we have the base64url header bytes + the SEPARATOR_CHAR byte:
|
// First, ensure we have the base64url header bytes + the SEPARATOR_CHAR byte:
|
||||||
ByteArrayInputStream prefixStream = new ByteArrayInputStream(jws.toByteArray());
|
InputStream prefixStream = Streams.of(jws.toByteArray());
|
||||||
|
|
||||||
// Next, b64 extension requires the raw (non-encoded) payload to be included directly in the signing input,
|
// Next, b64 extension requires the raw (non-encoded) payload to be included directly in the signing input,
|
||||||
// so we ensure we have an input stream for that:
|
// so we ensure we have an input stream for that:
|
||||||
if (payload.isClaims() || payload.isCompressed()) {
|
if (payload.isClaims() || payload.isCompressed()) {
|
||||||
ByteArrayOutputStream claimsOut = new ByteArrayOutputStream(8192);
|
ByteArrayOutputStream claimsOut = new ByteArrayOutputStream(8192);
|
||||||
writeAndClose("JWS Unencoded Payload", payload, claimsOut);
|
writeAndClose("JWS Unencoded Payload", payload, claimsOut);
|
||||||
payloadStream = new ByteArrayInputStream(claimsOut.toByteArray());
|
payloadStream = Streams.of(claimsOut.toByteArray());
|
||||||
} else {
|
} else {
|
||||||
// No claims and not compressed, so just get the direct InputStream:
|
// No claims and not compressed, so just get the direct InputStream:
|
||||||
payloadStream = Assert.stateNotNull(payload.toInputStream(), "Payload InputStream cannot be null.");
|
payloadStream = Assert.stateNotNull(payload.toInputStream(), "Payload InputStream cannot be null.");
|
||||||
|
@ -698,7 +697,7 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
||||||
if (content.isClaims()) {
|
if (content.isClaims()) {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
|
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
|
||||||
writeAndClose("JWE Claims", content, out);
|
writeAndClose("JWE Claims", content, out);
|
||||||
plaintext = new ByteArrayInputStream(out.toByteArray());
|
plaintext = Streams.of(out.toByteArray());
|
||||||
} else {
|
} else {
|
||||||
plaintext = content.toInputStream();
|
plaintext = content.toInputStream();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import io.jsonwebtoken.ProtectedHeader;
|
||||||
import io.jsonwebtoken.SigningKeyResolver;
|
import io.jsonwebtoken.SigningKeyResolver;
|
||||||
import io.jsonwebtoken.UnsupportedJwtException;
|
import io.jsonwebtoken.UnsupportedJwtException;
|
||||||
import io.jsonwebtoken.impl.io.AbstractParser;
|
import io.jsonwebtoken.impl.io.AbstractParser;
|
||||||
|
import io.jsonwebtoken.impl.io.BytesInputStream;
|
||||||
import io.jsonwebtoken.impl.io.CharSequenceReader;
|
import io.jsonwebtoken.impl.io.CharSequenceReader;
|
||||||
import io.jsonwebtoken.impl.io.JsonObjectDeserializer;
|
import io.jsonwebtoken.impl.io.JsonObjectDeserializer;
|
||||||
import io.jsonwebtoken.impl.io.Streams;
|
import io.jsonwebtoken.impl.io.Streams;
|
||||||
|
@ -56,7 +57,6 @@ import io.jsonwebtoken.io.Decoder;
|
||||||
import io.jsonwebtoken.io.DeserializationException;
|
import io.jsonwebtoken.io.DeserializationException;
|
||||||
import io.jsonwebtoken.io.Deserializer;
|
import io.jsonwebtoken.io.Deserializer;
|
||||||
import io.jsonwebtoken.lang.Assert;
|
import io.jsonwebtoken.lang.Assert;
|
||||||
import io.jsonwebtoken.lang.Classes;
|
|
||||||
import io.jsonwebtoken.lang.Collections;
|
import io.jsonwebtoken.lang.Collections;
|
||||||
import io.jsonwebtoken.lang.DateFormats;
|
import io.jsonwebtoken.lang.DateFormats;
|
||||||
import io.jsonwebtoken.lang.Objects;
|
import io.jsonwebtoken.lang.Objects;
|
||||||
|
@ -74,7 +74,6 @@ import io.jsonwebtoken.security.WeakKeyException;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
@ -315,7 +314,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
||||||
bb.rewind();
|
bb.rewind();
|
||||||
byte[] data = new byte[bb.remaining()];
|
byte[] data = new byte[bb.remaining()];
|
||||||
bb.get(data);
|
bb.get(data);
|
||||||
verificationInput = new ByteArrayInputStream(data);
|
verificationInput = Streams.of(data);
|
||||||
} else { // b64 extension
|
} else { // b64 extension
|
||||||
ByteBuffer headerBuf = StandardCharsets.US_ASCII.encode(Strings.wrap(tokenized.getProtected()));
|
ByteBuffer headerBuf = StandardCharsets.US_ASCII.encode(Strings.wrap(tokenized.getProtected()));
|
||||||
headerBuf.rewind();
|
headerBuf.rewind();
|
||||||
|
@ -325,7 +324,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
||||||
buf.rewind();
|
buf.rewind();
|
||||||
byte[] data = new byte[buf.remaining()];
|
byte[] data = new byte[buf.remaining()];
|
||||||
buf.get(data);
|
buf.get(data);
|
||||||
InputStream prefixStream = new ByteArrayInputStream(data);
|
InputStream prefixStream = Streams.of(data);
|
||||||
payloadStream = payload.toInputStream();
|
payloadStream = payload.toInputStream();
|
||||||
// We wrap the payloadStream here in an UncloseableInputStream to prevent the SequenceInputStream from
|
// We wrap the payloadStream here in an UncloseableInputStream to prevent the SequenceInputStream from
|
||||||
// closing it since we'll need to rewind/reset it if decompression is enabled
|
// closing it since we'll need to rewind/reset it if decompression is enabled
|
||||||
|
@ -378,7 +377,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
||||||
|
|
||||||
// =============== Header =================
|
// =============== Header =================
|
||||||
final byte[] headerBytes = decode(base64UrlHeader, "protected header");
|
final byte[] headerBytes = decode(base64UrlHeader, "protected header");
|
||||||
Map<String, ?> m = deserialize(new ByteArrayInputStream(headerBytes), "protected header");
|
Map<String, ?> m = deserialize(Streams.of(headerBytes), "protected header");
|
||||||
Header header;
|
Header header;
|
||||||
try {
|
try {
|
||||||
header = tokenized.createHeader(m);
|
header = tokenized.createHeader(m);
|
||||||
|
@ -517,7 +516,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
||||||
ByteBuffer buf = StandardCharsets.US_ASCII.encode(Strings.wrap(base64UrlHeader));
|
ByteBuffer buf = StandardCharsets.US_ASCII.encode(Strings.wrap(base64UrlHeader));
|
||||||
final byte[] aadBytes = new byte[buf.remaining()];
|
final byte[] aadBytes = new byte[buf.remaining()];
|
||||||
buf.get(aadBytes);
|
buf.get(aadBytes);
|
||||||
InputStream aad = new ByteArrayInputStream(aadBytes);
|
InputStream aad = Streams.of(aadBytes);
|
||||||
|
|
||||||
base64Url = base64UrlDigest;
|
base64Url = base64UrlDigest;
|
||||||
//guaranteed to be non-empty via the `alg` + digest check above:
|
//guaranteed to be non-empty via the `alg` + digest check above:
|
||||||
|
@ -834,8 +833,8 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Payload payloadFor(InputStream in) {
|
private static Payload payloadFor(InputStream in) {
|
||||||
if (in instanceof ByteArrayInputStream) {
|
if (in instanceof BytesInputStream) {
|
||||||
byte[] data = Classes.getFieldValue(in, "buf", byte[].class);
|
byte[] data = Streams.bytes(in, "Unable to obtain payload InputStream bytes.");
|
||||||
return new Payload(data, null);
|
return new Payload(data, null);
|
||||||
}
|
}
|
||||||
//if (in.markSupported()) in.mark(0);
|
//if (in.markSupported()) in.mark(0);
|
||||||
|
@ -874,7 +873,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
||||||
|
|
||||||
protected byte[] decode(CharSequence base64UrlEncoded, String name) {
|
protected byte[] decode(CharSequence base64UrlEncoded, String name) {
|
||||||
try {
|
try {
|
||||||
InputStream decoding = this.decoder.decode(new ByteArrayInputStream(Strings.utf8(base64UrlEncoded)));
|
InputStream decoding = this.decoder.decode(Streams.of(Strings.utf8(base64UrlEncoded)));
|
||||||
return Streams.bytes(decoding, "Unable to Base64Url-decode input.");
|
return Streams.bytes(decoding, "Unable to Base64Url-decode input.");
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// Don't disclose potentially-sensitive information per https://github.com/jwtk/jjwt/issues/824:
|
// Don't disclose potentially-sensitive information per https://github.com/jwtk/jjwt/issues/824:
|
||||||
|
|
|
@ -18,13 +18,13 @@ package io.jsonwebtoken.impl;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.CompressionCodec;
|
import io.jsonwebtoken.CompressionCodec;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.impl.io.Streams;
|
||||||
import io.jsonwebtoken.impl.lang.Bytes;
|
import io.jsonwebtoken.impl.lang.Bytes;
|
||||||
import io.jsonwebtoken.io.CompressionAlgorithm;
|
import io.jsonwebtoken.io.CompressionAlgorithm;
|
||||||
import io.jsonwebtoken.lang.Assert;
|
import io.jsonwebtoken.lang.Assert;
|
||||||
import io.jsonwebtoken.lang.Collections;
|
import io.jsonwebtoken.lang.Collections;
|
||||||
import io.jsonwebtoken.lang.Strings;
|
import io.jsonwebtoken.lang.Strings;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
@ -68,10 +68,10 @@ class Payload {
|
||||||
}
|
}
|
||||||
this.bytes = data;
|
this.bytes = data;
|
||||||
if (in == null && !Bytes.isEmpty(this.bytes)) {
|
if (in == null && !Bytes.isEmpty(this.bytes)) {
|
||||||
in = new ByteArrayInputStream(data);
|
in = Streams.of(data);
|
||||||
}
|
}
|
||||||
this.inputStreamEmpty = in == null;
|
this.inputStreamEmpty = in == null;
|
||||||
this.inputStream = this.inputStreamEmpty ? new ByteArrayInputStream(Bytes.EMPTY) : in;
|
this.inputStream = this.inputStreamEmpty ? Streams.of(Bytes.EMPTY) : in;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isClaims() {
|
boolean isClaims() {
|
||||||
|
|
|
@ -27,7 +27,6 @@ import io.jsonwebtoken.lang.Assert;
|
||||||
import io.jsonwebtoken.lang.Objects;
|
import io.jsonwebtoken.lang.Objects;
|
||||||
import io.jsonwebtoken.lang.Strings;
|
import io.jsonwebtoken.lang.Strings;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -152,7 +151,7 @@ public abstract class AbstractCompressionAlgorithm implements CompressionAlgorit
|
||||||
* @throws IOException if the decompression runs into an IO problem
|
* @throws IOException if the decompression runs into an IO problem
|
||||||
*/
|
*/
|
||||||
protected byte[] doDecompress(byte[] compressed) throws IOException {
|
protected byte[] doDecompress(byte[] compressed) throws IOException {
|
||||||
InputStream is = new ByteArrayInputStream(compressed);
|
InputStream is = Streams.of(compressed);
|
||||||
InputStream decompress = decompress(is);
|
InputStream decompress = decompress(is);
|
||||||
byte[] buffer = new byte[512];
|
byte[] buffer = new byte[512];
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
|
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2023 jsonwebtoken.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package io.jsonwebtoken.impl.io;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.lang.Bytes;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows read access to the internal byte array, avoiding the need copy/extract to a
|
||||||
|
* {@link java.io.ByteArrayOutputStream}.
|
||||||
|
*
|
||||||
|
* @since JJWT_RELEASE_VERSION
|
||||||
|
*/
|
||||||
|
public final class BytesInputStream extends ByteArrayInputStream {
|
||||||
|
|
||||||
|
BytesInputStream(byte[] buf) {
|
||||||
|
super(Bytes.isEmpty(buf) ? Bytes.EMPTY : buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return this.buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ import io.jsonwebtoken.io.DecodingException;
|
||||||
import io.jsonwebtoken.lang.Assert;
|
import io.jsonwebtoken.lang.Assert;
|
||||||
import io.jsonwebtoken.lang.Strings;
|
import io.jsonwebtoken.lang.Strings;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||||
|
@ -38,7 +37,7 @@ public class DelegateStringDecoder implements Decoder<InputStream, InputStream>
|
||||||
try {
|
try {
|
||||||
byte[] data = Streams.bytes(in, "Unable to Base64URL-decode input.");
|
byte[] data = Streams.bytes(in, "Unable to Base64URL-decode input.");
|
||||||
data = delegate.decode(Strings.utf8(data));
|
data = delegate.decode(Strings.utf8(data));
|
||||||
return new ByteArrayInputStream(data);
|
return Streams.of(data);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
String msg = "Unable to Base64Url-decode InputStream: " + t.getMessage();
|
String msg = "Unable to Base64Url-decode InputStream: " + t.getMessage();
|
||||||
throw new DecodingException(msg, t);
|
throw new DecodingException(msg, t);
|
||||||
|
|
|
@ -15,15 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.io;
|
package io.jsonwebtoken.impl.io;
|
||||||
|
|
||||||
import io.jsonwebtoken.impl.lang.AddOpens;
|
|
||||||
import io.jsonwebtoken.impl.lang.Bytes;
|
import io.jsonwebtoken.impl.lang.Bytes;
|
||||||
import io.jsonwebtoken.lang.Assert;
|
import io.jsonwebtoken.lang.Assert;
|
||||||
import io.jsonwebtoken.lang.Classes;
|
|
||||||
import io.jsonwebtoken.lang.Objects;
|
import io.jsonwebtoken.lang.Objects;
|
||||||
import io.jsonwebtoken.lang.Strings;
|
import io.jsonwebtoken.lang.Strings;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.Flushable;
|
import java.io.Flushable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -43,30 +40,26 @@ public class Streams {
|
||||||
*/
|
*/
|
||||||
public static final int EOF = -1;
|
public static final int EOF = -1;
|
||||||
|
|
||||||
static {
|
|
||||||
// For reflective access to ByteArrayInputStream via the 'bytes' static method on >= JDK 9:
|
|
||||||
AddOpens.open("java.base", "java.io");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] bytes(final InputStream in, String exmsg) {
|
public static byte[] bytes(final InputStream in, String exmsg) {
|
||||||
if (in instanceof ByteArrayInputStream) {
|
if (in instanceof BytesInputStream) {
|
||||||
return Classes.getFieldValue(in, "buf", byte[].class);
|
return ((BytesInputStream) in).getBytes();
|
||||||
}
|
}
|
||||||
|
// otherwise we have to copy over:
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
|
ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
|
||||||
copy(in, out, new byte[8192], exmsg);
|
copy(in, out, new byte[8192], exmsg);
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteArrayInputStream of(byte[] bytes) {
|
public static InputStream of(byte[] bytes) {
|
||||||
return Bytes.isEmpty(bytes) ? new ByteArrayInputStream(Bytes.EMPTY) : new ByteArrayInputStream(bytes);
|
return new BytesInputStream(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ByteArrayInputStream of(CharSequence seq) {
|
public static InputStream of(CharSequence seq) {
|
||||||
return of(Strings.utf8(seq));
|
return of(Strings.utf8(seq));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Reader reader(byte[] bytes) {
|
public static Reader reader(byte[] bytes) {
|
||||||
return reader(new ByteArrayInputStream(bytes));
|
return reader(Streams.of(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Reader reader(InputStream in) {
|
public static Reader reader(InputStream in) {
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Stefan Zobel
|
|
||||||
* Copyright © 2023 jsonwebtoken.io
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.jsonwebtoken.impl.lang;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility class that allows to open arbitrary packages to the calling module
|
|
||||||
* at runtime, so it is a kind of dynamic device for "--add-opens" that could be
|
|
||||||
* used inside libraries instead of forcing the application to be run with
|
|
||||||
* command line parameters like "--add-opens java.base/java.util=ALL-UNNAMED" or
|
|
||||||
* having the "Add-Opens:" entries supplied in the application Jar manifest.
|
|
||||||
* Note that this still works in the Java 17 GA release, dated 2021-09-14 but it
|
|
||||||
* may break at any time in the future (theoretically even for a minor
|
|
||||||
* release!).
|
|
||||||
*
|
|
||||||
* @since 0.12.1, gratefully copied from <a href="https://github.com/stefan-zobel/wip/blob/b74e927edddf19a5dce7c8610835f620c0b6f557/src/main/java/misc/AddOpens.java">https://github.com/stefan-zobel/wip/blob/b74e927edddf19a5dce7c8610835f620c0b6f557/src/main/java/misc/AddOpens.java</a>
|
|
||||||
* under the terms of the Apache 2 open source license (same as the JJWT license).
|
|
||||||
*/
|
|
||||||
public final class AddOpens {
|
|
||||||
|
|
||||||
// field offset of the override field (Warning: this may change at any time!)
|
|
||||||
private static final long OVERRIDE_OFFSET = 12;
|
|
||||||
private static final sun.misc.Unsafe U = getUnsafe();
|
|
||||||
|
|
||||||
private AddOpens() {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open one or more packages in the given module to the current module. Example
|
|
||||||
* usage:
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* boolean success = AddOpens.open("java.base", "java.util", "java.net");
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* @param moduleName the module you want to open
|
|
||||||
* @param packageNames packages in that module you want to be opened
|
|
||||||
* @return {@code true} if the open operation has succeeded for all packages,
|
|
||||||
* otherwise {@code false}
|
|
||||||
*/
|
|
||||||
public static boolean open(String moduleName, String... packageNames) {
|
|
||||||
// Use reflection so that this code can run on Java 8
|
|
||||||
Class<?> javaLangModule;
|
|
||||||
try {
|
|
||||||
javaLangModule = Class.forName("java.lang.Module");
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// we must be < Java 9
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// the module we are currently running in (either named or unnamed)
|
|
||||||
Object thisModule = getCurrentModule();
|
|
||||||
// find the module to open
|
|
||||||
Object targetModule = findModule(moduleName);
|
|
||||||
// get the method that is also used by "--add-opens"
|
|
||||||
Method m = javaLangModule.getDeclaredMethod("implAddOpens", String.class, javaLangModule);
|
|
||||||
// override language-level access checks
|
|
||||||
setAccessible(m);
|
|
||||||
// open given packages in the target module to this module
|
|
||||||
for (String package_ : packageNames) {
|
|
||||||
m.invoke(targetModule, package_, thisModule);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (Throwable ignore) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object findModule(String moduleName) {
|
|
||||||
// Use reflection so that this code can run on Java 8
|
|
||||||
try {
|
|
||||||
Class<?> moduleLayerClass = Class.forName("java.lang.ModuleLayer");
|
|
||||||
Method bootMethod = moduleLayerClass.getDeclaredMethod("boot");
|
|
||||||
Object bootLayer = bootMethod.invoke(null);
|
|
||||||
Method findModuleMethod = moduleLayerClass.getDeclaredMethod("findModule", String.class);
|
|
||||||
Object optionalModule = findModuleMethod.invoke(bootLayer, moduleName);
|
|
||||||
Class<?> optionalClass = Class.forName("java.util.Optional");
|
|
||||||
Method getMethod = optionalClass.getDeclaredMethod("get");
|
|
||||||
return getMethod.invoke(optionalModule);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getCurrentModule() {
|
|
||||||
// Use reflection so that this code can run on Java 8
|
|
||||||
try {
|
|
||||||
Method m = Class.class.getDeclaredMethod("getModule");
|
|
||||||
setAccessible(m);
|
|
||||||
return m.invoke(AddOpens.class);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setAccessible(Method method) {
|
|
||||||
if (U != null) {
|
|
||||||
U.putBoolean(method, OVERRIDE_OFFSET, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static sun.misc.Unsafe getUnsafe() {
|
|
||||||
try {
|
|
||||||
Field unsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
|
||||||
unsafe.setAccessible(true);
|
|
||||||
return (sun.misc.Unsafe) unsafe.get(null);
|
|
||||||
} catch (Throwable ignore) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security;
|
package io.jsonwebtoken.impl.security;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.io.Streams;
|
||||||
import io.jsonwebtoken.impl.lang.Nameable;
|
import io.jsonwebtoken.impl.lang.Nameable;
|
||||||
import io.jsonwebtoken.impl.lang.Parameter;
|
import io.jsonwebtoken.impl.lang.Parameter;
|
||||||
import io.jsonwebtoken.impl.lang.ParameterReadable;
|
import io.jsonwebtoken.impl.lang.ParameterReadable;
|
||||||
|
@ -31,7 +32,6 @@ import io.jsonwebtoken.security.JwkThumbprint;
|
||||||
import io.jsonwebtoken.security.Jwks;
|
import io.jsonwebtoken.security.Jwks;
|
||||||
import io.jsonwebtoken.security.KeyOperation;
|
import io.jsonwebtoken.security.KeyOperation;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
|
@ -152,7 +152,7 @@ public abstract class AbstractJwk<K extends Key> implements Jwk<K>, ParameterRea
|
||||||
String json = toThumbprintJson();
|
String json = toThumbprintJson();
|
||||||
Assert.hasText(json, "Canonical JWK Thumbprint JSON cannot be null or empty.");
|
Assert.hasText(json, "Canonical JWK Thumbprint JSON cannot be null or empty.");
|
||||||
byte[] bytes = json.getBytes(StandardCharsets.UTF_8); // https://www.rfc-editor.org/rfc/rfc7638#section-3 #2
|
byte[] bytes = json.getBytes(StandardCharsets.UTF_8); // https://www.rfc-editor.org/rfc/rfc7638#section-3 #2
|
||||||
InputStream in = new ByteArrayInputStream(bytes);
|
InputStream in = Streams.of(bytes);
|
||||||
byte[] digest = alg.digest(new DefaultRequest<>(in, this.context.getProvider(), this.context.getRandom()));
|
byte[] digest = alg.digest(new DefaultRequest<>(in, this.context.getProvider(), this.context.getRandom()));
|
||||||
return new DefaultJwkThumbprint(digest, alg);
|
return new DefaultJwkThumbprint(digest, alg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,9 +135,8 @@ final class DefaultMacAlgorithm extends AbstractSecureDigestAlgorithm<SecretKey,
|
||||||
throw new InvalidKeyException(msg);
|
throw new InvalidKeyException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can ignore PKCS11 key name assertions for two reasons:
|
// We can ignore PKCS11 key name assertions because HSM module key algorithm names don't always align with
|
||||||
// 1. HSM module key algorithm names don't always align with JCA standard algorithm names, and
|
// JCA standard algorithm names:
|
||||||
// 2. Our KeysBridge.findBitLength implementation can extract the key length so we can still validate with that
|
|
||||||
boolean pkcs11Key = KeysBridge.isSunPkcs11GenericSecret(key);
|
boolean pkcs11Key = KeysBridge.isSunPkcs11GenericSecret(key);
|
||||||
|
|
||||||
//assert key's jca name is valid if it's a JWA standard algorithm:
|
//assert key's jca name is valid if it's a JWA standard algorithm:
|
||||||
|
|
|
@ -26,7 +26,6 @@ import io.jsonwebtoken.security.DecryptAeadRequest;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.SequenceInputStream;
|
import java.io.SequenceInputStream;
|
||||||
|
@ -93,7 +92,7 @@ public class GcmAesAeadAlgorithm extends AesAlgorithm implements AeadAlgorithm {
|
||||||
final AlgorithmParameterSpec ivSpec = getIvSpec(iv);
|
final AlgorithmParameterSpec ivSpec = getIvSpec(iv);
|
||||||
|
|
||||||
//for tagged GCM, the JCA spec requires that the tag be appended to the end of the ciphertext byte array:
|
//for tagged GCM, the JCA spec requires that the tag be appended to the end of the ciphertext byte array:
|
||||||
final InputStream taggedCiphertext = new SequenceInputStream(ciphertext, new ByteArrayInputStream(tag));
|
final InputStream taggedCiphertext = new SequenceInputStream(ciphertext, Streams.of(tag));
|
||||||
|
|
||||||
jca(req).withCipher(new CheckedFunction<Cipher, byte[]>() {
|
jca(req).withCipher(new CheckedFunction<Cipher, byte[]>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,7 +31,6 @@ import io.jsonwebtoken.security.SignatureException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -145,11 +144,11 @@ public class HmacAesAeadAlgorithm extends AesAlgorithm implements AeadAlgorithm
|
||||||
|
|
||||||
Collection<InputStream> streams = new ArrayList<>(4);
|
Collection<InputStream> streams = new ArrayList<>(4);
|
||||||
if (!Bytes.isEmpty(aad)) { // must come first if it exists
|
if (!Bytes.isEmpty(aad)) { // must come first if it exists
|
||||||
streams.add(new ByteArrayInputStream(aad));
|
streams.add(Streams.of(aad));
|
||||||
}
|
}
|
||||||
streams.add(new ByteArrayInputStream(iv));
|
streams.add(Streams.of(iv));
|
||||||
streams.add(ciphertext);
|
streams.add(ciphertext);
|
||||||
streams.add(new ByteArrayInputStream(AL));
|
streams.add(Streams.of(AL));
|
||||||
InputStream in = new SequenceInputStream(Collections.enumeration(streams));
|
InputStream in = new SequenceInputStream(Collections.enumeration(streams));
|
||||||
|
|
||||||
SecretKey key = new SecretKeySpec(macKeyBytes, SIGALG.getJcaName());
|
SecretKey key = new SecretKeySpec(macKeyBytes, SIGALG.getJcaName());
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package io.jsonwebtoken.impl.security;
|
package io.jsonwebtoken.impl.security;
|
||||||
|
|
||||||
import io.jsonwebtoken.Identifiable;
|
import io.jsonwebtoken.Identifiable;
|
||||||
|
import io.jsonwebtoken.impl.io.Streams;
|
||||||
import io.jsonwebtoken.impl.lang.Bytes;
|
import io.jsonwebtoken.impl.lang.Bytes;
|
||||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||||
import io.jsonwebtoken.impl.lang.CheckedSupplier;
|
import io.jsonwebtoken.impl.lang.CheckedSupplier;
|
||||||
|
@ -36,7 +37,6 @@ import javax.crypto.Mac;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.SecretKeyFactory;
|
import javax.crypto.SecretKeyFactory;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.AlgorithmParameters;
|
import java.security.AlgorithmParameters;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
@ -332,7 +332,7 @@ public class JcaTemplate {
|
||||||
return fallback(CertificateFactory.class, new CheckedFunction<CertificateFactory, X509Certificate>() {
|
return fallback(CertificateFactory.class, new CheckedFunction<CertificateFactory, X509Certificate>() {
|
||||||
@Override
|
@Override
|
||||||
public X509Certificate apply(CertificateFactory cf) throws CertificateException {
|
public X509Certificate apply(CertificateFactory cf) throws CertificateException {
|
||||||
InputStream is = new ByteArrayInputStream(x509DerBytes);
|
InputStream is = Streams.of(x509DerBytes);
|
||||||
return (X509Certificate) cf.generateCertificate(is);
|
return (X509Certificate) cf.generateCertificate(is);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,9 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security;
|
package io.jsonwebtoken.impl.security;
|
||||||
|
|
||||||
import io.jsonwebtoken.impl.lang.AddOpens;
|
|
||||||
import io.jsonwebtoken.impl.lang.Bytes;
|
import io.jsonwebtoken.impl.lang.Bytes;
|
||||||
import io.jsonwebtoken.impl.lang.OptionalMethodInvoker;
|
|
||||||
import io.jsonwebtoken.lang.Assert;
|
import io.jsonwebtoken.lang.Assert;
|
||||||
import io.jsonwebtoken.lang.Strings;
|
import io.jsonwebtoken.lang.Strings;
|
||||||
import io.jsonwebtoken.security.InvalidKeyException;
|
import io.jsonwebtoken.security.InvalidKeyException;
|
||||||
|
@ -30,6 +28,8 @@ import javax.crypto.SecretKey;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.interfaces.ECKey;
|
||||||
|
import java.security.interfaces.RSAKey;
|
||||||
|
|
||||||
@SuppressWarnings({"unused"}) // reflection bridge class for the io.jsonwebtoken.security.Keys implementation
|
@SuppressWarnings({"unused"}) // reflection bridge class for the io.jsonwebtoken.security.Keys implementation
|
||||||
public final class KeysBridge {
|
public final class KeysBridge {
|
||||||
|
@ -37,16 +37,6 @@ public final class KeysBridge {
|
||||||
private static final String SUNPKCS11_GENERIC_SECRET_CLASSNAME = "sun.security.pkcs11.P11Key$P11SecretKey";
|
private static final String SUNPKCS11_GENERIC_SECRET_CLASSNAME = "sun.security.pkcs11.P11Key$P11SecretKey";
|
||||||
private static final String SUNPKCS11_GENERIC_SECRET_ALGNAME = "Generic Secret"; // https://github.com/openjdk/jdk/blob/4f90abaf17716493bad740dcef76d49f16d69379/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java#L1292
|
private static final String SUNPKCS11_GENERIC_SECRET_ALGNAME = "Generic Secret"; // https://github.com/openjdk/jdk/blob/4f90abaf17716493bad740dcef76d49f16d69379/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11KeyStore.java#L1292
|
||||||
|
|
||||||
private static final String SUN_KEYUTIL_CLASSNAME = "sun.security.util.KeyUtil";
|
|
||||||
private static final OptionalMethodInvoker<Key, Integer> SUN_KEYSIZE =
|
|
||||||
new OptionalMethodInvoker<>(SUN_KEYUTIL_CLASSNAME, "getKeySize", Key.class, true);
|
|
||||||
private static final String SUN_KEYUTIL_ERR = "Unexpected " + SUN_KEYUTIL_CLASSNAME + " invocation error.";
|
|
||||||
|
|
||||||
static {
|
|
||||||
// For reflective access to KeyUtil on >= JDK 9:
|
|
||||||
AddOpens.open("java.base", "sun.security.util");
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent instantiation
|
// prevent instantiation
|
||||||
private KeysBridge() {
|
private KeysBridge() {
|
||||||
}
|
}
|
||||||
|
@ -119,24 +109,30 @@ public final class KeysBridge {
|
||||||
*/
|
*/
|
||||||
public static int findBitLength(Key key) {
|
public static int findBitLength(Key key) {
|
||||||
|
|
||||||
Integer retval = SUN_KEYSIZE.apply(key);
|
int bitlen = -1;
|
||||||
int bitlen = Assert.stateNotNull(retval, SUN_KEYUTIL_ERR);
|
|
||||||
|
|
||||||
// SunPKCS11 SecretKey lengths are unfortunately reported in bytes, not bits
|
// try to parse the length from key specification
|
||||||
// per https://bugs.openjdk.org/browse/JDK-8163173
|
if (key instanceof SecretKey) {
|
||||||
// (they should be multiplying the PKCS11 CKA_VALUE_LEN value by 8 since their own
|
SecretKey secretKey = (SecretKey) key;
|
||||||
// sun.security.util.Length#getLength() JavaDoc states that values are intended to be in bits, not bytes)
|
if ("RAW".equals(secretKey.getFormat())) {
|
||||||
// So we account for that here:
|
byte[] encoded = findEncoded(secretKey);
|
||||||
if (bitlen > 0 && isSunPkcs11GenericSecret(key)) {
|
if (!Bytes.isEmpty(encoded)) {
|
||||||
bitlen *= Byte.SIZE;
|
bitlen = (int) Bytes.bitLength(encoded);
|
||||||
|
Bytes.clear(encoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key instanceof RSAKey) {
|
||||||
|
RSAKey rsaKey = (RSAKey) key;
|
||||||
|
bitlen = rsaKey.getModulus().bitLength();
|
||||||
|
} else if (key instanceof ECKey) {
|
||||||
|
ECKey ecKey = (ECKey) key;
|
||||||
|
bitlen = ecKey.getParams().getOrder().bitLength();
|
||||||
|
} else {
|
||||||
|
// We can check additional logic for EdwardsCurve even if the current JDK version doesn't support it:
|
||||||
|
EdwardsCurve curve = EdwardsCurve.findByKey(key);
|
||||||
|
if (curve != null) bitlen = curve.getKeyBitLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitlen > 0) return bitlen;
|
|
||||||
|
|
||||||
// We can check additional logic for EdwardsCurve even if the current JDK version doesn't support it:
|
|
||||||
EdwardsCurve curve = EdwardsCurve.findByKey(key);
|
|
||||||
if (curve != null) bitlen = curve.getKeyBitLength();
|
|
||||||
|
|
||||||
return bitlen;
|
return bitlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package io.jsonwebtoken.impl.security;
|
package io.jsonwebtoken.impl.security;
|
||||||
|
|
||||||
import io.jsonwebtoken.impl.ParameterMap;
|
import io.jsonwebtoken.impl.ParameterMap;
|
||||||
|
import io.jsonwebtoken.impl.io.Streams;
|
||||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||||
import io.jsonwebtoken.impl.lang.Function;
|
import io.jsonwebtoken.impl.lang.Function;
|
||||||
import io.jsonwebtoken.impl.lang.Functions;
|
import io.jsonwebtoken.impl.lang.Functions;
|
||||||
|
@ -27,7 +28,6 @@ import io.jsonwebtoken.security.Jwks;
|
||||||
import io.jsonwebtoken.security.Request;
|
import io.jsonwebtoken.security.Request;
|
||||||
import io.jsonwebtoken.security.X509Builder;
|
import io.jsonwebtoken.security.X509Builder;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
@ -99,7 +99,7 @@ public class X509BuilderSupport implements X509Builder<X509BuilderSupport> {
|
||||||
|
|
||||||
private byte[] computeThumbprint(final X509Certificate cert, HashAlgorithm alg) {
|
private byte[] computeThumbprint(final X509Certificate cert, HashAlgorithm alg) {
|
||||||
byte[] encoded = GET_X509_BYTES.apply(cert);
|
byte[] encoded = GET_X509_BYTES.apply(cert);
|
||||||
InputStream in = new ByteArrayInputStream(encoded);
|
InputStream in = Streams.of(encoded);
|
||||||
Request<InputStream> request = new DefaultRequest<>(in, null, null);
|
Request<InputStream> request = new DefaultRequest<>(in, null, null);
|
||||||
return alg.digest(request);
|
return alg.digest(request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package io.jsonwebtoken
|
||||||
import io.jsonwebtoken.SignatureAlgorithm
|
import io.jsonwebtoken.SignatureAlgorithm
|
||||||
import io.jsonwebtoken.impl.*
|
import io.jsonwebtoken.impl.*
|
||||||
import io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm
|
import io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.impl.lang.Bytes
|
import io.jsonwebtoken.impl.lang.Bytes
|
||||||
import io.jsonwebtoken.impl.lang.Services
|
import io.jsonwebtoken.impl.lang.Services
|
||||||
import io.jsonwebtoken.impl.security.*
|
import io.jsonwebtoken.impl.security.*
|
||||||
|
@ -141,7 +142,7 @@ class JwtsTest {
|
||||||
def h = base64Url('{"alg":"HS256"}')
|
def h = base64Url('{"alg":"HS256"}')
|
||||||
def c = base64Url('{"sub":"joe","exp":"-42-"}')
|
def c = base64Url('{"sub":"joe","exp":"-42-"}')
|
||||||
def data = Strings.utf8(("$h.$c" as String))
|
def data = Strings.utf8(("$h.$c" as String))
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def request = new DefaultSecureRequest<>(payload, null, null, key)
|
def request = new DefaultSecureRequest<>(payload, null, null, key)
|
||||||
def result = Jwts.SIG.HS256.digest(request)
|
def result = Jwts.SIG.HS256.digest(request)
|
||||||
def sig = Encoders.BASE64URL.encode(result)
|
def sig = Encoders.BASE64URL.encode(result)
|
||||||
|
@ -191,7 +192,7 @@ class JwtsTest {
|
||||||
@Test
|
@Test
|
||||||
void testContentStreamWithContentType() {
|
void testContentStreamWithContentType() {
|
||||||
String s = 'Hello JJWT'
|
String s = 'Hello JJWT'
|
||||||
InputStream content = new ByteArrayInputStream(Strings.utf8(s))
|
InputStream content = Streams.of(Strings.utf8(s))
|
||||||
String cty = 'text/plain'
|
String cty = 'text/plain'
|
||||||
String compact = Jwts.builder().content(content, cty).compact()
|
String compact = Jwts.builder().content(content, cty).compact()
|
||||||
def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)
|
def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)
|
||||||
|
@ -202,7 +203,7 @@ class JwtsTest {
|
||||||
@Test
|
@Test
|
||||||
void testContentStreamWithoutContentType() {
|
void testContentStreamWithoutContentType() {
|
||||||
String s = 'Hello JJWT'
|
String s = 'Hello JJWT'
|
||||||
InputStream content = new ByteArrayInputStream(Strings.utf8(s))
|
InputStream content = Streams.of(Strings.utf8(s))
|
||||||
String compact = Jwts.builder().content(content).compact()
|
String compact = Jwts.builder().content(content).compact()
|
||||||
def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)
|
def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)
|
||||||
assertNull jwt.header.getContentType()
|
assertNull jwt.header.getContentType()
|
||||||
|
|
|
@ -17,6 +17,7 @@ package io.jsonwebtoken
|
||||||
|
|
||||||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||||
import io.jsonwebtoken.impl.DefaultJwtParser
|
import io.jsonwebtoken.impl.DefaultJwtParser
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.impl.lang.Bytes
|
import io.jsonwebtoken.impl.lang.Bytes
|
||||||
import io.jsonwebtoken.impl.lang.Services
|
import io.jsonwebtoken.impl.lang.Services
|
||||||
import io.jsonwebtoken.impl.security.TestKeys
|
import io.jsonwebtoken.impl.security.TestKeys
|
||||||
|
@ -69,8 +70,8 @@ class RFC7797Test {
|
||||||
String s = Jwts.builder().signWith(key).content(content).encodePayload(false).compact()
|
String s = Jwts.builder().signWith(key).content(content).encodePayload(false).compact()
|
||||||
|
|
||||||
// But verify with 3 types of sources: string, byte array, and two different kinds of InputStreams:
|
// But verify with 3 types of sources: string, byte array, and two different kinds of InputStreams:
|
||||||
InputStream asByteInputStream = new ByteArrayInputStream(content)
|
InputStream asByteInputStream = Streams.of(content)
|
||||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(content))
|
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))
|
||||||
|
|
||||||
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
||||||
def parser = Jwts.parser().verifyWith(key).build()
|
def parser = Jwts.parser().verifyWith(key).build()
|
||||||
|
@ -107,8 +108,8 @@ class RFC7797Test {
|
||||||
String s = Jwts.builder().signWith(key).content(content).encodePayload(false).compact()
|
String s = Jwts.builder().signWith(key).content(content).encodePayload(false).compact()
|
||||||
|
|
||||||
// But verify with 3 types of sources: string, byte array, and two different kinds of InputStreams:
|
// But verify with 3 types of sources: string, byte array, and two different kinds of InputStreams:
|
||||||
InputStream asByteInputStream = new ByteArrayInputStream(content)
|
InputStream asByteInputStream = Streams.of(content)
|
||||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(content))
|
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))
|
||||||
|
|
||||||
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
||||||
def parser = Jwts.parser().verifyWith(key).build()
|
def parser = Jwts.parser().verifyWith(key).build()
|
||||||
|
@ -128,13 +129,13 @@ class RFC7797Test {
|
||||||
def key = TestKeys.HS256
|
def key = TestKeys.HS256
|
||||||
|
|
||||||
byte[] content = Strings.utf8('$.02') // https://datatracker.ietf.org/doc/html/rfc7797#section-4.2
|
byte[] content = Strings.utf8('$.02') // https://datatracker.ietf.org/doc/html/rfc7797#section-4.2
|
||||||
InputStream contentStream = new ByteArrayInputStream(content)
|
InputStream contentStream = Streams.of(content)
|
||||||
|
|
||||||
String s = Jwts.builder().signWith(key).content(contentStream).encodePayload(false).compact()
|
String s = Jwts.builder().signWith(key).content(contentStream).encodePayload(false).compact()
|
||||||
|
|
||||||
// But verify with 3 types of sources: byte array, and two different kinds of InputStreams:
|
// But verify with 3 types of sources: byte array, and two different kinds of InputStreams:
|
||||||
InputStream asByteInputStream = new ByteArrayInputStream(content)
|
InputStream asByteInputStream =Streams.of(content)
|
||||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(content))
|
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))
|
||||||
|
|
||||||
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
||||||
def parser = Jwts.parser().verifyWith(key).build()
|
def parser = Jwts.parser().verifyWith(key).build()
|
||||||
|
@ -348,8 +349,8 @@ class RFC7797Test {
|
||||||
.compact()
|
.compact()
|
||||||
|
|
||||||
// But verify with 3 types of sources: byte array, and two different kinds of InputStreams:
|
// But verify with 3 types of sources: byte array, and two different kinds of InputStreams:
|
||||||
InputStream asByteInputStream = new ByteArrayInputStream(compressed)
|
InputStream asByteInputStream = Streams.of(compressed)
|
||||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(compressed))
|
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(compressed))
|
||||||
|
|
||||||
for (def payload : [compressed, asByteInputStream, asBufferedInputStream]) {
|
for (def payload : [compressed, asByteInputStream, asBufferedInputStream]) {
|
||||||
def parser = Jwts.parser().verifyWith(key).build()
|
def parser = Jwts.parser().verifyWith(key).build()
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package io.jsonwebtoken.impl
|
package io.jsonwebtoken.impl
|
||||||
|
|
||||||
import io.jsonwebtoken.Jwts
|
import io.jsonwebtoken.Jwts
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.impl.lang.Bytes
|
import io.jsonwebtoken.impl.lang.Bytes
|
||||||
import io.jsonwebtoken.impl.security.DefaultHashAlgorithm
|
import io.jsonwebtoken.impl.security.DefaultHashAlgorithm
|
||||||
import io.jsonwebtoken.impl.security.DefaultRequest
|
import io.jsonwebtoken.impl.security.DefaultRequest
|
||||||
|
@ -269,7 +270,7 @@ class DefaultMutableJweHeaderTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testX509Sha1Thumbprint() {
|
void testX509Sha1Thumbprint() {
|
||||||
def payload = new ByteArrayInputStream(TestKeys.RS256.cert.getEncoded())
|
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
|
||||||
def request = new DefaultRequest(payload, null, null)
|
def request = new DefaultRequest(payload, null, null)
|
||||||
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
||||||
String encoded = Encoders.BASE64URL.encode(x5t)
|
String encoded = Encoders.BASE64URL.encode(x5t)
|
||||||
|
@ -285,7 +286,7 @@ class DefaultMutableJweHeaderTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testX509Sha256Thumbprint() {
|
void testX509Sha256Thumbprint() {
|
||||||
def payload = new ByteArrayInputStream(TestKeys.RS256.cert.getEncoded())
|
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
|
||||||
def request = new DefaultRequest(payload, null, null)
|
def request = new DefaultRequest(payload, null, null)
|
||||||
def x5tS256 = Jwks.HASH.@SHA256.digest(request)
|
def x5tS256 = Jwks.HASH.@SHA256.digest(request)
|
||||||
String encoded = Encoders.BASE64URL.encode(x5tS256)
|
String encoded = Encoders.BASE64URL.encode(x5tS256)
|
||||||
|
|
|
@ -25,7 +25,7 @@ class CountingInputStreamTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void readEmpty() {
|
void readEmpty() {
|
||||||
def stream = new CountingInputStream(new ByteArrayInputStream(Bytes.EMPTY))
|
def stream = new CountingInputStream(Streams.of(Bytes.EMPTY))
|
||||||
stream.read()
|
stream.read()
|
||||||
assertEquals 0, stream.getCount()
|
assertEquals 0, stream.getCount()
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class CountingInputStreamTest {
|
||||||
void readSingle() {
|
void readSingle() {
|
||||||
def single = (byte) 0x18 // any random byte is fine
|
def single = (byte) 0x18 // any random byte is fine
|
||||||
def data = new byte[1]; data[0] = single
|
def data = new byte[1]; data[0] = single
|
||||||
def stream = new CountingInputStream(new ByteArrayInputStream(data))
|
def stream = new CountingInputStream(Streams.of(data))
|
||||||
assertEquals single, stream.read()
|
assertEquals single, stream.read()
|
||||||
assertEquals 1, stream.getCount()
|
assertEquals 1, stream.getCount()
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ class CountingInputStreamTest {
|
||||||
@Test
|
@Test
|
||||||
void testSkip() {
|
void testSkip() {
|
||||||
def data = Strings.utf8('hello world')
|
def data = Strings.utf8('hello world')
|
||||||
def stream = new CountingInputStream(new ByteArrayInputStream(data))
|
def stream = new CountingInputStream(Streams.of(data))
|
||||||
stream.skip(6)
|
stream.skip(6)
|
||||||
assertEquals 6, stream.getCount()
|
assertEquals 6, stream.getCount()
|
||||||
int w = ('w' as char)
|
int w = ('w' as char)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class DelegateStringDecoderTest {
|
||||||
void decode() {
|
void decode() {
|
||||||
def value = 'test'
|
def value = 'test'
|
||||||
def bytes = Strings.utf8(value)
|
def bytes = Strings.utf8(value)
|
||||||
def ins = new ByteArrayInputStream(bytes)
|
def ins = Streams.of(bytes)
|
||||||
def test = new Decoder<CharSequence, byte[]>() {
|
def test = new Decoder<CharSequence, byte[]>() {
|
||||||
@Override
|
@Override
|
||||||
byte[] decode(CharSequence s) throws DecodingException {
|
byte[] decode(CharSequence s) throws DecodingException {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.io.Encoders
|
import io.jsonwebtoken.io.Encoders
|
||||||
import io.jsonwebtoken.security.*
|
import io.jsonwebtoken.security.*
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -64,7 +65,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testX509CertificateSha1Thumbprint() {
|
void testX509CertificateSha1Thumbprint() {
|
||||||
def payload = new ByteArrayInputStream(TestKeys.RS256.cert.getEncoded())
|
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
|
||||||
Request<byte[]> request = new DefaultRequest(payload, null, null)
|
Request<byte[]> request = new DefaultRequest(payload, null, null)
|
||||||
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
||||||
def encoded = Encoders.BASE64URL.encode(x5t)
|
def encoded = Encoders.BASE64URL.encode(x5t)
|
||||||
|
@ -75,7 +76,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testX509CertificateSha1ThumbprintEnabled() {
|
void testX509CertificateSha1ThumbprintEnabled() {
|
||||||
def payload = new ByteArrayInputStream(TestKeys.RS256.cert.getEncoded())
|
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
|
||||||
Request<byte[]> request = new DefaultRequest(payload, null, null)
|
Request<byte[]> request = new DefaultRequest(payload, null, null)
|
||||||
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
||||||
def encoded = Encoders.BASE64URL.encode(x5t)
|
def encoded = Encoders.BASE64URL.encode(x5t)
|
||||||
|
@ -86,7 +87,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testX509CertificateSha256Thumbprint() {
|
void testX509CertificateSha256Thumbprint() {
|
||||||
def payload = new ByteArrayInputStream(TestKeys.RS256.cert.getEncoded())
|
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
|
||||||
Request<byte[]> request = new DefaultRequest(payload, null, null)
|
Request<byte[]> request = new DefaultRequest(payload, null, null)
|
||||||
def x5tS256 = Jwks.HASH.SHA256.digest(request)
|
def x5tS256 = Jwks.HASH.SHA256.digest(request)
|
||||||
def encoded = Encoders.BASE64URL.encode(x5tS256)
|
def encoded = Encoders.BASE64URL.encode(x5tS256)
|
||||||
|
@ -97,7 +98,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testX509CertificateSha256ThumbprintEnabled() {
|
void testX509CertificateSha256ThumbprintEnabled() {
|
||||||
def payload = new ByteArrayInputStream(TestKeys.RS256.cert.getEncoded())
|
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
|
||||||
Request<InputStream> request = new DefaultRequest(payload, null, null)
|
Request<InputStream> request = new DefaultRequest(payload, null, null)
|
||||||
def x5tS256 = Jwks.HASH.SHA256.digest(request)
|
def x5tS256 = Jwks.HASH.SHA256.digest(request)
|
||||||
def encoded = Encoders.BASE64URL.encode(x5tS256)
|
def encoded = Encoders.BASE64URL.encode(x5tS256)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
import io.jsonwebtoken.Jwts
|
import io.jsonwebtoken.Jwts
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.lang.Strings
|
import io.jsonwebtoken.lang.Strings
|
||||||
import io.jsonwebtoken.security.SecureRequest
|
import io.jsonwebtoken.security.SecureRequest
|
||||||
import io.jsonwebtoken.security.SignatureException
|
import io.jsonwebtoken.security.SignatureException
|
||||||
|
@ -37,7 +38,7 @@ class AbstractSecureDigestAlgorithmTest {
|
||||||
Provider provider = Security.getProvider('BC')
|
Provider provider = Security.getProvider('BC')
|
||||||
def pair = Jwts.SIG.RS256.keyPair().build()
|
def pair = Jwts.SIG.RS256.keyPair().build()
|
||||||
byte[] data = Strings.utf8('foo')
|
byte[] data = Strings.utf8('foo')
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
byte[] signature = Jwts.SIG.RS256.digest(new DefaultSecureRequest<>(payload, provider, null, pair.getPrivate()))
|
byte[] signature = Jwts.SIG.RS256.digest(new DefaultSecureRequest<>(payload, provider, null, pair.getPrivate()))
|
||||||
payload.reset()
|
payload.reset()
|
||||||
assertTrue Jwts.SIG.RS256.verify(new DefaultVerifySecureDigestRequest<PublicKey>(payload, provider, null, pair.getPublic(), signature))
|
assertTrue Jwts.SIG.RS256.verify(new DefaultVerifySecureDigestRequest<PublicKey>(payload, provider, null, pair.getPublic(), signature))
|
||||||
|
@ -54,7 +55,7 @@ class AbstractSecureDigestAlgorithmTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
def payload = new ByteArrayInputStream(Strings.utf8('foo'))
|
def payload = Streams.of(Strings.utf8('foo'))
|
||||||
alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
|
alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
assertTrue e.getMessage().startsWith('Unable to compute test signature with JCA algorithm \'test\' using key {')
|
assertTrue e.getMessage().startsWith('Unable to compute test signature with JCA algorithm \'test\' using key {')
|
||||||
|
@ -74,7 +75,7 @@ class AbstractSecureDigestAlgorithmTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def data = Strings.utf8('foo')
|
def data = Strings.utf8('foo')
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
try {
|
try {
|
||||||
byte[] signature = alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
|
byte[] signature = alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
|
||||||
payload.reset()
|
payload.reset()
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.lang.Strings
|
import io.jsonwebtoken.lang.Strings
|
||||||
import io.jsonwebtoken.security.HashAlgorithm
|
import io.jsonwebtoken.security.HashAlgorithm
|
||||||
import io.jsonwebtoken.security.Jwks
|
import io.jsonwebtoken.security.Jwks
|
||||||
|
@ -29,7 +30,7 @@ class DefaultHashAlgorithmTest {
|
||||||
@Test
|
@Test
|
||||||
void testDigestAndVerify() {
|
void testDigestAndVerify() {
|
||||||
byte[] data = Strings.utf8('Hello World')
|
byte[] data = Strings.utf8('Hello World')
|
||||||
InputStream payload = new ByteArrayInputStream(data)
|
InputStream payload = Streams.of(data)
|
||||||
for (HashAlgorithm alg : algs) {
|
for (HashAlgorithm alg : algs) {
|
||||||
byte[] hash = alg.digest(new DefaultRequest<>(payload, null, null))
|
byte[] hash = alg.digest(new DefaultRequest<>(payload, null, null))
|
||||||
payload.reset()
|
payload.reset()
|
||||||
|
|
|
@ -17,6 +17,7 @@ package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
import io.jsonwebtoken.impl.io.CharSequenceReader
|
import io.jsonwebtoken.impl.io.CharSequenceReader
|
||||||
import io.jsonwebtoken.impl.io.ConvertingParser
|
import io.jsonwebtoken.impl.io.ConvertingParser
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.io.AbstractDeserializer
|
import io.jsonwebtoken.io.AbstractDeserializer
|
||||||
import io.jsonwebtoken.io.DeserializationException
|
import io.jsonwebtoken.io.DeserializationException
|
||||||
import io.jsonwebtoken.io.Deserializer
|
import io.jsonwebtoken.io.Deserializer
|
||||||
|
@ -52,7 +53,7 @@ class DefaultJwkParserBuilderTest {
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException)
|
@Test(expected = IllegalArgumentException)
|
||||||
void parseNull() {
|
void parseNull() {
|
||||||
Jwks.parser().build().parse((CharSequence)null)
|
Jwks.parser().build().parse((CharSequence) null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException)
|
@Test(expected = IllegalArgumentException)
|
||||||
|
@ -133,7 +134,7 @@ class DefaultJwkParserBuilderTest {
|
||||||
assertEquals jwk, parsed
|
assertEquals jwk, parsed
|
||||||
|
|
||||||
// InputStream parsing:
|
// InputStream parsing:
|
||||||
parsed = parser.parse(new ByteArrayInputStream(Strings.utf8(json)))
|
parsed = parser.parse(Streams.of(json))
|
||||||
assertEquals jwk, parsed
|
assertEquals jwk, parsed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.io.Encoders
|
import io.jsonwebtoken.io.Encoders
|
||||||
import io.jsonwebtoken.lang.Strings
|
import io.jsonwebtoken.lang.Strings
|
||||||
import io.jsonwebtoken.security.HashAlgorithm
|
import io.jsonwebtoken.security.HashAlgorithm
|
||||||
|
@ -30,7 +31,7 @@ class DefaultJwkThumbprintTest {
|
||||||
|
|
||||||
private static String content = "Hello World"
|
private static String content = "Hello World"
|
||||||
private static HashAlgorithm alg = Jwks.HASH.SHA256
|
private static HashAlgorithm alg = Jwks.HASH.SHA256
|
||||||
private static byte[] digest = alg.digest(new DefaultRequest<InputStream>(new ByteArrayInputStream(Strings.utf8(content)), null, null))
|
private static byte[] digest = alg.digest(new DefaultRequest<InputStream>(Streams.of(content), null, null))
|
||||||
private static String expectedToString = Encoders.BASE64URL.encode(digest)
|
private static String expectedToString = Encoders.BASE64URL.encode(digest)
|
||||||
private static String expectedUriString = DefaultJwkThumbprint.URI_PREFIX + alg.getId() + ":" + expectedToString
|
private static String expectedUriString = DefaultJwkThumbprint.URI_PREFIX + alg.getId() + ":" + expectedToString
|
||||||
private static URI expectedUri = URI.create(expectedUriString)
|
private static URI expectedUri = URI.create(expectedUriString)
|
||||||
|
@ -81,7 +82,7 @@ class DefaultJwkThumbprintTest {
|
||||||
assertFalse thumbprint == new DefaultJwkThumbprint(digest, DefaultHashAlgorithm.SHA1)
|
assertFalse thumbprint == new DefaultJwkThumbprint(digest, DefaultHashAlgorithm.SHA1)
|
||||||
|
|
||||||
// same alg, different digest:
|
// same alg, different digest:
|
||||||
def payload = new ByteArrayInputStream(Strings.utf8('Hello World!'))
|
def payload = Streams.of(Strings.utf8('Hello World!'))
|
||||||
byte[] digest2 = alg.digest(new DefaultRequest<>(payload, null, null))
|
byte[] digest2 = alg.digest(new DefaultRequest<>(payload, null, null))
|
||||||
assertFalse thumbprint == new DefaultJwkThumbprint(digest2, DefaultHashAlgorithm.SHA1)
|
assertFalse thumbprint == new DefaultJwkThumbprint(digest2, DefaultHashAlgorithm.SHA1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@ class EcSignatureAlgorithmTest {
|
||||||
@Test
|
@Test
|
||||||
void testVerifyWithPrivateKey() {
|
void testVerifyWithPrivateKey() {
|
||||||
byte[] data = 'foo'.getBytes(StandardCharsets.UTF_8)
|
byte[] data = 'foo'.getBytes(StandardCharsets.UTF_8)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
algs().each {
|
algs().each {
|
||||||
payload.reset()
|
payload.reset()
|
||||||
def pair = it.keyPair().build()
|
def pair = it.keyPair().build()
|
||||||
|
@ -194,7 +194,7 @@ class EcSignatureAlgorithmTest {
|
||||||
BigInteger order = BigInteger.ONE
|
BigInteger order = BigInteger.ONE
|
||||||
ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new TestECField(), BigInteger.ONE, BigInteger.ONE), new ECPoint(BigInteger.ONE, BigInteger.ONE), order, 1)
|
ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new TestECField(), BigInteger.ONE, BigInteger.ONE), new ECPoint(BigInteger.ONE, BigInteger.ONE), order, 1)
|
||||||
ECPublicKey pub = new TestECPublicKey(algorithm: 'EC', params: spec)
|
ECPublicKey pub = new TestECPublicKey(algorithm: 'EC', params: spec)
|
||||||
def request = new DefaultVerifySecureDigestRequest(new ByteArrayInputStream(new byte[1]), null, null, pub, new byte[1])
|
def request = new DefaultVerifySecureDigestRequest(Streams.of(new byte[1]), null, null, pub, new byte[1])
|
||||||
try {
|
try {
|
||||||
it.verify(request)
|
it.verify(request)
|
||||||
} catch (InvalidKeyException expected) {
|
} catch (InvalidKeyException expected) {
|
||||||
|
@ -310,7 +310,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def signatureStart = token.lastIndexOf('.')
|
def signatureStart = token.lastIndexOf('.')
|
||||||
def withoutSignature = token.substring(0, signatureStart)
|
def withoutSignature = token.substring(0, signatureStart)
|
||||||
def data = Strings.ascii(withoutSignature);
|
def data = Strings.ascii(withoutSignature);
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def signature = Decoders.BASE64URL.decode(token.substring(signatureStart + 1))
|
def signature = Decoders.BASE64URL.decode(token.substring(signatureStart + 1))
|
||||||
assertTrue "Signature do not match that of other implementations", alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, pub, signature))
|
assertTrue "Signature do not match that of other implementations", alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, pub, signature))
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ class EcSignatureAlgorithmTest {
|
||||||
assertTrue keypair.getPublic() instanceof ECPublicKey
|
assertTrue keypair.getPublic() instanceof ECPublicKey
|
||||||
assertTrue keypair.getPrivate() instanceof ECPrivateKey
|
assertTrue keypair.getPrivate() instanceof ECPrivateKey
|
||||||
def data = Strings.ascii(withoutSignature)
|
def data = Strings.ascii(withoutSignature)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def signature = alg.digest(new DefaultSecureRequest<>(payload, null, null, keypair.private))
|
def signature = alg.digest(new DefaultSecureRequest<>(payload, null, null, keypair.private))
|
||||||
payload.reset()
|
payload.reset()
|
||||||
assertTrue alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, signature))
|
assertTrue alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, signature))
|
||||||
|
@ -473,7 +473,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def signature = token.substring(signatureStart + 1)
|
def signature = token.substring(signatureStart + 1)
|
||||||
|
|
||||||
def data = withoutSignature.getBytes(StandardCharsets.US_ASCII)
|
def data = withoutSignature.getBytes(StandardCharsets.US_ASCII)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def sigBytes = Decoders.BASE64URL.decode(signature)
|
def sigBytes = Decoders.BASE64URL.decode(signature)
|
||||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, pub, sigBytes)
|
def request = new DefaultVerifySecureDigestRequest(payload, null, null, pub, sigBytes)
|
||||||
assert alg.verify(request), "Signature do not match that of other implementations"
|
assert alg.verify(request), "Signature do not match that of other implementations"
|
||||||
|
@ -495,7 +495,7 @@ class EcSignatureAlgorithmTest {
|
||||||
signature.initSign(keypair.private)
|
signature.initSign(keypair.private)
|
||||||
signature.update(data)
|
signature.update(data)
|
||||||
def signed = signature.sign()
|
def signed = signature.sign()
|
||||||
def request = new DefaultVerifySecureDigestRequest(new ByteArrayInputStream(data), null, null, keypair.public, signed)
|
def request = new DefaultVerifySecureDigestRequest(Streams.of(data), null, null, keypair.public, signed)
|
||||||
try {
|
try {
|
||||||
alg.verify(request)
|
alg.verify(request)
|
||||||
fail()
|
fail()
|
||||||
|
@ -519,7 +519,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def keypair = alg.keyPair().build()
|
def keypair = alg.keyPair().build()
|
||||||
def signature = Signature.getInstance(alg.jcaName as String)
|
def signature = Signature.getInstance(alg.jcaName as String)
|
||||||
def data = Strings.ascii(withoutSignature)
|
def data = Strings.ascii(withoutSignature)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
signature.initSign(keypair.private)
|
signature.initSign(keypair.private)
|
||||||
signature.update(data)
|
signature.update(data)
|
||||||
def signed = signature.sign()
|
def signed = signature.sign()
|
||||||
|
@ -538,7 +538,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def alg = Jwts.SIG.ES256
|
def alg = Jwts.SIG.ES256
|
||||||
def keypair = alg.keyPair().build()
|
def keypair = alg.keyPair().build()
|
||||||
def data = Strings.ascii(withoutSignature)
|
def data = Strings.ascii(withoutSignature)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, forgedSig)
|
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, forgedSig)
|
||||||
assertFalse alg.verify(request)
|
assertFalse alg.verify(request)
|
||||||
}
|
}
|
||||||
|
@ -556,7 +556,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def alg = Jwts.SIG.ES256
|
def alg = Jwts.SIG.ES256
|
||||||
def keypair = alg.keyPair().build()
|
def keypair = alg.keyPair().build()
|
||||||
def data = Strings.ascii(withoutSignature)
|
def data = Strings.ascii(withoutSignature)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, sig)
|
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, sig)
|
||||||
assertFalse alg.verify(request)
|
assertFalse alg.verify(request)
|
||||||
}
|
}
|
||||||
|
@ -574,7 +574,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def alg = Jwts.SIG.ES256
|
def alg = Jwts.SIG.ES256
|
||||||
def keypair = alg.keyPair().build()
|
def keypair = alg.keyPair().build()
|
||||||
def data = Strings.ascii(withoutSignature)
|
def data = Strings.ascii(withoutSignature)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, sig)
|
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, sig)
|
||||||
assertFalse alg.verify(request)
|
assertFalse alg.verify(request)
|
||||||
}
|
}
|
||||||
|
@ -587,7 +587,7 @@ class EcSignatureAlgorithmTest {
|
||||||
def alg = Jwts.SIG.ES256
|
def alg = Jwts.SIG.ES256
|
||||||
def keypair = alg.keyPair().build()
|
def keypair = alg.keyPair().build()
|
||||||
def data = Strings.ascii(withoutSignature)
|
def data = Strings.ascii(withoutSignature)
|
||||||
def payload = new ByteArrayInputStream(data)
|
def payload = Streams.of(data)
|
||||||
def invalidSignature = Decoders.BASE64URL.decode(invalidEncodedSignature)
|
def invalidSignature = Decoders.BASE64URL.decode(invalidEncodedSignature)
|
||||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, invalidSignature)
|
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, invalidSignature)
|
||||||
assertFalse("Forged signature must not be considered valid.", alg.verify(request))
|
assertFalse("Forged signature must not be considered valid.", alg.verify(request))
|
||||||
|
|
|
@ -60,7 +60,7 @@ class GcmAesAeadAlgorithmTest {
|
||||||
|
|
||||||
def alg = Jwts.ENC.A256GCM
|
def alg = Jwts.ENC.A256GCM
|
||||||
|
|
||||||
def ins = new ByteArrayInputStream(P)
|
def ins = Streams.of(P)
|
||||||
def aad = Streams.of(AAD)
|
def aad = Streams.of(AAD)
|
||||||
def out = new ByteArrayOutputStream(8192)
|
def out = new ByteArrayOutputStream(8192)
|
||||||
def res = new DefaultAeadResult(out)
|
def res = new DefaultAeadResult(out)
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.lang.Registry
|
import io.jsonwebtoken.lang.Registry
|
||||||
import io.jsonwebtoken.security.HashAlgorithm
|
import io.jsonwebtoken.security.HashAlgorithm
|
||||||
import io.jsonwebtoken.security.Jwks
|
import io.jsonwebtoken.security.Jwks
|
||||||
|
@ -80,7 +81,7 @@ class HashAlgorithmsTest {
|
||||||
|
|
||||||
static DefaultRequest<InputStream> request(String msg) {
|
static DefaultRequest<InputStream> request(String msg) {
|
||||||
byte[] data = msg.getBytes(StandardCharsets.UTF_8)
|
byte[] data = msg.getBytes(StandardCharsets.UTF_8)
|
||||||
InputStream payload = new ByteArrayInputStream(data)
|
InputStream payload = Streams.of(data)
|
||||||
return new DefaultRequest<InputStream>(payload, null, null)
|
return new DefaultRequest<InputStream>(payload, null, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.impl.lang.Bytes
|
import io.jsonwebtoken.impl.lang.Bytes
|
||||||
import io.jsonwebtoken.impl.lang.CheckedFunction
|
import io.jsonwebtoken.impl.lang.CheckedFunction
|
||||||
import io.jsonwebtoken.lang.Classes
|
import io.jsonwebtoken.lang.Classes
|
||||||
|
@ -347,7 +348,7 @@ class JcaTemplateTest {
|
||||||
X509Certificate cert = template.withCertificateFactory(new CheckedFunction<CertificateFactory, X509Certificate>() {
|
X509Certificate cert = template.withCertificateFactory(new CheckedFunction<CertificateFactory, X509Certificate>() {
|
||||||
@Override
|
@Override
|
||||||
X509Certificate apply(CertificateFactory certificateFactory) throws Exception {
|
X509Certificate apply(CertificateFactory certificateFactory) throws Exception {
|
||||||
(X509Certificate)certificateFactory.generateCertificate(new ByteArrayInputStream(expected.getEncoded()))
|
(X509Certificate) certificateFactory.generateCertificate(Streams.of(expected.getEncoded()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
assertEquals expected, cert
|
assertEquals expected, cert
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
package io.jsonwebtoken.impl.security
|
package io.jsonwebtoken.impl.security
|
||||||
|
|
||||||
import io.jsonwebtoken.impl.RfcTests
|
import io.jsonwebtoken.impl.RfcTests
|
||||||
|
import io.jsonwebtoken.impl.io.Streams
|
||||||
import io.jsonwebtoken.security.HashAlgorithm
|
import io.jsonwebtoken.security.HashAlgorithm
|
||||||
import io.jsonwebtoken.security.JwkThumbprint
|
import io.jsonwebtoken.security.JwkThumbprint
|
||||||
import io.jsonwebtoken.security.Jwks
|
import io.jsonwebtoken.security.Jwks
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
import javax.crypto.SecretKey
|
import javax.crypto.SecretKey
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
import static io.jsonwebtoken.impl.security.DefaultHashAlgorithm.SHA1
|
import static io.jsonwebtoken.impl.security.DefaultHashAlgorithm.SHA1
|
||||||
import static org.junit.Assert.assertEquals
|
import static org.junit.Assert.assertEquals
|
||||||
|
@ -32,8 +32,7 @@ class JwkThumbprintsTest {
|
||||||
static final HashAlgorithm SHA256 = Jwks.HASH.@SHA256
|
static final HashAlgorithm SHA256 = Jwks.HASH.@SHA256
|
||||||
|
|
||||||
static byte[] digest(String json, HashAlgorithm alg) {
|
static byte[] digest(String json, HashAlgorithm alg) {
|
||||||
def utf8Bytes = json.getBytes(StandardCharsets.UTF_8)
|
def payload = Streams.of(json)
|
||||||
def payload = new ByteArrayInputStream(utf8Bytes);
|
|
||||||
def req = new DefaultRequest(payload, null, null)
|
def req = new DefaultRequest(payload, null, null)
|
||||||
return alg.digest(req)
|
return alg.digest(req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ class EncryptionAlgorithmsTest {
|
||||||
assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)
|
assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
def ciphertext = new ByteArrayInputStream(ciphertextBytes)
|
def ciphertext = Streams.of(ciphertextBytes)
|
||||||
out = new ByteArrayOutputStream(8192)
|
out = new ByteArrayOutputStream(8192)
|
||||||
def dreq = new DefaultDecryptAeadRequest(ciphertext, key, null, iv, tag)
|
def dreq = new DefaultDecryptAeadRequest(ciphertext, key, null, iv, tag)
|
||||||
alg.decrypt(dreq, out)
|
alg.decrypt(dreq, out)
|
||||||
|
@ -155,7 +155,7 @@ class EncryptionAlgorithmsTest {
|
||||||
assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)
|
assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
def ciphertext = new ByteArrayInputStream(ciphertextBytes)
|
def ciphertext = Streams.of(ciphertextBytes)
|
||||||
out = new ByteArrayOutputStream(8192)
|
out = new ByteArrayOutputStream(8192)
|
||||||
def dreq = new DefaultDecryptAeadRequest(ciphertext, key, aad, iv, tag)
|
def dreq = new DefaultDecryptAeadRequest(ciphertext, key, aad, iv, tag)
|
||||||
alg.decrypt(dreq, out)
|
alg.decrypt(dreq, out)
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -133,7 +133,10 @@
|
||||||
<test.addOpens>
|
<test.addOpens>
|
||||||
--add-opens java.base/java.lang=ALL-UNNAMED, <!-- Needed by EasyMock/cglib -->
|
--add-opens java.base/java.lang=ALL-UNNAMED, <!-- Needed by EasyMock/cglib -->
|
||||||
--add-opens java.desktop/java.beans=ALL-UNNAMED, <!-- Needed by EasyMock/cglib -->
|
--add-opens java.desktop/java.beans=ALL-UNNAMED, <!-- Needed by EasyMock/cglib -->
|
||||||
--add-opens java.base/java.lang.ref=ALL-UNNAMED <!-- Needed by PowerMock -->
|
--add-opens java.base/java.lang.ref=ALL-UNNAMED, <!-- Needed by PowerMock -->
|
||||||
|
<!-- needed by KeysImplTest.testKeyPairFor, KeysTest.testDeprecatedKeyPairFor, and
|
||||||
|
KeysTest.testKeyPairBuilder: -->
|
||||||
|
--add-opens java.base/sun.security.util=ALL-UNNAMED
|
||||||
</test.addOpens>
|
</test.addOpens>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
Loading…
Reference in New Issue