Merge pull request #29 from jwtk/android

Android + Better test coverage.
This commit is contained in:
Les Hazlewood 2015-05-12 17:09:06 -07:00
commit 078dbaa9c2
36 changed files with 540 additions and 127 deletions

126
pom.xml
View File

@ -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>

View File

@ -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.");
}
}

View 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);
}
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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);

View 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();
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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.'
}
}
}

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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')
}

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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())
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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."
}
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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() {

View File

@ -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 {

View File

@ -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 {