mirror of
https://github.com/jwtk/jjwt.git
synced 2025-03-09 06:46:50 +00:00
Merge pull request #29 from jwtk/android
Android + Better test coverage.
This commit is contained in:
commit
078dbaa9c2
126
pom.xml
126
pom.xml
@ -70,8 +70,9 @@
|
||||
<!-- Test Dependencies: Only required for testing when building. Not required by users at runtime: -->
|
||||
<groovy.version>2.3.0-beta-2</groovy.version>
|
||||
<logback.version>1.0.7</logback.version>
|
||||
<easymock.version>3.1</easymock.version>
|
||||
<testng.version>6.8</testng.version>
|
||||
<easymock.version>3.3.1</easymock.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<powermock.version>1.6.2</powermock.version>
|
||||
<failsafe.plugin.version>2.12.4</failsafe.plugin.version>
|
||||
|
||||
</properties>
|
||||
@ -83,14 +84,6 @@
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
@ -106,6 +99,22 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Provided scope: only used in Android environments - not downloaded as a transitive dependency.
|
||||
This dependency is only a stub of the actual implementation, which means we can't use it at test time.
|
||||
An Android environment is required to test for real. -->
|
||||
<dependency>
|
||||
<groupId>com.google.android</groupId>
|
||||
<artifactId>android</artifactId>
|
||||
<version>4.1.1.4</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Dependencies: Only required for testing when building. Not required by users at runtime: -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
@ -113,12 +122,6 @@
|
||||
<version>${logback.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>${testng.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
@ -131,6 +134,30 @@
|
||||
<version>${easymock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-easymock</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-core</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@ -249,6 +276,73 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<configuration>
|
||||
<instrumentation>
|
||||
<excludes>
|
||||
<exclude>io/jsonwebtoken/lang/*.class</exclude>
|
||||
</excludes>
|
||||
<ignores>
|
||||
<ignore>io.jsonwebtoken.lang.*</ignore>
|
||||
</ignores>
|
||||
</instrumentation>
|
||||
<check>
|
||||
<lineRate>100</lineRate>
|
||||
<branchRate>100</branchRate>
|
||||
<packageLineRate>100</packageLineRate>
|
||||
<packageBranchRate>100</packageBranchRate>
|
||||
<haltOnFailure>true</haltOnFailure>
|
||||
<regexes>
|
||||
<regex>
|
||||
<!-- Cannot get to 100% on DefaultClaims because of Cobertura bug w/ generics:
|
||||
https://github.com/cobertura/cobertura/issues/207 -->
|
||||
<pattern>io.jsonwebtoken.impl.DefaultClaims</pattern>
|
||||
<lineRate>96</lineRate>
|
||||
<branchRate>100</branchRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>io.jsonwebtoken.impl.Base64UrlCodec</pattern>
|
||||
<lineRate>100</lineRate>
|
||||
<branchRate>95</branchRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>io.jsonwebtoken.impl.DefaultJwtBuilder</pattern>
|
||||
<lineRate>91</lineRate>
|
||||
<branchRate>85</branchRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>io.jsonwebtoken.impl.DefaultJwtParser</pattern>
|
||||
<lineRate>97</lineRate>
|
||||
<branchRate>88</branchRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator</pattern>
|
||||
<lineRate>95</lineRate>
|
||||
<branchRate>100</branchRate>
|
||||
</regex>
|
||||
<regex>
|
||||
<pattern>io.jsonwebtoken.impl.crypto.RsaSignatureValidator</pattern>
|
||||
<lineRate>93</lineRate>
|
||||
<branchRate>100</branchRate>
|
||||
</regex>
|
||||
</regexes>
|
||||
</check>
|
||||
<formats>
|
||||
<format>html</format>
|
||||
</formats>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
|
@ -43,20 +43,22 @@ public class SigningKeyResolverAdapter implements SigningKeyResolver {
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, Claims claims) {
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.forName(header.getAlgorithm());
|
||||
byte[] keyBytes = resolveSigningKeyBytes(header, claims);
|
||||
Assert.isTrue(!alg.isRsa(), "resolveSigningKeyBytes(JwsHeader, Claims) cannot be used for RSA signatures. " +
|
||||
Assert.isTrue(alg.isHmac(), "The default resolveSigningKey(JwsHeader, Claims) implementation cannot be " +
|
||||
"used for asymmetric key algorithms (RSA, Elliptic Curve). " +
|
||||
"Override the resolveSigningKey(JwsHeader, Claims) method instead and return a " +
|
||||
"PublicKey or PrivateKey instance.");
|
||||
"Key instance appropriate for the " + alg.name() + " algorithm.");
|
||||
byte[] keyBytes = resolveSigningKeyBytes(header, claims);
|
||||
return new SecretKeySpec(keyBytes, alg.getJcaName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key resolveSigningKey(JwsHeader header, String plaintext) {
|
||||
SignatureAlgorithm alg = SignatureAlgorithm.forName(header.getAlgorithm());
|
||||
byte[] keyBytes = resolveSigningKeyBytes(header, plaintext);
|
||||
Assert.isTrue(!alg.isRsa(), "resolveSigningKeyBytes(JwsHeader, String) cannot be used for RSA signatures. " +
|
||||
Assert.isTrue(alg.isHmac(), "The default resolveSigningKey(JwsHeader, String) implementation cannot be " +
|
||||
"used for asymmetric key algorithms (RSA, Elliptic Curve). " +
|
||||
"Override the resolveSigningKey(JwsHeader, String) method instead and return a " +
|
||||
"PublicKey or PrivateKey instance.");
|
||||
"Key instance appropriate for the " + alg.name() + " algorithm.");
|
||||
byte[] keyBytes = resolveSigningKeyBytes(header, plaintext);
|
||||
return new SecretKeySpec(keyBytes, alg.getJcaName());
|
||||
}
|
||||
|
||||
@ -65,7 +67,8 @@ public class SigningKeyResolverAdapter implements SigningKeyResolver {
|
||||
* key bytes. This implementation simply throws an exception: if the JWS parsed is a Claims JWS, you must
|
||||
* override this method or the {@link #resolveSigningKey(JwsHeader, Claims)} method instead.
|
||||
*
|
||||
* <p><b>NOTE:</b> You cannot override this method when validating RSA signatures. If you expect RSA signatures, </p>
|
||||
* <p><b>NOTE:</b> You cannot override this method when validating RSA signatures. If you expect RSA signatures,
|
||||
* you must override the {@link #resolveSigningKey(JwsHeader, Claims)} method instead.</p>
|
||||
*
|
||||
* @param header the parsed {@link JwsHeader}
|
||||
* @param claims the parsed {@link Claims}
|
||||
@ -74,7 +77,7 @@ public class SigningKeyResolverAdapter implements SigningKeyResolver {
|
||||
public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
|
||||
throw new UnsupportedJwtException("The specified SigningKeyResolver implementation does not support " +
|
||||
"Claims JWS signing key resolution. Consider overriding either the " +
|
||||
"resolveSigningKey(JwsHeader, Claims) or " +
|
||||
"resolveSigningKey(JwsHeader, Claims) method or, for HMAC algorithms, the " +
|
||||
"resolveSigningKeyBytes(JwsHeader, Claims) method.");
|
||||
}
|
||||
|
||||
@ -90,7 +93,7 @@ public class SigningKeyResolverAdapter implements SigningKeyResolver {
|
||||
public byte[] resolveSigningKeyBytes(JwsHeader header, String payload) {
|
||||
throw new UnsupportedJwtException("The specified SigningKeyResolver implementation does not support " +
|
||||
"plaintext JWS signing key resolution. Consider overriding either the " +
|
||||
"resolveSigningKey(JwsHeader, String) or " +
|
||||
"resolveSigningKey(JwsHeader, String) method or, for HMAC algorithms, the " +
|
||||
"resolveSigningKeyBytes(JwsHeader, String) method.");
|
||||
}
|
||||
}
|
||||
|
30
src/main/java/io/jsonwebtoken/impl/AndroidBase64Codec.java
Normal file
30
src/main/java/io/jsonwebtoken/impl/AndroidBase64Codec.java
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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;
|
||||
|
||||
public class AndroidBase64Codec extends AbstractTextCodec {
|
||||
|
||||
@Override
|
||||
public String encode(byte[] data) {
|
||||
int flags = android.util.Base64.NO_PADDING | android.util.Base64.NO_WRAP;
|
||||
return android.util.Base64.encodeToString(data, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decode(String encoded) {
|
||||
return android.util.Base64.decode(encoded, android.util.Base64.DEFAULT);
|
||||
}
|
||||
}
|
@ -15,18 +15,14 @@
|
||||
*/
|
||||
package io.jsonwebtoken.impl;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
public class Base64Codec extends AbstractTextCodec {
|
||||
|
||||
@Override
|
||||
public String encode(byte[] data) {
|
||||
return DatatypeConverter.printBase64Binary(data);
|
||||
return javax.xml.bind.DatatypeConverter.printBase64Binary(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decode(String encoded) {
|
||||
return DatatypeConverter.parseBase64Binary(encoded);
|
||||
return javax.xml.bind.DatatypeConverter.parseBase64Binary(encoded);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class DefaultJwtBuilder implements JwtBuilder {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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;
|
||||
|
||||
public class DefaultTextCodecFactory implements TextCodecFactory {
|
||||
|
||||
protected String getSystemProperty(String key) {
|
||||
return System.getProperty(key);
|
||||
}
|
||||
|
||||
protected boolean isAndroid() {
|
||||
|
||||
String name = getSystemProperty("java.vm.name");
|
||||
if (name != null) {
|
||||
String lcase = name.toLowerCase();
|
||||
return lcase.contains("dalvik");
|
||||
}
|
||||
|
||||
name = getSystemProperty("java.vm.vendor");
|
||||
if (name != null) {
|
||||
String lcase = name.toLowerCase();
|
||||
return lcase.contains("android");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TextCodec getTextCodec() {
|
||||
|
||||
if (isAndroid()) {
|
||||
return new AndroidBase64Codec();
|
||||
}
|
||||
|
||||
return new Base64Codec();
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ package io.jsonwebtoken.impl;
|
||||
|
||||
public interface TextCodec {
|
||||
|
||||
public static final TextCodec BASE64 = new Base64Codec();
|
||||
public static final TextCodec BASE64 = new DefaultTextCodecFactory().getTextCodec();
|
||||
public static final TextCodec BASE64URL = new Base64UrlCodec();
|
||||
|
||||
String encode(String data);
|
||||
|
21
src/main/java/io/jsonwebtoken/impl/TextCodecFactory.java
Normal file
21
src/main/java/io/jsonwebtoken/impl/TextCodecFactory.java
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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;
|
||||
|
||||
public interface TextCodecFactory {
|
||||
|
||||
TextCodec getTextCodec();
|
||||
}
|
@ -55,7 +55,8 @@ public abstract class EllipticCurveProvider extends SignatureProvider {
|
||||
}
|
||||
|
||||
public static KeyPair generateKeyPair(String jcaAlgorithmName, String jcaProviderName, SignatureAlgorithm alg, SecureRandom random) {
|
||||
Assert.isTrue(alg != null && alg.isEllipticCurve(), "SignatureAlgorithm argument must represent an Elliptic Curve algorithm.");
|
||||
Assert.notNull(alg, "SignatureAlgorithm argument cannot be null.");
|
||||
Assert.isTrue(alg.isEllipticCurve(), "SignatureAlgorithm argument must represent an Elliptic Curve algorithm.");
|
||||
try {
|
||||
KeyPairGenerator g = KeyPairGenerator.getInstance(jcaAlgorithmName, jcaProviderName);
|
||||
String paramSpecCurveName = EC_CURVE_NAMES.get(alg);
|
||||
|
@ -23,20 +23,15 @@ import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
|
||||
public class EllipticCurveSignatureValidator extends EllipticCurveProvider implements SignatureValidator {
|
||||
|
||||
private static final String NO_EC_PRIVATE_KEY_MSG =
|
||||
"Elliptic Curve signature validation requires an ECPublicKey. ECPrivateKeys may not be used.";
|
||||
|
||||
private static final String EC_PUBLIC_KEY_REQD_MSG =
|
||||
"Elliptic Curve Signature validation requires either an ECPublicKey instance.";
|
||||
"Elliptic Curve signature validation requires an ECPublicKey instance.";
|
||||
|
||||
public EllipticCurveSignatureValidator(SignatureAlgorithm alg, Key key) {
|
||||
super(alg, key);
|
||||
Assert.isTrue(!(key instanceof ECPrivateKey), NO_EC_PRIVATE_KEY_MSG);
|
||||
Assert.isTrue(key instanceof ECPublicKey, EC_PUBLIC_KEY_REQD_MSG);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class RsaSignatureValidator extends RsaProvider implements SignatureValid
|
||||
throw new SignatureException(msg, e);
|
||||
}
|
||||
} else {
|
||||
assert this.SIGNER != null;
|
||||
Assert.notNull(this.SIGNER, "RSA Signer instance cannot be null. This is a bug. Please report it.");
|
||||
byte[] computed = this.SIGNER.sign(data);
|
||||
return Arrays.equals(computed, signature);
|
||||
}
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class ExpiredJwtExceptionTest {
|
||||
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class JwtHandlerAdapterTest {
|
||||
|
||||
|
@ -16,13 +16,12 @@
|
||||
package io.jsonwebtoken
|
||||
|
||||
import io.jsonwebtoken.impl.TextCodec
|
||||
import org.testng.annotations.Test
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import java.security.SecureRandom
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class JwtParserTest {
|
||||
|
||||
@ -97,7 +96,7 @@ class JwtParserTest {
|
||||
Jwts.parser().setSigningKey(randomKey()).parse(bad);
|
||||
fail()
|
||||
} catch (SignatureException se) {
|
||||
assertEquals se.getMessage(), "Unsupported signature algorithm '$badAlgorithmName'"
|
||||
assertEquals se.getMessage(), "Unsupported signature algorithm '$badAlgorithmName'".toString()
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,8 +638,8 @@ class JwtParserTest {
|
||||
fail()
|
||||
} catch (UnsupportedJwtException ex) {
|
||||
assertEquals ex.getMessage(), 'The specified SigningKeyResolver implementation does not support ' +
|
||||
'Claims JWS signing key resolution. Consider overriding either the ' +
|
||||
'resolveSigningKey(JwsHeader, Claims) or resolveSigningKeyBytes(JwsHeader, Claims) method.'
|
||||
'Claims JWS signing key resolution. Consider overriding either the resolveSigningKey(JwsHeader, Claims) method ' +
|
||||
'or, for HMAC algorithms, the resolveSigningKeyBytes(JwsHeader, Claims) method.'
|
||||
}
|
||||
}
|
||||
|
||||
@ -709,8 +708,8 @@ class JwtParserTest {
|
||||
fail()
|
||||
} catch (UnsupportedJwtException ex) {
|
||||
assertEquals ex.getMessage(), 'The specified SigningKeyResolver implementation does not support plaintext ' +
|
||||
'JWS signing key resolution. Consider overriding either the ' +
|
||||
'resolveSigningKey(JwsHeader, String) or resolveSigningKeyBytes(JwsHeader, String) method.'
|
||||
'JWS signing key resolution. Consider overriding either the resolveSigningKey(JwsHeader, String) ' +
|
||||
'method or, for HMAC algorithms, the resolveSigningKeyBytes(JwsHeader, String) method.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import io.jsonwebtoken.impl.TextCodec
|
||||
import io.jsonwebtoken.impl.crypto.EllipticCurveProvider
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import io.jsonwebtoken.impl.crypto.RsaProvider
|
||||
import org.testng.annotations.Test
|
||||
import org.junit.Test
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
@ -31,7 +31,7 @@ import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class JwtsTest {
|
||||
|
||||
@ -108,20 +108,20 @@ class JwtsTest {
|
||||
|
||||
def token = Jwts.parser().parse(jwt);
|
||||
|
||||
assertEquals token.body, claims
|
||||
assert token.body == claims
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException)
|
||||
@Test(expected = IllegalArgumentException)
|
||||
void testParseNull() {
|
||||
Jwts.parser().parse(null)
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException)
|
||||
@Test(expected = IllegalArgumentException)
|
||||
void testParseEmptyString() {
|
||||
Jwts.parser().parse('')
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException)
|
||||
@Test(expected = IllegalArgumentException)
|
||||
void testParseWhitespaceString() {
|
||||
Jwts.parser().parse(' ')
|
||||
}
|
||||
@ -401,7 +401,7 @@ class JwtsTest {
|
||||
testEC(SignatureAlgorithm.ES256, true)
|
||||
fail("EC private keys cannot be used to validate EC signatures.")
|
||||
} catch (UnsupportedJwtException e) {
|
||||
assertEquals e.cause.message, "Elliptic Curve signature validation requires an ECPublicKey. ECPrivateKeys may not be used."
|
||||
assertEquals e.cause.message, "Elliptic Curve signature validation requires an ECPublicKey instance."
|
||||
}
|
||||
}
|
||||
|
||||
@ -520,6 +520,39 @@ class JwtsTest {
|
||||
}
|
||||
}
|
||||
|
||||
//Asserts correct behavior for https://github.com/jwtk/jjwt/issues/25
|
||||
@Test
|
||||
void testParseForgedEllipticCurvePublicKeyAsHmacToken() {
|
||||
|
||||
//Create a legitimate RSA public and private key pair:
|
||||
KeyPair kp = EllipticCurveProvider.generateKeyPair()
|
||||
PublicKey publicKey = kp.getPublic();
|
||||
//PrivateKey privateKey = kp.getPrivate();
|
||||
|
||||
ObjectMapper om = new ObjectMapper()
|
||||
String header = TextCodec.BASE64URL.encode(om.writeValueAsString(['alg': 'HS256']))
|
||||
String body = TextCodec.BASE64URL.encode(om.writeValueAsString('foo'))
|
||||
String compact = header + '.' + body + '.'
|
||||
|
||||
// Now for the forgery: simulate an attacker using the Elliptic Curve public key to sign a token, but
|
||||
// using it as an HMAC signing key instead of Elliptic Curve:
|
||||
Mac mac = Mac.getInstance('HmacSHA256');
|
||||
mac.init(new SecretKeySpec(publicKey.getEncoded(), 'HmacSHA256'));
|
||||
byte[] signatureBytes = mac.doFinal(compact.getBytes(Charset.forName('US-ASCII')))
|
||||
String encodedSignature = TextCodec.BASE64URL.encode(signatureBytes);
|
||||
|
||||
//Finally, the forged token is the header + body + forged signature:
|
||||
String forged = compact + encodedSignature;
|
||||
|
||||
// Assert that the parser does not recognized the forged token:
|
||||
try {
|
||||
Jwts.parser().setSigningKey(publicKey).parse(forged);
|
||||
fail("Forged token must not be successfully parsed.")
|
||||
} catch (UnsupportedJwtException expected) {
|
||||
assertTrue expected.getMessage().startsWith('The parsed JWT indicates it was signed with the')
|
||||
}
|
||||
}
|
||||
|
||||
static void testRsa(SignatureAlgorithm alg, int keySize=1024, boolean verifyWithPrivateKey=false) {
|
||||
|
||||
KeyPair kp = RsaProvider.generateKeyPair(keySize)
|
||||
@ -537,10 +570,8 @@ class JwtsTest {
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||
|
||||
assertEquals token.header, [alg: alg.name()]
|
||||
|
||||
assertEquals token.body, claims
|
||||
|
||||
assert [alg: alg.name()] == token.header
|
||||
assert token.body == claims
|
||||
}
|
||||
|
||||
static void testHmac(SignatureAlgorithm alg) {
|
||||
@ -553,9 +584,8 @@ class JwtsTest {
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt)
|
||||
|
||||
assertEquals token.header, [alg: alg.name()]
|
||||
|
||||
assertEquals token.body, claims
|
||||
assert token.header == [alg: alg.name()]
|
||||
assert token.body == claims
|
||||
}
|
||||
|
||||
static void testEC(SignatureAlgorithm alg, boolean verifyWithPrivateKey=false) {
|
||||
@ -575,9 +605,8 @@ class JwtsTest {
|
||||
|
||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||
|
||||
assertEquals token.header, [alg: alg.name()]
|
||||
|
||||
assertEquals token.body, claims
|
||||
assert token.header == [alg: alg.name()]
|
||||
assert token.body == claims
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class PrematureJwtExceptionTest {
|
||||
|
||||
|
@ -15,9 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class SignatureAlgorithmTest {
|
||||
|
||||
@ -38,7 +37,7 @@ class SignatureAlgorithmTest {
|
||||
assert alg.description != null && alg.description != ""
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = SignatureException)
|
||||
@Test(expected = SignatureException)
|
||||
void testUnrecognizedAlgorithmName() {
|
||||
SignatureAlgorithm.forName('whatever')
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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
|
||||
|
||||
import io.jsonwebtoken.impl.crypto.RsaProvider
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
class SigningKeyResolverAdapterTest {
|
||||
|
||||
@Test
|
||||
void testResolveClaimsSigningKeyWithRsaKey() {
|
||||
|
||||
def pair = RsaProvider.generateKeyPair(1024) //real apps should use 4096 or better. We're only reducing the size here so the tests are fast
|
||||
|
||||
def compact = Jwts.builder().claim('foo', 'bar').signWith(SignatureAlgorithm.RS256, pair.private).compact()
|
||||
|
||||
Jws<Claims> jws = Jwts.parser().setSigningKey(pair.public).parseClaimsJws(compact)
|
||||
|
||||
try {
|
||||
new SigningKeyResolverAdapter().resolveSigningKey(jws.header, jws.body)
|
||||
fail()
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals "The default resolveSigningKey(JwsHeader, Claims) implementation cannot be used for asymmetric key algorithms (RSA, Elliptic Curve). Override the resolveSigningKey(JwsHeader, Claims) method instead and return a Key instance appropriate for the RS256 algorithm.", iae.message
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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
|
||||
|
||||
import android.util.Base64
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import static org.easymock.EasyMock.*
|
||||
import static org.junit.Assert.*
|
||||
import static org.powermock.api.easymock.PowerMock.mockStatic
|
||||
import static org.powermock.api.easymock.PowerMock.replayAll
|
||||
import static org.powermock.api.easymock.PowerMock.verifyAll
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest([Base64.class])
|
||||
class AndroidBase64CodecTest {
|
||||
|
||||
@Test
|
||||
public void testEncode() {
|
||||
|
||||
mockStatic(Base64.class);
|
||||
|
||||
byte[] bytes = new byte[32];
|
||||
String s = "foo";
|
||||
int flags = Base64.NO_PADDING | Base64.NO_WRAP;
|
||||
|
||||
expect(Base64.encodeToString(same(bytes), eq(flags))).andReturn(s);
|
||||
replayAll();
|
||||
|
||||
AndroidBase64Codec codec = new AndroidBase64Codec();
|
||||
|
||||
String val = codec.encode(bytes);
|
||||
|
||||
verifyAll();
|
||||
assertEquals(val, s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecode() {
|
||||
|
||||
mockStatic(Base64.class);
|
||||
|
||||
byte[] bytes = new byte[32];
|
||||
String s = "foo";
|
||||
|
||||
expect(Base64.decode((String)same(s), eq(Base64.DEFAULT))).andReturn(bytes);
|
||||
replayAll();
|
||||
|
||||
AndroidBase64Codec codec = new AndroidBase64Codec();
|
||||
|
||||
def val = codec.decode(s);
|
||||
|
||||
verifyAll();
|
||||
assertSame bytes, val
|
||||
}
|
||||
}
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultHeaderTest {
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultJwsHeaderTest {
|
||||
|
||||
|
@ -17,8 +17,8 @@ package io.jsonwebtoken.impl
|
||||
|
||||
import io.jsonwebtoken.JwsHeader
|
||||
import io.jsonwebtoken.Jwts
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultJwsTest {
|
||||
|
||||
|
@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.JsonMappingException
|
||||
import io.jsonwebtoken.Jwts
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultJwtBuilderTest {
|
||||
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest([System.class])
|
||||
class DefaultTextCodecFactoryTest {
|
||||
|
||||
@Test
|
||||
void testIsAndroidByVmName() {
|
||||
|
||||
def factory = new DefaultTextCodecFactory() {
|
||||
@Override
|
||||
protected String getSystemProperty(String key) {
|
||||
return 'dalvik'
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue factory.getTextCodec() instanceof AndroidBase64Codec
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsAndroidByNullVmName() {
|
||||
|
||||
def factory = new DefaultTextCodecFactory() {
|
||||
@Override
|
||||
protected String getSystemProperty(String key) {
|
||||
if (key == 'java.vm.name') return null;
|
||||
return 'android'
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue factory.getTextCodec() instanceof AndroidBase64Codec
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsAndroidByNullVmNameAndNullVendorName() {
|
||||
|
||||
def factory = new DefaultTextCodecFactory() {
|
||||
@Override
|
||||
protected String getSystemProperty(String key) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
assertFalse factory.getTextCodec() instanceof AndroidBase64Codec
|
||||
}
|
||||
}
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
package io.jsonwebtoken.impl
|
||||
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class JwtMapTest {
|
||||
|
||||
@ -115,6 +115,7 @@ class JwtMapTest {
|
||||
void testValues() {
|
||||
def m = new JwtMap()
|
||||
m.putAll([a: 'b', c: 'd'])
|
||||
assertEquals( m.values(), ['b', 'd'] as Set)
|
||||
def s = ['b', 'd']
|
||||
assertTrue m.values().containsAll(s) && s.containsAll(m.values())
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultSignatureValidatorFactoryTest {
|
||||
|
||||
|
@ -16,11 +16,8 @@
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class DefaultSignerFactoryTest {
|
||||
|
||||
|
@ -16,15 +16,14 @@
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.KeyPair
|
||||
import java.security.NoSuchProviderException
|
||||
import java.security.interfaces.ECPrivateKey
|
||||
import java.security.interfaces.ECPublicKey
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class EllipticCurveProviderTest {
|
||||
|
||||
@ -46,4 +45,24 @@ class EllipticCurveProviderTest {
|
||||
assertTrue ise.cause instanceof NoSuchProviderException
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateKeyPairWithNullAlgorithm() {
|
||||
try {
|
||||
EllipticCurveProvider.generateKeyPair("ECDSA", "Foo", null, null)
|
||||
fail()
|
||||
} catch (IllegalArgumentException ise) {
|
||||
assertEquals ise.message, "SignatureAlgorithm argument cannot be null."
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateKeyPairWithNonEllipticCurveAlgorithm() {
|
||||
try {
|
||||
EllipticCurveProvider.generateKeyPair("ECDSA", "Foo", SignatureAlgorithm.HS256, null)
|
||||
fail()
|
||||
} catch (IllegalArgumentException ise) {
|
||||
assertEquals ise.message, "SignatureAlgorithm argument must represent an Elliptic Curve algorithm."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,13 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.PublicKey
|
||||
import java.security.Signature
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class EllipticCurveSignatureValidatorTest {
|
||||
|
||||
|
@ -17,14 +17,14 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class EllipticCurveSignerTest {
|
||||
|
||||
|
@ -16,10 +16,8 @@
|
||||
package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import static org.testng.Assert.*
|
||||
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class MacProviderTest {
|
||||
|
||||
|
@ -17,8 +17,8 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
import javax.crypto.Mac
|
||||
import java.security.InvalidKeyException
|
||||
|
@ -17,7 +17,6 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.KeyPair
|
||||
@ -26,7 +25,8 @@ import java.security.interfaces.RSAPrivateKey
|
||||
import java.security.interfaces.RSAPublicKey
|
||||
import java.security.spec.PSSParameterSpec
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RsaProviderTest {
|
||||
|
||||
|
@ -17,24 +17,26 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
import org.junit.Test
|
||||
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.Signature
|
||||
|
||||
import static org.testng.Assert.assertEquals
|
||||
import static org.testng.Assert.assertSame
|
||||
import static org.testng.Assert.fail
|
||||
import java.security.*
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RsaSignatureValidatorTest {
|
||||
|
||||
private static final Random rng = new Random(); //doesn't need to be secure - we're just testing
|
||||
|
||||
@Test
|
||||
void testConstructorWithNonRsaKey() {
|
||||
try {
|
||||
new RsaSignatureValidator(SignatureAlgorithm.RS256, MacProvider.generateKey());
|
||||
fail()
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals "RSA Signature validation requires either a RSAPublicKey or RSAPrivateKey instance.", iae.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDoVerifyWithInvalidKeyException() {
|
||||
|
||||
|
@ -17,7 +17,6 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import java.security.InvalidKeyException
|
||||
@ -26,7 +25,8 @@ import java.security.KeyPairGenerator
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RsaSignerTest {
|
||||
|
||||
|
@ -17,12 +17,12 @@ package io.jsonwebtoken.impl.crypto
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.SignatureException
|
||||
import org.testng.annotations.Test
|
||||
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.Signature
|
||||
|
||||
import static org.testng.Assert.*
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class SignatureProviderTest {
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user