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.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -588,18 +587,18 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
InputStream payloadStream = null; // not needed unless b64 is enabled
|
||||
if (this.encodePayload) {
|
||||
encodeAndWrite("JWS Payload", payload, jws);
|
||||
signingInput = new ByteArrayInputStream(jws.toByteArray());
|
||||
signingInput = Streams.of(jws.toByteArray());
|
||||
} else { // b64
|
||||
|
||||
// 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,
|
||||
// so we ensure we have an input stream for that:
|
||||
if (payload.isClaims() || payload.isCompressed()) {
|
||||
ByteArrayOutputStream claimsOut = new ByteArrayOutputStream(8192);
|
||||
writeAndClose("JWS Unencoded Payload", payload, claimsOut);
|
||||
payloadStream = new ByteArrayInputStream(claimsOut.toByteArray());
|
||||
payloadStream = Streams.of(claimsOut.toByteArray());
|
||||
} else {
|
||||
// No claims and not compressed, so just get the direct InputStream:
|
||||
payloadStream = Assert.stateNotNull(payload.toInputStream(), "Payload InputStream cannot be null.");
|
||||
|
@ -698,7 +697,7 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
if (content.isClaims()) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
|
||||
writeAndClose("JWE Claims", content, out);
|
||||
plaintext = new ByteArrayInputStream(out.toByteArray());
|
||||
plaintext = Streams.of(out.toByteArray());
|
||||
} else {
|
||||
plaintext = content.toInputStream();
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import io.jsonwebtoken.ProtectedHeader;
|
|||
import io.jsonwebtoken.SigningKeyResolver;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import io.jsonwebtoken.impl.io.AbstractParser;
|
||||
import io.jsonwebtoken.impl.io.BytesInputStream;
|
||||
import io.jsonwebtoken.impl.io.CharSequenceReader;
|
||||
import io.jsonwebtoken.impl.io.JsonObjectDeserializer;
|
||||
import io.jsonwebtoken.impl.io.Streams;
|
||||
|
@ -56,7 +57,6 @@ import io.jsonwebtoken.io.Decoder;
|
|||
import io.jsonwebtoken.io.DeserializationException;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.DateFormats;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
|
@ -74,7 +74,6 @@ import io.jsonwebtoken.security.WeakKeyException;
|
|||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
@ -315,7 +314,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
|||
bb.rewind();
|
||||
byte[] data = new byte[bb.remaining()];
|
||||
bb.get(data);
|
||||
verificationInput = new ByteArrayInputStream(data);
|
||||
verificationInput = Streams.of(data);
|
||||
} else { // b64 extension
|
||||
ByteBuffer headerBuf = StandardCharsets.US_ASCII.encode(Strings.wrap(tokenized.getProtected()));
|
||||
headerBuf.rewind();
|
||||
|
@ -325,7 +324,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
|||
buf.rewind();
|
||||
byte[] data = new byte[buf.remaining()];
|
||||
buf.get(data);
|
||||
InputStream prefixStream = new ByteArrayInputStream(data);
|
||||
InputStream prefixStream = Streams.of(data);
|
||||
payloadStream = payload.toInputStream();
|
||||
// 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
|
||||
|
@ -378,7 +377,7 @@ public class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtPa
|
|||
|
||||
// =============== 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;
|
||||
try {
|
||||
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));
|
||||
final byte[] aadBytes = new byte[buf.remaining()];
|
||||
buf.get(aadBytes);
|
||||
InputStream aad = new ByteArrayInputStream(aadBytes);
|
||||
InputStream aad = Streams.of(aadBytes);
|
||||
|
||||
base64Url = base64UrlDigest;
|
||||
//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) {
|
||||
if (in instanceof ByteArrayInputStream) {
|
||||
byte[] data = Classes.getFieldValue(in, "buf", byte[].class);
|
||||
if (in instanceof BytesInputStream) {
|
||||
byte[] data = Streams.bytes(in, "Unable to obtain payload InputStream bytes.");
|
||||
return new Payload(data, null);
|
||||
}
|
||||
//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) {
|
||||
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.");
|
||||
} catch (Throwable t) {
|
||||
// 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.CompressionCodec;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.impl.io.Streams;
|
||||
import io.jsonwebtoken.impl.lang.Bytes;
|
||||
import io.jsonwebtoken.io.CompressionAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
@ -68,10 +68,10 @@ class Payload {
|
|||
}
|
||||
this.bytes = data;
|
||||
if (in == null && !Bytes.isEmpty(this.bytes)) {
|
||||
in = new ByteArrayInputStream(data);
|
||||
in = Streams.of(data);
|
||||
}
|
||||
this.inputStreamEmpty = in == null;
|
||||
this.inputStream = this.inputStreamEmpty ? new ByteArrayInputStream(Bytes.EMPTY) : in;
|
||||
this.inputStream = this.inputStreamEmpty ? Streams.of(Bytes.EMPTY) : in;
|
||||
}
|
||||
|
||||
boolean isClaims() {
|
||||
|
|
|
@ -27,7 +27,6 @@ import io.jsonwebtoken.lang.Assert;
|
|||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -152,7 +151,7 @@ public abstract class AbstractCompressionAlgorithm implements CompressionAlgorit
|
|||
* @throws IOException if the decompression runs into an IO problem
|
||||
*/
|
||||
protected byte[] doDecompress(byte[] compressed) throws IOException {
|
||||
InputStream is = new ByteArrayInputStream(compressed);
|
||||
InputStream is = Streams.of(compressed);
|
||||
InputStream decompress = decompress(is);
|
||||
byte[] buffer = new byte[512];
|
||||
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.Strings;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
|
@ -38,7 +37,7 @@ public class DelegateStringDecoder implements Decoder<InputStream, InputStream>
|
|||
try {
|
||||
byte[] data = Streams.bytes(in, "Unable to Base64URL-decode input.");
|
||||
data = delegate.decode(Strings.utf8(data));
|
||||
return new ByteArrayInputStream(data);
|
||||
return Streams.of(data);
|
||||
} catch (Throwable t) {
|
||||
String msg = "Unable to Base64Url-decode InputStream: " + t.getMessage();
|
||||
throw new DecodingException(msg, t);
|
||||
|
|
|
@ -15,15 +15,12 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.io;
|
||||
|
||||
import io.jsonwebtoken.impl.lang.AddOpens;
|
||||
import io.jsonwebtoken.impl.lang.Bytes;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Flushable;
|
||||
import java.io.IOException;
|
||||
|
@ -43,30 +40,26 @@ public class Streams {
|
|||
*/
|
||||
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) {
|
||||
if (in instanceof ByteArrayInputStream) {
|
||||
return Classes.getFieldValue(in, "buf", byte[].class);
|
||||
if (in instanceof BytesInputStream) {
|
||||
return ((BytesInputStream) in).getBytes();
|
||||
}
|
||||
// otherwise we have to copy over:
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
|
||||
copy(in, out, new byte[8192], exmsg);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static ByteArrayInputStream of(byte[] bytes) {
|
||||
return Bytes.isEmpty(bytes) ? new ByteArrayInputStream(Bytes.EMPTY) : new ByteArrayInputStream(bytes);
|
||||
public static InputStream of(byte[] bytes) {
|
||||
return new BytesInputStream(bytes);
|
||||
}
|
||||
|
||||
public static ByteArrayInputStream of(CharSequence seq) {
|
||||
public static InputStream of(CharSequence seq) {
|
||||
return of(Strings.utf8(seq));
|
||||
}
|
||||
|
||||
public static Reader reader(byte[] bytes) {
|
||||
return reader(new ByteArrayInputStream(bytes));
|
||||
return reader(Streams.of(bytes));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import io.jsonwebtoken.impl.io.Streams;
|
||||
import io.jsonwebtoken.impl.lang.Nameable;
|
||||
import io.jsonwebtoken.impl.lang.Parameter;
|
||||
import io.jsonwebtoken.impl.lang.ParameterReadable;
|
||||
|
@ -31,7 +32,6 @@ import io.jsonwebtoken.security.JwkThumbprint;
|
|||
import io.jsonwebtoken.security.Jwks;
|
||||
import io.jsonwebtoken.security.KeyOperation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
|
@ -152,7 +152,7 @@ public abstract class AbstractJwk<K extends Key> implements Jwk<K>, ParameterRea
|
|||
String json = toThumbprintJson();
|
||||
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
|
||||
InputStream in = new ByteArrayInputStream(bytes);
|
||||
InputStream in = Streams.of(bytes);
|
||||
byte[] digest = alg.digest(new DefaultRequest<>(in, this.context.getProvider(), this.context.getRandom()));
|
||||
return new DefaultJwkThumbprint(digest, alg);
|
||||
}
|
||||
|
|
|
@ -135,9 +135,8 @@ final class DefaultMacAlgorithm extends AbstractSecureDigestAlgorithm<SecretKey,
|
|||
throw new InvalidKeyException(msg);
|
||||
}
|
||||
|
||||
// We can ignore PKCS11 key name assertions for two reasons:
|
||||
// 1. HSM module key algorithm names don't always align with JCA standard algorithm names, and
|
||||
// 2. Our KeysBridge.findBitLength implementation can extract the key length so we can still validate with that
|
||||
// We can ignore PKCS11 key name assertions because HSM module key algorithm names don't always align with
|
||||
// JCA standard algorithm names:
|
||||
boolean pkcs11Key = KeysBridge.isSunPkcs11GenericSecret(key);
|
||||
|
||||
//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.SecretKey;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
|
@ -93,7 +92,7 @@ public class GcmAesAeadAlgorithm extends AesAlgorithm implements AeadAlgorithm {
|
|||
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:
|
||||
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[]>() {
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,6 @@ import io.jsonwebtoken.security.SignatureException;
|
|||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -145,11 +144,11 @@ public class HmacAesAeadAlgorithm extends AesAlgorithm implements AeadAlgorithm
|
|||
|
||||
Collection<InputStream> streams = new ArrayList<>(4);
|
||||
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(new ByteArrayInputStream(AL));
|
||||
streams.add(Streams.of(AL));
|
||||
InputStream in = new SequenceInputStream(Collections.enumeration(streams));
|
||||
|
||||
SecretKey key = new SecretKeySpec(macKeyBytes, SIGALG.getJcaName());
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.Identifiable;
|
||||
import io.jsonwebtoken.impl.io.Streams;
|
||||
import io.jsonwebtoken.impl.lang.Bytes;
|
||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||
import io.jsonwebtoken.impl.lang.CheckedSupplier;
|
||||
|
@ -36,7 +37,6 @@ import javax.crypto.Mac;
|
|||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
|
@ -332,7 +332,7 @@ public class JcaTemplate {
|
|||
return fallback(CertificateFactory.class, new CheckedFunction<CertificateFactory, X509Certificate>() {
|
||||
@Override
|
||||
public X509Certificate apply(CertificateFactory cf) throws CertificateException {
|
||||
InputStream is = new ByteArrayInputStream(x509DerBytes);
|
||||
InputStream is = Streams.of(x509DerBytes);
|
||||
return (X509Certificate) cf.generateCertificate(is);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.impl.lang.AddOpens;
|
||||
import io.jsonwebtoken.impl.lang.Bytes;
|
||||
import io.jsonwebtoken.impl.lang.OptionalMethodInvoker;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
|
@ -30,6 +28,8 @@ import javax.crypto.SecretKey;
|
|||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
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
|
||||
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_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
|
||||
private KeysBridge() {
|
||||
}
|
||||
|
@ -119,24 +109,30 @@ public final class KeysBridge {
|
|||
*/
|
||||
public static int findBitLength(Key key) {
|
||||
|
||||
Integer retval = SUN_KEYSIZE.apply(key);
|
||||
int bitlen = Assert.stateNotNull(retval, SUN_KEYUTIL_ERR);
|
||||
int bitlen = -1;
|
||||
|
||||
// SunPKCS11 SecretKey lengths are unfortunately reported in bytes, not bits
|
||||
// per https://bugs.openjdk.org/browse/JDK-8163173
|
||||
// (they should be multiplying the PKCS11 CKA_VALUE_LEN value by 8 since their own
|
||||
// sun.security.util.Length#getLength() JavaDoc states that values are intended to be in bits, not bytes)
|
||||
// So we account for that here:
|
||||
if (bitlen > 0 && isSunPkcs11GenericSecret(key)) {
|
||||
bitlen *= Byte.SIZE;
|
||||
// try to parse the length from key specification
|
||||
if (key instanceof SecretKey) {
|
||||
SecretKey secretKey = (SecretKey) key;
|
||||
if ("RAW".equals(secretKey.getFormat())) {
|
||||
byte[] encoded = findEncoded(secretKey);
|
||||
if (!Bytes.isEmpty(encoded)) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package io.jsonwebtoken.impl.security;
|
||||
|
||||
import io.jsonwebtoken.impl.ParameterMap;
|
||||
import io.jsonwebtoken.impl.io.Streams;
|
||||
import io.jsonwebtoken.impl.lang.CheckedFunction;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.impl.lang.Functions;
|
||||
|
@ -27,7 +28,6 @@ import io.jsonwebtoken.security.Jwks;
|
|||
import io.jsonwebtoken.security.Request;
|
||||
import io.jsonwebtoken.security.X509Builder;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
@ -99,7 +99,7 @@ public class X509BuilderSupport implements X509Builder<X509BuilderSupport> {
|
|||
|
||||
private byte[] computeThumbprint(final X509Certificate cert, HashAlgorithm alg) {
|
||||
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);
|
||||
return alg.digest(request);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package io.jsonwebtoken
|
|||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.impl.*
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.impl.lang.Bytes
|
||||
import io.jsonwebtoken.impl.lang.Services
|
||||
import io.jsonwebtoken.impl.security.*
|
||||
|
@ -141,7 +142,7 @@ class JwtsTest {
|
|||
def h = base64Url('{"alg":"HS256"}')
|
||||
def c = base64Url('{"sub":"joe","exp":"-42-"}')
|
||||
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 result = Jwts.SIG.HS256.digest(request)
|
||||
def sig = Encoders.BASE64URL.encode(result)
|
||||
|
@ -191,7 +192,7 @@ class JwtsTest {
|
|||
@Test
|
||||
void testContentStreamWithContentType() {
|
||||
String s = 'Hello JJWT'
|
||||
InputStream content = new ByteArrayInputStream(Strings.utf8(s))
|
||||
InputStream content = Streams.of(Strings.utf8(s))
|
||||
String cty = 'text/plain'
|
||||
String compact = Jwts.builder().content(content, cty).compact()
|
||||
def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)
|
||||
|
@ -202,7 +203,7 @@ class JwtsTest {
|
|||
@Test
|
||||
void testContentStreamWithoutContentType() {
|
||||
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()
|
||||
def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)
|
||||
assertNull jwt.header.getContentType()
|
||||
|
|
|
@ -17,6 +17,7 @@ package io.jsonwebtoken
|
|||
|
||||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.DefaultJwtParser
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.impl.lang.Bytes
|
||||
import io.jsonwebtoken.impl.lang.Services
|
||||
import io.jsonwebtoken.impl.security.TestKeys
|
||||
|
@ -69,8 +70,8 @@ class RFC7797Test {
|
|||
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:
|
||||
InputStream asByteInputStream = new ByteArrayInputStream(content)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(content))
|
||||
InputStream asByteInputStream = Streams.of(content)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))
|
||||
|
||||
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
||||
def parser = Jwts.parser().verifyWith(key).build()
|
||||
|
@ -107,8 +108,8 @@ class RFC7797Test {
|
|||
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:
|
||||
InputStream asByteInputStream = new ByteArrayInputStream(content)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(content))
|
||||
InputStream asByteInputStream = Streams.of(content)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))
|
||||
|
||||
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
||||
def parser = Jwts.parser().verifyWith(key).build()
|
||||
|
@ -128,13 +129,13 @@ class RFC7797Test {
|
|||
def key = TestKeys.HS256
|
||||
|
||||
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()
|
||||
|
||||
// But verify with 3 types of sources: byte array, and two different kinds of InputStreams:
|
||||
InputStream asByteInputStream = new ByteArrayInputStream(content)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(content))
|
||||
InputStream asByteInputStream =Streams.of(content)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))
|
||||
|
||||
for (def payload : [content, asByteInputStream, asBufferedInputStream]) {
|
||||
def parser = Jwts.parser().verifyWith(key).build()
|
||||
|
@ -348,8 +349,8 @@ class RFC7797Test {
|
|||
.compact()
|
||||
|
||||
// But verify with 3 types of sources: byte array, and two different kinds of InputStreams:
|
||||
InputStream asByteInputStream = new ByteArrayInputStream(compressed)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(compressed))
|
||||
InputStream asByteInputStream = Streams.of(compressed)
|
||||
InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(compressed))
|
||||
|
||||
for (def payload : [compressed, asByteInputStream, asBufferedInputStream]) {
|
||||
def parser = Jwts.parser().verifyWith(key).build()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package io.jsonwebtoken.impl
|
||||
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.impl.lang.Bytes
|
||||
import io.jsonwebtoken.impl.security.DefaultHashAlgorithm
|
||||
import io.jsonwebtoken.impl.security.DefaultRequest
|
||||
|
@ -269,7 +270,7 @@ class DefaultMutableJweHeaderTest {
|
|||
*/
|
||||
@Test
|
||||
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 x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
||||
String encoded = Encoders.BASE64URL.encode(x5t)
|
||||
|
@ -285,7 +286,7 @@ class DefaultMutableJweHeaderTest {
|
|||
*/
|
||||
@Test
|
||||
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 x5tS256 = Jwks.HASH.@SHA256.digest(request)
|
||||
String encoded = Encoders.BASE64URL.encode(x5tS256)
|
||||
|
|
|
@ -25,7 +25,7 @@ class CountingInputStreamTest {
|
|||
|
||||
@Test
|
||||
void readEmpty() {
|
||||
def stream = new CountingInputStream(new ByteArrayInputStream(Bytes.EMPTY))
|
||||
def stream = new CountingInputStream(Streams.of(Bytes.EMPTY))
|
||||
stream.read()
|
||||
assertEquals 0, stream.getCount()
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class CountingInputStreamTest {
|
|||
void readSingle() {
|
||||
def single = (byte) 0x18 // any random byte is fine
|
||||
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 1, stream.getCount()
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class CountingInputStreamTest {
|
|||
@Test
|
||||
void testSkip() {
|
||||
def data = Strings.utf8('hello world')
|
||||
def stream = new CountingInputStream(new ByteArrayInputStream(data))
|
||||
def stream = new CountingInputStream(Streams.of(data))
|
||||
stream.skip(6)
|
||||
assertEquals 6, stream.getCount()
|
||||
int w = ('w' as char)
|
||||
|
|
|
@ -29,7 +29,7 @@ class DelegateStringDecoderTest {
|
|||
void decode() {
|
||||
def value = 'test'
|
||||
def bytes = Strings.utf8(value)
|
||||
def ins = new ByteArrayInputStream(bytes)
|
||||
def ins = Streams.of(bytes)
|
||||
def test = new Decoder<CharSequence, byte[]>() {
|
||||
@Override
|
||||
byte[] decode(CharSequence s) throws DecodingException {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.security.*
|
||||
import org.junit.Test
|
||||
|
@ -64,7 +65,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
|||
|
||||
@Test
|
||||
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)
|
||||
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
||||
def encoded = Encoders.BASE64URL.encode(x5t)
|
||||
|
@ -75,7 +76,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
|||
|
||||
@Test
|
||||
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)
|
||||
def x5t = DefaultHashAlgorithm.SHA1.digest(request)
|
||||
def encoded = Encoders.BASE64URL.encode(x5t)
|
||||
|
@ -86,7 +87,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
|||
|
||||
@Test
|
||||
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)
|
||||
def x5tS256 = Jwks.HASH.SHA256.digest(request)
|
||||
def encoded = Encoders.BASE64URL.encode(x5tS256)
|
||||
|
@ -97,7 +98,7 @@ class AbstractAsymmetricJwkBuilderTest {
|
|||
|
||||
@Test
|
||||
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)
|
||||
def x5tS256 = Jwks.HASH.SHA256.digest(request)
|
||||
def encoded = Encoders.BASE64URL.encode(x5tS256)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.SecureRequest
|
||||
import io.jsonwebtoken.security.SignatureException
|
||||
|
@ -37,7 +38,7 @@ class AbstractSecureDigestAlgorithmTest {
|
|||
Provider provider = Security.getProvider('BC')
|
||||
def pair = Jwts.SIG.RS256.keyPair().build()
|
||||
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()))
|
||||
payload.reset()
|
||||
assertTrue Jwts.SIG.RS256.verify(new DefaultVerifySecureDigestRequest<PublicKey>(payload, provider, null, pair.getPublic(), signature))
|
||||
|
@ -54,7 +55,7 @@ class AbstractSecureDigestAlgorithmTest {
|
|||
}
|
||||
}
|
||||
try {
|
||||
def payload = new ByteArrayInputStream(Strings.utf8('foo'))
|
||||
def payload = Streams.of(Strings.utf8('foo'))
|
||||
alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
|
||||
} catch (SignatureException e) {
|
||||
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 payload = new ByteArrayInputStream(data)
|
||||
def payload = Streams.of(data)
|
||||
try {
|
||||
byte[] signature = alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
|
||||
payload.reset()
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.HashAlgorithm
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
|
@ -29,7 +30,7 @@ class DefaultHashAlgorithmTest {
|
|||
@Test
|
||||
void testDigestAndVerify() {
|
||||
byte[] data = Strings.utf8('Hello World')
|
||||
InputStream payload = new ByteArrayInputStream(data)
|
||||
InputStream payload = Streams.of(data)
|
||||
for (HashAlgorithm alg : algs) {
|
||||
byte[] hash = alg.digest(new DefaultRequest<>(payload, null, null))
|
||||
payload.reset()
|
||||
|
|
|
@ -17,6 +17,7 @@ package io.jsonwebtoken.impl.security
|
|||
|
||||
import io.jsonwebtoken.impl.io.CharSequenceReader
|
||||
import io.jsonwebtoken.impl.io.ConvertingParser
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.io.AbstractDeserializer
|
||||
import io.jsonwebtoken.io.DeserializationException
|
||||
import io.jsonwebtoken.io.Deserializer
|
||||
|
@ -52,7 +53,7 @@ class DefaultJwkParserBuilderTest {
|
|||
|
||||
@Test(expected = IllegalArgumentException)
|
||||
void parseNull() {
|
||||
Jwks.parser().build().parse((CharSequence)null)
|
||||
Jwks.parser().build().parse((CharSequence) null)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException)
|
||||
|
@ -133,7 +134,7 @@ class DefaultJwkParserBuilderTest {
|
|||
assertEquals jwk, parsed
|
||||
|
||||
// InputStream parsing:
|
||||
parsed = parser.parse(new ByteArrayInputStream(Strings.utf8(json)))
|
||||
parsed = parser.parse(Streams.of(json))
|
||||
assertEquals jwk, parsed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.HashAlgorithm
|
||||
|
@ -30,7 +31,7 @@ class DefaultJwkThumbprintTest {
|
|||
|
||||
private static String content = "Hello World"
|
||||
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 expectedUriString = DefaultJwkThumbprint.URI_PREFIX + alg.getId() + ":" + expectedToString
|
||||
private static URI expectedUri = URI.create(expectedUriString)
|
||||
|
@ -81,7 +82,7 @@ class DefaultJwkThumbprintTest {
|
|||
assertFalse thumbprint == new DefaultJwkThumbprint(digest, DefaultHashAlgorithm.SHA1)
|
||||
|
||||
// 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))
|
||||
assertFalse thumbprint == new DefaultJwkThumbprint(digest2, DefaultHashAlgorithm.SHA1)
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ class EcSignatureAlgorithmTest {
|
|||
@Test
|
||||
void testVerifyWithPrivateKey() {
|
||||
byte[] data = 'foo'.getBytes(StandardCharsets.UTF_8)
|
||||
def payload = new ByteArrayInputStream(data)
|
||||
def payload = Streams.of(data)
|
||||
algs().each {
|
||||
payload.reset()
|
||||
def pair = it.keyPair().build()
|
||||
|
@ -194,7 +194,7 @@ class EcSignatureAlgorithmTest {
|
|||
BigInteger order = BigInteger.ONE
|
||||
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)
|
||||
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 {
|
||||
it.verify(request)
|
||||
} catch (InvalidKeyException expected) {
|
||||
|
@ -310,7 +310,7 @@ class EcSignatureAlgorithmTest {
|
|||
def signatureStart = token.lastIndexOf('.')
|
||||
def withoutSignature = token.substring(0, signatureStart)
|
||||
def data = Strings.ascii(withoutSignature);
|
||||
def payload = new ByteArrayInputStream(data)
|
||||
def payload = Streams.of(data)
|
||||
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))
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ class EcSignatureAlgorithmTest {
|
|||
assertTrue keypair.getPublic() instanceof ECPublicKey
|
||||
assertTrue keypair.getPrivate() instanceof ECPrivateKey
|
||||
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))
|
||||
payload.reset()
|
||||
assertTrue alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, signature))
|
||||
|
@ -473,7 +473,7 @@ class EcSignatureAlgorithmTest {
|
|||
def signature = token.substring(signatureStart + 1)
|
||||
|
||||
def data = withoutSignature.getBytes(StandardCharsets.US_ASCII)
|
||||
def payload = new ByteArrayInputStream(data)
|
||||
def payload = Streams.of(data)
|
||||
def sigBytes = Decoders.BASE64URL.decode(signature)
|
||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, pub, sigBytes)
|
||||
assert alg.verify(request), "Signature do not match that of other implementations"
|
||||
|
@ -495,7 +495,7 @@ class EcSignatureAlgorithmTest {
|
|||
signature.initSign(keypair.private)
|
||||
signature.update(data)
|
||||
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 {
|
||||
alg.verify(request)
|
||||
fail()
|
||||
|
@ -519,7 +519,7 @@ class EcSignatureAlgorithmTest {
|
|||
def keypair = alg.keyPair().build()
|
||||
def signature = Signature.getInstance(alg.jcaName as String)
|
||||
def data = Strings.ascii(withoutSignature)
|
||||
def payload = new ByteArrayInputStream(data)
|
||||
def payload = Streams.of(data)
|
||||
signature.initSign(keypair.private)
|
||||
signature.update(data)
|
||||
def signed = signature.sign()
|
||||
|
@ -538,7 +538,7 @@ class EcSignatureAlgorithmTest {
|
|||
def alg = Jwts.SIG.ES256
|
||||
def keypair = alg.keyPair().build()
|
||||
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)
|
||||
assertFalse alg.verify(request)
|
||||
}
|
||||
|
@ -556,7 +556,7 @@ class EcSignatureAlgorithmTest {
|
|||
def alg = Jwts.SIG.ES256
|
||||
def keypair = alg.keyPair().build()
|
||||
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)
|
||||
assertFalse alg.verify(request)
|
||||
}
|
||||
|
@ -574,7 +574,7 @@ class EcSignatureAlgorithmTest {
|
|||
def alg = Jwts.SIG.ES256
|
||||
def keypair = alg.keyPair().build()
|
||||
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)
|
||||
assertFalse alg.verify(request)
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ class EcSignatureAlgorithmTest {
|
|||
def alg = Jwts.SIG.ES256
|
||||
def keypair = alg.keyPair().build()
|
||||
def data = Strings.ascii(withoutSignature)
|
||||
def payload = new ByteArrayInputStream(data)
|
||||
def payload = Streams.of(data)
|
||||
def invalidSignature = Decoders.BASE64URL.decode(invalidEncodedSignature)
|
||||
def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, invalidSignature)
|
||||
assertFalse("Forged signature must not be considered valid.", alg.verify(request))
|
||||
|
|
|
@ -60,7 +60,7 @@ class GcmAesAeadAlgorithmTest {
|
|||
|
||||
def alg = Jwts.ENC.A256GCM
|
||||
|
||||
def ins = new ByteArrayInputStream(P)
|
||||
def ins = Streams.of(P)
|
||||
def aad = Streams.of(AAD)
|
||||
def out = new ByteArrayOutputStream(8192)
|
||||
def res = new DefaultAeadResult(out)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.lang.Registry
|
||||
import io.jsonwebtoken.security.HashAlgorithm
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
|
@ -80,7 +81,7 @@ class HashAlgorithmsTest {
|
|||
|
||||
static DefaultRequest<InputStream> request(String msg) {
|
||||
byte[] data = msg.getBytes(StandardCharsets.UTF_8)
|
||||
InputStream payload = new ByteArrayInputStream(data)
|
||||
InputStream payload = Streams.of(data)
|
||||
return new DefaultRequest<InputStream>(payload, null, null)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.impl.lang.Bytes
|
||||
import io.jsonwebtoken.impl.lang.CheckedFunction
|
||||
import io.jsonwebtoken.lang.Classes
|
||||
|
@ -347,7 +348,7 @@ class JcaTemplateTest {
|
|||
X509Certificate cert = template.withCertificateFactory(new CheckedFunction<CertificateFactory, X509Certificate>() {
|
||||
@Override
|
||||
X509Certificate apply(CertificateFactory certificateFactory) throws Exception {
|
||||
(X509Certificate)certificateFactory.generateCertificate(new ByteArrayInputStream(expected.getEncoded()))
|
||||
(X509Certificate) certificateFactory.generateCertificate(Streams.of(expected.getEncoded()))
|
||||
}
|
||||
})
|
||||
assertEquals expected, cert
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
package io.jsonwebtoken.impl.security
|
||||
|
||||
import io.jsonwebtoken.impl.RfcTests
|
||||
import io.jsonwebtoken.impl.io.Streams
|
||||
import io.jsonwebtoken.security.HashAlgorithm
|
||||
import io.jsonwebtoken.security.JwkThumbprint
|
||||
import io.jsonwebtoken.security.Jwks
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.SecretKey
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import static io.jsonwebtoken.impl.security.DefaultHashAlgorithm.SHA1
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
@ -32,8 +32,7 @@ class JwkThumbprintsTest {
|
|||
static final HashAlgorithm SHA256 = Jwks.HASH.@SHA256
|
||||
|
||||
static byte[] digest(String json, HashAlgorithm alg) {
|
||||
def utf8Bytes = json.getBytes(StandardCharsets.UTF_8)
|
||||
def payload = new ByteArrayInputStream(utf8Bytes);
|
||||
def payload = Streams.of(json)
|
||||
def req = new DefaultRequest(payload, null, null)
|
||||
return alg.digest(req)
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ class EncryptionAlgorithmsTest {
|
|||
assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)
|
||||
}
|
||||
|
||||
def ciphertext = new ByteArrayInputStream(ciphertextBytes)
|
||||
def ciphertext = Streams.of(ciphertextBytes)
|
||||
out = new ByteArrayOutputStream(8192)
|
||||
def dreq = new DefaultDecryptAeadRequest(ciphertext, key, null, iv, tag)
|
||||
alg.decrypt(dreq, out)
|
||||
|
@ -155,7 +155,7 @@ class EncryptionAlgorithmsTest {
|
|||
assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)
|
||||
}
|
||||
|
||||
def ciphertext = new ByteArrayInputStream(ciphertextBytes)
|
||||
def ciphertext = Streams.of(ciphertextBytes)
|
||||
out = new ByteArrayOutputStream(8192)
|
||||
def dreq = new DefaultDecryptAeadRequest(ciphertext, key, aad, iv, tag)
|
||||
alg.decrypt(dreq, out)
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -133,7 +133,10 @@
|
|||
<test.addOpens>
|
||||
--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.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>
|
||||
|
||||
</properties>
|
||||
|
|
Loading…
Reference in New Issue