/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cxf.rs.security.jose.jws;
import java.security.PrivateKey;
import java.security.Security;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter;
import org.apache.cxf.rs.security.jose.common.JoseConstants;
import org.apache.cxf.rs.security.jose.common.JoseType;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
import org.apache.cxf.rs.security.jose.jwk.KeyOperation;
import org.apache.cxf.rs.security.jose.jwk.KeyType;
import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.cxf.rt.security.crypto.CryptoUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Assert;
import org.junit.Test;
public class JwsCompactReaderWriterTest extends Assert {
public static final String TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD =
"eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY";
public static final String UNSIGNED_PLAIN_DOCUMENT = "$.02";
public static final String ENCODED_TOKEN_SIGNED_BY_MAC =
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
+ ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+ ".dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
private static final String ENCODED_MAC_KEY = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75"
+ "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow";
private static final String ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC =
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU"
+ "zI1NiIsDQogImp3ayI6eyJrdHkiOiJvY3QiLA0KICJrZXlfb3BzIjpbDQogInNpZ24iLA0KICJ2ZXJpZnkiDQogXX19"
+ ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+ ".8cFZqb15gEDYRZqSzUu23nQnKNynru1ADByRPvmmOq8";
private static final String RSA_MODULUS_ENCODED = "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
+ "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
+ "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
+ "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
+ "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
+ "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ";
private static final String RSA_PUBLIC_EXPONENT_ENCODED = "AQAB";
private static final String RSA_PRIVATE_EXPONENT_ENCODED =
"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
+ "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
+ "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
+ "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
+ "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
+ "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ";
private static final String ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY =
"eyJhbGciOiJSUzI1NiJ9"
+ "."
+ "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
+ "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
+ "."
+ "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
+ "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
+ "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
+ "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
+ "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
+ "p0igcN_IoypGlUPQGe77Rw";
private static final String EC_PRIVATE_KEY_ENCODED =
"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI";
private static final String EC_X_POINT_ENCODED =
"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU";
private static final String EC_Y_POINT_ENCODED =
"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0";
@Test
public void testWriteJwsSignedByMacSpecExample() throws Exception {
JwsHeaders headers = new JwsHeaders();
headers.setType(JoseType.JWT);
headers.setSignatureAlgorithm(SignatureAlgorithm.HS256);
JwsCompactProducer jws = initSpecJwtTokenWriter(headers);
jws.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256));
assertEquals(ENCODED_TOKEN_SIGNED_BY_MAC, jws.getSignedEncodedJws());
}
@Test
public void testWriteReadJwsUnencodedPayload() throws Exception {
JwsHeaders headers = new JwsHeaders(SignatureAlgorithm.HS256);
headers.setPayloadEncodingStatus(false);
JwsCompactProducer producer = new JwsCompactProducer(headers,
UNSIGNED_PLAIN_DOCUMENT,
true);
producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256));
assertEquals(TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD, producer.getSignedEncodedJws());
JwsCompactConsumer consumer =
new JwsCompactConsumer(TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD, UNSIGNED_PLAIN_DOCUMENT);
assertTrue(consumer.verifySignatureWith(
new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)));
}
@Test
public void testNoneSignature() throws Exception {
JwtClaims claims = new JwtClaims();
claims.setClaim("a", "b");
JwsJwtCompactProducer producer = new JwsJwtCompactProducer(claims);
producer.signWith(new NoneJwsSignatureProvider());
JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(producer.getSignedEncodedJws());
assertTrue(consumer.verifySignatureWith(new NoneJwsSignatureVerifier()));
JwtClaims claims2 = consumer.getJwtClaims();
assertEquals(claims, claims2);
}
@Test
public void testWriteReadJwsUnsigned() throws Exception {
JwsHeaders headers = new JwsHeaders(JoseType.JWT);
headers.setSignatureAlgorithm(SignatureAlgorithm.NONE);
JwtClaims claims = new JwtClaims();
claims.setIssuer("https://jwt-idp.example.com");
claims.setSubject("mailto:mike@example.com");
claims.setAudiences(Collections.singletonList("https://jwt-rp.example.net"));
claims.setNotBefore(1300815780L);
claims.setExpiryTime(1300819380L);
claims.setClaim("http://claims.example.com/member", true);
JwsCompactProducer writer = new JwsJwtCompactProducer(headers, claims);
String signed = writer.getSignedEncodedJws();
JwsJwtCompactConsumer reader = new JwsJwtCompactConsumer(signed);
assertEquals(0, reader.getDecodedSignature().length);
JwtToken token = reader.getJwtToken();
assertEquals(new JwtToken(headers, claims), token);
}
@Test
public void testReadJwsSignedByMacSpecExample() throws Exception {
JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(ENCODED_TOKEN_SIGNED_BY_MAC);
assertTrue(jws.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY,
SignatureAlgorithm.HS256)));
JwtToken token = jws.getJwtToken();
JwsHeaders headers = new JwsHeaders(token.getJwsHeaders());
assertEquals(JoseType.JWT, headers.getType());
assertEquals(SignatureAlgorithm.HS256, headers.getSignatureAlgorithm());
validateSpecClaim(token.getClaims());
}
@Test
public void testWriteJwsWithJwkSignedByMac() throws Exception {
JsonWebKey key = new JsonWebKey();
key.setKeyType(KeyType.OCTET);
key.setKeyOperation(Arrays.asList(
new KeyOperation[]{KeyOperation.SIGN, KeyOperation.VERIFY}));
doTestWriteJwsWithJwkSignedByMac(key);
}
@Test
public void testWriteJwsWithJwkAsMapSignedByMac() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put(JsonWebKey.KEY_TYPE, JsonWebKey.KEY_TYPE_OCTET);
map.put(JsonWebKey.KEY_OPERATIONS,
new KeyOperation[]{KeyOperation.SIGN, KeyOperation.VERIFY});
doTestWriteJwsWithJwkSignedByMac(map);
}
private void doTestWriteJwsWithJwkSignedByMac(Object jsonWebKey) throws Exception {
JwsHeaders headers = new JwsHeaders();
headers.setType(JoseType.JWT);
headers.setSignatureAlgorithm(SignatureAlgorithm.HS256);
headers.setHeader(JoseConstants.HEADER_JSON_WEB_KEY, jsonWebKey);
JwtClaims claims = new JwtClaims();
claims.setIssuer("joe");
claims.setExpiryTime(1300819380L);
claims.setClaim("http://example.com/is_root", Boolean.TRUE);
JwtToken token = new JwtToken(headers, claims);
JwsCompactProducer jws = new JwsJwtCompactProducer(token, getWriter());
jws.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256));
assertEquals(ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC, jws.getSignedEncodedJws());
}
@Test
public void testReadJwsWithJwkSignedByMac() throws Exception {
JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC);
assertTrue(jws.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY,
SignatureAlgorithm.HS256)));
JwtToken token = jws.getJwtToken();
JwsHeaders headers = new JwsHeaders(token.getJwsHeaders());
assertEquals(JoseType.JWT, headers.getType());
assertEquals(SignatureAlgorithm.HS256, headers.getSignatureAlgorithm());
JsonWebKey key = headers.getJsonWebKey();
assertEquals(KeyType.OCTET, key.getKeyType());
List<KeyOperation> keyOps = key.getKeyOperation();
assertEquals(2, keyOps.size());
assertEquals(KeyOperation.SIGN, keyOps.get(0));
assertEquals(KeyOperation.VERIFY, keyOps.get(1));
validateSpecClaim(token.getClaims());
}
private void validateSpecClaim(JwtClaims claims) {
assertEquals("joe", claims.getIssuer());
assertEquals(Long.valueOf(1300819380), claims.getExpiryTime());
assertEquals(Boolean.TRUE, claims.getClaim("http://example.com/is_root"));
}
@Test
public void testWriteJwsSignedByPrivateKey() throws Exception {
JwsHeaders headers = new JwsHeaders();
headers.setSignatureAlgorithm(SignatureAlgorithm.RS256);
JwsCompactProducer jws = initSpecJwtTokenWriter(headers);
PrivateKey key = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED);
jws.signWith(new PrivateKeyJwsSignatureProvider(key, SignatureAlgorithm.RS256));
assertEquals(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY, jws.getSignedEncodedJws());
}
@Test
public void testJwsPsSha() throws Exception {
Security.addProvider(new BouncyCastleProvider());
try {
JwsHeaders outHeaders = new JwsHeaders();
outHeaders.setSignatureAlgorithm(SignatureAlgorithm.PS256);
JwsCompactProducer producer = initSpecJwtTokenWriter(outHeaders);
PrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED);
String signed = producer.signWith(
new PrivateKeyJwsSignatureProvider(privateKey, SignatureAlgorithm.PS256));
JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(signed);
RSAPublicKey key = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED, RSA_PUBLIC_EXPONENT_ENCODED);
assertTrue(jws.verifySignatureWith(new PublicKeyJwsSignatureVerifier(key, SignatureAlgorithm.PS256)));
JwtToken token = jws.getJwtToken();
JwsHeaders inHeaders = new JwsHeaders(token.getJwsHeaders());
assertEquals(SignatureAlgorithm.PS256,
inHeaders.getSignatureAlgorithm());
validateSpecClaim(token.getClaims());
} finally {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
}
}
@Test
public void testWriteReadJwsSignedByESPrivateKey() throws Exception {
JwsHeaders headers = new JwsHeaders();
headers.setSignatureAlgorithm(SignatureAlgorithm.ES256);
JwsCompactProducer jws = initSpecJwtTokenWriter(headers);
ECPrivateKey privateKey = CryptoUtils.getECPrivateKey(JsonWebKey.EC_CURVE_P256,
EC_PRIVATE_KEY_ENCODED);
jws.signWith(new EcDsaJwsSignatureProvider(privateKey, SignatureAlgorithm.ES256));
String signedJws = jws.getSignedEncodedJws();
ECPublicKey publicKey = CryptoUtils.getECPublicKey(JsonWebKey.EC_CURVE_P256,
EC_X_POINT_ENCODED,
EC_Y_POINT_ENCODED);
JwsJwtCompactConsumer jwsConsumer = new JwsJwtCompactConsumer(signedJws);
assertTrue(jwsConsumer.verifySignatureWith(new EcDsaJwsSignatureVerifier(publicKey,
SignatureAlgorithm.ES256)));
JwtToken token = jwsConsumer.getJwtToken();
JwsHeaders headersReceived = new JwsHeaders(token.getJwsHeaders());
assertEquals(SignatureAlgorithm.ES256, headersReceived.getSignatureAlgorithm());
validateSpecClaim(token.getClaims());
}
@Test
public void testReadJwsSignedByPrivateKey() throws Exception {
JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY);
RSAPublicKey key = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED, RSA_PUBLIC_EXPONENT_ENCODED);
assertTrue(jws.verifySignatureWith(new PublicKeyJwsSignatureVerifier(key, SignatureAlgorithm.RS256)));
JwtToken token = jws.getJwtToken();
JwsHeaders headers = new JwsHeaders(token.getJwsHeaders());
assertEquals(SignatureAlgorithm.RS256, headers.getSignatureAlgorithm());
validateSpecClaim(token.getClaims());
}
private JwsCompactProducer initSpecJwtTokenWriter(JwsHeaders jwsHeaders) throws Exception {
JwtClaims claims = new JwtClaims();
claims.setIssuer("joe");
claims.setExpiryTime(1300819380L);
claims.setClaim("http://example.com/is_root", Boolean.TRUE);
JwtToken token = new JwtToken(jwsHeaders, claims);
return new JwsJwtCompactProducer(token, getWriter());
}
private JsonMapObjectReaderWriter getWriter() {
JsonMapObjectReaderWriter jsonWriter = new JsonMapObjectReaderWriter();
jsonWriter.setFormat(true);
return jsonWriter;
}
}