/*
 * Copyright 2012-2017 Brian Campbell
 *
 * 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 org.jose4j.cookbook;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.jose4j.jwa.AlgorithmConstraints.ConstraintType.WHITELIST;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import java.security.Key;
import java.util.Map;

import org.jose4j.base64url.Base64Url;
import org.jose4j.jca.ProviderContextTest;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwa.JceProviderTestSupport;
import org.jose4j.jwa.JceProviderTestSupport.RunnableTest;
import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
import org.jose4j.jwe.ContentEncryptionKeyDescriptor;
import org.jose4j.jwe.JsonWebEncryption;
import org.jose4j.jwe.KeyManagementAlgorithm;
import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;
import org.jose4j.jwk.EllipticCurveJsonWebKey;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.OctetSequenceJsonWebKey;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.Use;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.jwx.CompactSerializer;
import org.jose4j.jwx.HeaderParameterNames;
import org.jose4j.jwx.Headers;
import org.jose4j.keys.AesKey;
import org.jose4j.keys.EllipticCurves;
import org.jose4j.keys.PbkdfKey;
import org.jose4j.lang.JoseException;
import org.jose4j.lang.JsonHelp;
import org.junit.Test;

/**
 * Tests of the examples from the JOSE Cookbook.
 * Started with http://tools.ietf.org/html/draft-ietf-jose-cookbook-01 and then worked with
 * incremental updates from Matt at https://github.com/linuxwolf/jose-more
 * and eventually resulted in http://tools.ietf.org/html/draft-ietf-jose-cookbook-02 that
 * incorporates fixes to issues found while doing all this. Way at the end on page 94
 * there's even acknowledgement of it
 * http://tools.ietf.org/html/draft-ietf-jose-cookbook-02#appendix-A
 *
 * and by -06 I got myself in there twice!
 * https://tools.ietf.org/html/draft-ietf-jose-cookbook-06#appendix-A
 *
 * 3.1.  EC Public Key
 * 3.2.  EC Private Key
 * 3.3.  RSA Public Key
 * 3.4.  RSA Private Key
 * 3.5.  Octet Key (MAC Computation)
 * 3.6.  Octet Key (Encryption)
 *
 * 4.1. RSA v1.5 Signature
 * 4.2. RSA-PSS Signature (via the the Bouncy Castle provider)
 * 4.3. ECDSA Signature
 * 4.4. HMAC-SHA2 Integrity Protection
 * 4.5. Detached Signature
 *
 * 5.1. Key Encryption using RSA v1.5 and AES-HMAC-SHA2
 * 5.2. Key Encryption using RSA-OAEP with A256GCM
 * 5.3. Key Wrap using PBES2-AES-KeyWrap with AES-CBC-HMAC-SHA2
 * 5.4. Key Agreement with Key Wrapping using ECDH-ES and AES-KeyWrap with AES-GCM
 * 5.5. Key Agreement using ECDH-ES with AES-CBC-HMAC-SHA2
 * 5.6. Direct Encryption using AES-GCM
 * 5.7. Key Wrap using AES-GCM KeyWrap with AES-CBC-HMAC-SHA2
 * 5.8. Key Wrap using AES-KeyWrap with AES-GCM
 * 5.9. Compressed Content
 *
 * 6. Nesting Signatures and Encryption
 *
 */
public class JoseCookbookTest
{
    // http://tools.ietf.org/html/draft-ietf-jose-cookbook-01#section-3
    String encodedJwsPayload =
            "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" +
            "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" +
            "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" +
            "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4";
    String jwsPayload = Base64Url.decodeToUtf8String(encodedJwsPayload);

    // http://tools.ietf.org/html/draft-ietf-jose-cookbook-01#section-4
    String jwePlaintext = "You can trust us to stick with you through thick and " +
            "thin–to the bitter end. And you can trust us to "+
            "keep any secret of yours–closer than you keep it " +
            "yourself. But you cannot trust us to let you face trouble " +
            "alone, and go off without a word. We are your friends, Frodo.";

    String figure3RsaJwkJsonString =
            "{" +
            "  \"kty\": \"RSA\"," +
            "  \"kid\": \"bilbo.baggins@hobbiton.example\",\n" +
            "  \"use\": \"sig\",\n" +
            "  \"n\": \"n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT\n" +
            "      -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV\n" +
            "      wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-\n" +
            "      oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde\n" +
            "      3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC\n" +
            "      LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g\n" +
            "      HdrNP5zw\",\n" +
            "  \"e\": \"AQAB\",\n" +
            "  \"d\": \"bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e\n" +
            "      iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld\n" +
            "      Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b\n" +
            "      MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU\n" +
            "      6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj\n" +
            "      d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc\n" +
            "      OpBrQzwQ\",\n" +
            "  \"p\": \"3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR\n" +
            "      aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG\n" +
            "      peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8\n" +
            "      bUq0k\",\n" +
            "  \"q\": \"uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT\n" +
            "      8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an\n" +
            "      V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0\n" +
            "      s7pFc\",\n" +
            "  \"dp\": \"B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q\n" +
            "      1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn\n" +
            "      -RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX\n" +
            "      59ehik\",\n" +
            "  \"dq\": \"CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr\n" +
            "      AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK\n" +
            "      bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK\n" +
            "      T1cYF8\",\n" +
            "  \"qi\": \"3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N\n" +
            "      ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh\n" +
            "      jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP\n" +
            "      z8aaI4\"\n" +
            "}";

    String figure62RsaJwkJsonString =
        "{\n" +
        "  \"kty\": \"RSA\",\n" +
        "  \"kid\": \"samwise.gamgee@hobbiton.example\",\n" +
        "  \"use\": \"enc\",\n" +
        "  \"n\": \"wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRr\n" +
        "      I4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-Fy\n" +
        "      XJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnk\n" +
        "      Nrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeSt\n" +
        "      sqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M\n" +
        "      5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIU\n" +
        "      e7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBOD\n" +
        "      FskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb\n" +
        "      86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqB\n" +
        "      SAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhO\n" +
        "      OnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDa\n" +
        "      iCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnT\n" +
        "      yC0xhWBlsolZE\",\n" +
        "  \"e\": \"AQAB\",\n" +
        "  \"alg\": \"RSA-OAEP\",\n" +
        "  \"d\": \"n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bx\n" +
        "      cc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq\n" +
        "      -B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT\n" +
        "      2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9E\n" +
        "      A-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876\n" +
        "      DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIj\n" +
        "      h1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r\n" +
        "      -MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yD\n" +
        "      F-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1L\n" +
        "      oomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W\n" +
        "      _IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28\n" +
        "      S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c\n" +
        "      9WsWgRzI-K8gE\",\n" +
        "  \"p\": \"7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKgh\n" +
        "      vM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsY\n" +
        "      a_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3m\n" +
        "      Y46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-\n" +
        "      RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9s\n" +
        "      fbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgP\n" +
        "      gWCv5HoQ\",\n" +
        "  \"q\": \"zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6Zy\n" +
        "      KQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDc\n" +
        "      qssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYG\n" +
        "      RuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJ\n" +
        "      aPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EX\n" +
        "      e2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJ\n" +
        "      JlXXnH8Q\",\n" +
        "  \"dp\": \"19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xn\n" +
        "      x5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQ\n" +
        "      J_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72F\n" +
        "      ZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3i\n" +
        "      XjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGm\n" +
        "      pKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9Lc\n" +
        "      nwwT0jvoQ\",\n" +
        "  \"dq\": \"S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1\n" +
        "      VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM\n" +
        "      1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fg\n" +
        "      dyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrI\n" +
        "      ChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2\n" +
        "      AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iz\n" +
        "      nBNCeOUIQ\",\n" +
        "  \"qi\": \"FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCc\n" +
        "      iRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80\n" +
        "      oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMw\n" +
        "      QqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl\n" +
        "      27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4\n" +
        "      UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq\n" +
        "      8EzqZEKIA\"\n" +
        "}";

    @Test
    public void EC_Public_Key_3_1() throws JoseException
    {
        String jwkJson =
                "   {\n" +
                "     \"kty\": \"EC\",\n" +
                "     \"kid\": \"bilbo.baggins@hobbiton.example\",\n" +
                "     \"use\": \"sig\",\n" +
                "     \"crv\": \"P-521\",\n" +
                "     \"x\": \"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\",\n" +
                "     \"y\": \"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\"\n" +
                "   }";

        commonEcKey(jwkJson);
    }

    private EllipticCurveJsonWebKey commonEcKey(String jwkJson) throws JoseException
    {
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
        assertThat(jwk.getKeyId(), is(equalTo("bilbo.baggins@hobbiton.example")));
        assertThat(jwk.getUse(), is(equalTo(Use.SIGNATURE)));
        EllipticCurveJsonWebKey ecJwk = (EllipticCurveJsonWebKey) jwk;
        String curveName = ecJwk.getCurveName();
        assertThat(curveName, is(equalTo(EllipticCurves.P_521)));

        Key key = jwk.getKey();
        JsonWebKey jwkFromKey = JsonWebKey.Factory.newJwk(key);
        String jsonOutput = jwkFromKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
        // check the x and y in the output look the same (to ensure leading zero bytes are there, for example)
        assertThat(jsonOutput, containsString("\"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\""));
        assertThat(jsonOutput, containsString("\"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\""));
        // make sure the private key isn't there
        assertThat(jsonOutput, not(containsString("AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt")));
        return ecJwk;
    }

    @Test
    public void EC_Private_Key_3_2() throws JoseException
    {
        String jwkJson = "\n" +
                "   {\n" +
                "     \"kty\": \"EC\",\n" +
                "     \"kid\": \"bilbo.baggins@hobbiton.example\",\n" +
                "     \"use\": \"sig\",\n" +
                "     \"crv\": \"P-521\",\n" +
                "     \"x\": \"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\",\n" +
                "     \"y\": \"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\",\n" +
                "     \"d\": \"AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt\"\n" +
                "   }";
        EllipticCurveJsonWebKey jwk = commonEcKey(jwkJson);
        String jsonOutput = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
        // check the d in the output look the same (to ensure leading zero bytes are there, for example) and that it's there
        assertThat(jsonOutput, containsString("\"AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt\""));
    }

    @Test
    public void RSA_Public_Key_3_3() throws JoseException
    {
        String jwkJson =
                "   {\n" +
                "     \"kty\": \"RSA\",\n" +
                "     \"kid\": \"bilbo.baggins@hobbiton.example\",\n" +
                "     \"use\": \"sig\",\n" +
                "     \"n\": \"n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT\n" +
                "         -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV\n" +
                "         wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-\n" +
                "         oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde\n" +
                "         3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC\n" +
                "         LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g\n" +
                "         HdrNP5zw\",\n" +
                "     \"e\": \"AQAB\"\n" +
                "   }";
        commonRsaKey(jwkJson);

    }

    private RsaJsonWebKey commonRsaKey(String jwkJson) throws JoseException
    {
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
        assertThat(jwk.getKeyId(), is(equalTo("bilbo.baggins@hobbiton.example")));
        assertThat(jwk.getUse(), is(equalTo(Use.SIGNATURE)));

        // For a 2048-bit key, the field "n" value is 256 octets in length when decoded.
        Key key = jwk.getKey();
        JsonWebKey jwkFromKey = JsonWebKey.Factory.newJwk(key);
        String jsonOutput = jwkFromKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
        final Map<String,Object> params = JsonUtil.parseJson(jsonOutput);
        // check the public key parts in the output look the same just 'cause
        final String n = JsonHelp.getString(params, "n");
        final String expectedN = "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0d" +
                "Apr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzS" +
                "nbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw";
        assertThat(n, equalTo(expectedN));
        assertThat(JsonHelp.getString(params, "e"), equalTo("AQAB"));
        // make sure the private key parts aren't there
        assertThat(params.get("d"), is(nullValue()));
        assertThat(params.get("p"), is(nullValue()));
        assertThat(params.get("q"), is(nullValue()));
        assertThat(params.get("dp"), is(nullValue()));
        assertThat(params.get("dq"), is(nullValue()));
        assertThat(params.get("qi"), is(nullValue()));
        return (RsaJsonWebKey) jwk;
    }

    @Test
    public void RSA_Private_Key_3_4() throws JoseException
    {
        String jwkJson = "   {\n" +
                "     \"kty\": \"RSA\",\n" +
                "     \"kid\": \"bilbo.baggins@hobbiton.example\",\n" +
                "     \"use\": \"sig\",\n" +
                "     \"n\": \"n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT\n" +
                "         -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV\n" +
                "         wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-\n" +
                "         oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde\n" +
                "         3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC\n" +
                "         LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g\n" +
                "         HdrNP5zw\",\n" +
                "     \"e\": \"AQAB\",\n" +
                "     \"d\": \"bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e\n" +
                "         iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld\n" +
                "         Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b\n" +
                "         MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU\n" +
                "         6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj\n" +
                "         d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc\n" +
                "         OpBrQzwQ\",\n" +
                "     \"p\": \"3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR\n" +
                "         aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG\n" +
                "         peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8\n" +
                "         bUq0k\",\n" +
                "     \"q\": \"uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT\n" +
                "         8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an\n" +
                "         V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0\n" +
                "         s7pFc\",\n" +
                "     \"dp\": \"B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q\n" +
                "         1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn\n" +
                "         -RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX\n" +
                "         59ehik\",\n" +
                "     \"dq\": \"CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr\n" +
                "         AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK\n" +
                "         bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK\n" +
                "         T1cYF8\",\n" +
                "     \"qi\": \"3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N\n" +
                "         ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh\n" +
                "         jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP\n" +
                "         z8aaI4\"\n" +
                "   }";
        RsaJsonWebKey jwk = commonRsaKey(jwkJson);
        String jsonOutput = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
        Map<String, Object> params = JsonUtil.parseJson(jsonOutput);
        String expectedD = "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_" +
                "42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj" +
                "d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ";
        assertThat(JsonHelp.getString(params, "d"), is(equalTo(expectedD)));
        String expectedP = "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG" +
                "peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k";
        assertThat(JsonHelp.getString(params, "p"), is(equalTo(expectedP)));
        String expectedQ = "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY" +
                "898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc";
        assertThat(JsonHelp.getString(params, "q"), is(equalTo(expectedQ)));
        String expectedDP = "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn" +
                "-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik";
        assertThat(JsonHelp.getString(params, "dp"), is(equalTo(expectedDP)));
        String expectedDQ = "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK" +
                "bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8";
        assertThat(JsonHelp.getString(params, "dq"), is(equalTo(expectedDQ)));
        String expectedQI = "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh" +
                "jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4";
        assertThat(JsonHelp.getString(params, "qi"), is(equalTo(expectedQI)));
    }

    @Test
    public void Octet_Key_MAC_3_5() throws JoseException
    {
        String jwkJson =
                "   {\n" +
                "     \"kty\": \"oct\",\n" +
                "     \"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\",\n" +
                "     \"use\": \"sig\",\n" +
                "     \"alg\": \"HS256\",\n" +
                "     \"k\": \"hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg\"\n" +
                "   }";
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
        assertThat(jwk.getKeyId(), is(equalTo("018c0ae5-4d9b-471b-bfd6-eef314bc7037")));
        assertThat(jwk.getUse(), is(equalTo(Use.SIGNATURE)));
        assertThat(jwk.getAlgorithm(), is(equalTo(AlgorithmIdentifiers.HMAC_SHA256)));
        OctetSequenceJsonWebKey octJwk = (OctetSequenceJsonWebKey) jwk;
        byte[] octetSequence = octJwk.getOctetSequence();
        assertThat(octetSequence.length, is(equalTo(32)));

        jwk = JsonWebKey.Factory.newJwk(jwk.getKey());
        String jsonOutput = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
        assertThat(jsonOutput, containsString("\"hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg\""));
    }

    @Test
    public void Octet_Key_Enc_3_6() throws JoseException
    {
        String jwkJson =
                "   {\n" +
                "     \"kty\": \"oct\",\n" +
                "     \"kid\": \"1e571774-2e08-40da-8308-e8d68773842d\",\n" +
                "     \"use\": \"enc\",\n" +
                "     \"alg\": \"A256GCM\",\n" +
                "     \"k\": \"AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8\"\n" +
                "   }";
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
        assertThat(jwk.getKeyId(), is(equalTo("1e571774-2e08-40da-8308-e8d68773842d")));
        assertThat(jwk.getUse(), is(equalTo(Use.ENCRYPTION)));
        assertThat(jwk.getAlgorithm(), is(equalTo(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM)));
        OctetSequenceJsonWebKey octJwk = (OctetSequenceJsonWebKey) jwk;
        byte[] octetSequence = octJwk.getOctetSequence();
        assertThat(octetSequence.length, is(equalTo(32)));
        assertThat(octetSequence[0], is(equalTo((byte)0)));
        jwk = JsonWebKey.Factory.newJwk(jwk.getKey());
        String jsonOutput = jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
        assertThat(jsonOutput, containsString("\"AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8\""));
    }

    @Test
    public void rsa_v1_5Signature_4_1() throws JoseException
    {
        String jwsCompactSerialization =
                "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" +
                "hhbXBsZSJ9" +
                "." +
                "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" +
                "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" +
                "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" +
                "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4" +
                "." +
                "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK" +
                "ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J" +
                "IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w" +
                "W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP" +
                "xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f" +
                "cIe8u9ipH84ogoree7vjbU5y18kDquDg";

        String alg = AlgorithmIdentifiers.RSA_USING_SHA256;

        // verify consuming the JWS
        JsonWebSignature jws = new JsonWebSignature();
        jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, alg));
        jws.setCompactSerialization(jwsCompactSerialization);
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(figure3RsaJwkJsonString);
        jws.setKey(jwk.getKey());
        assertThat(jws.verifySignature(), is(true));
        assertThat(jws.getPayload(), equalTo(jwsPayload));
        assertThat(jws.getKeyIdHeaderValue(), equalTo(jwk.getKeyId()));
        assertThat(alg, equalTo(jws.getAlgorithmHeaderValue()));

        // verify reproducing it (it's just luck that using the setters for the headers results in the exact same
        // JSON representation of the header)
        jws = new JsonWebSignature();
        jws.setPayload(jwsPayload);
        jws.setAlgorithmHeaderValue(alg);
        jws.setKeyIdHeaderValue(jwk.getKeyId());
        PublicJsonWebKey rsaJwk = (PublicJsonWebKey) jwk;
        jws.setKey(rsaJwk.getPrivateKey());
        String compactSerialization = jws.getCompactSerialization();
        assertThat(jwsCompactSerialization, equalTo(compactSerialization));
    }

    @Test
    public void rsaPssSignature_4_2() throws Exception
    {
        final String rsaPssUsingSha384 = AlgorithmIdentifiers.RSA_PSS_USING_SHA384;
        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setSignatureAlgsNeeded(rsaPssUsingSha384);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws JoseException
            {
                PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(figure3RsaJwkJsonString);

                String cs =
                        "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" +
                        "hhbXBsZSJ9" +
                        "." +
                        "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" +
                        "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" +
                        "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" +
                        "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4" +
                        "." +
                        "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I" +
                        "pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU" +
                        "vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX" +
                        "e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT" +
                        "0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a" +
                        "6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw";

                JsonWebSignature jws = new JsonWebSignature();
                jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_PSS_USING_SHA384));
                jws.setCompactSerialization(cs);
                jws.setKey(jwk.getPublicKey());
                assertThat(jws.verifySignature(), is(true));
                assertThat(jws.getPayload(), equalTo(jwsPayload));
                assertThat(jws.getKeyIdHeaderValue(), equalTo(jwk.getKeyId()));
                assertThat(rsaPssUsingSha384, equalTo(jws.getAlgorithmHeaderValue()));

                // can't easily verify reproducing RSA-PSS because "it is probabilistic rather than deterministic,
                // incorporating a randomly generated salt value" - from http://tools.ietf.org/html/rfc3447#section-8.1
            }
        });

    }

    @Test
    public void ecdsaSignature_4_3() throws JoseException
    {
        String jwkJson = 
                "{\n" +
                "  \"kty\": \"EC\",\n" +
                "  \"kid\": \"bilbo.baggins@hobbiton.example\",\n" +
                "  \"use\": \"sig\",\n" +
                "  \"crv\": \"P-521\",\n" +
                "  \"x\": \"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9\n" +
                "      A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\",\n" +
                "  \"y\": \"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy\n" +
                "      SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\",\n" +
                "  \"d\": \"AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb\n" +
                "      KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt\"\n" +
                "}";


        String jwsCompactSerialization =
                "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" +
                "hhbXBsZSJ9" +
                "." +
                "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" +
                "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" +
                "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" +
                "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4" +
                "." +
                "AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvb" +
                "u9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kv" +
                "AD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2";

        String alg = AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512;

        // verify consuming the JWS
        JsonWebSignature jws = new JsonWebSignature();
        jws.setCompactSerialization(jwsCompactSerialization);
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);

        jws.setKey(jwk.getKey());
        jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, alg));
        assertThat(jws.getUnverifiedPayload(), equalTo(jwsPayload));

        assertThat(jws.verifySignature(), is(true));
        assertThat(jws.getPayload(), equalTo(jwsPayload));

        assertThat(jws.getKeyIdHeaderValue(), equalTo(jwk.getKeyId()));
        assertThat(alg, equalTo(jws.getAlgorithmHeaderValue()));

        // can't really verify reproducing ECDSA
    }

    @Test
    public void hmacSha2IntegrityProtection_4_4() throws JoseException
    {
        String jwkJson =
               "   {\n" +
               "     \"kty\": \"oct\",\n" +
               "     \"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\",\n" +
               "     \"use\": \"sig\",\n" +
               "     \"k\": \"hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg\"\n" +
               "   }";

        String jwsCompactSerialization =
                "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" +
                "VlZjMxNGJjNzAzNyJ9" +
                "." +
                "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" +
                "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" +
                "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" +
                "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4" +
                "." +
                "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0";

        String alg = AlgorithmIdentifiers.HMAC_SHA256;

        // verify consuming the JWS
        JsonWebSignature jws = new JsonWebSignature();
        jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, alg));
        jws.setCompactSerialization(jwsCompactSerialization);
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
        jws.setKey(jwk.getKey());
        assertThat(jws.verifySignature(), is(true));
        assertThat(jws.getPayload(), equalTo(jwsPayload));
        assertThat(jws.getKeyIdHeaderValue(), equalTo(jwk.getKeyId()));
        assertThat(alg, equalTo(jws.getAlgorithmHeaderValue()));

        // verify reproducing it
        jws = new JsonWebSignature();
        jws.setPayload(jwsPayload);
        jws.setAlgorithmHeaderValue(alg);
        jws.setKeyIdHeaderValue(jwk.getKeyId());
        jws.setKey(jwk.getKey());
        String compactSerialization = jws.getCompactSerialization();
        assertThat(jwsCompactSerialization, equalTo(compactSerialization));
    }

    @Test
    public void detached_4_5() throws JoseException
    {
        String jwkJsonString =
                "   {\n" +
                "     \"kty\": \"oct\",\n" +
                "     \"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\",\n" +
                "     \"use\": \"sig\",\n" +
                "     \"k\": \"hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg\"\n" +
                "   }";

        String detachedCs = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" +
                "VlZjMxNGJjNzAzNyJ9" +
                "." +
                "." +
                "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0";

        JsonWebSignature jws = new JsonWebSignature();
        jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, AlgorithmIdentifiers.HMAC_SHA256));
        jws.setCompactSerialization(detachedCs);
        JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJsonString);
        jws.setKey(jwk.getKey());
        jws.setEncodedPayload(encodedJwsPayload);
        assertThat(jws.verifySignature(), is(true));
        assertThat(jws.getPayload(), equalTo(jwsPayload));

        // verify reproducing it (it's just mostly luck that using the setters for the headers results in the exact same
        // JSON representation of the header)
        jws = new JsonWebSignature();
        jws.setPayload(jwsPayload);
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
        jws.setKeyIdHeaderValue(jwk.getKeyId());
        jws.setKey(jwk.getKey());
        // To create a detached signature, sign and then concatenate the encoded header, two dots "..", and the encoded signature
        jws.sign();
        String encodedHeader = jws.getHeaders().getEncodedHeader();
        String encodedSignature = jws.getEncodedSignature();
        String reproducedDetachedCs = encodedHeader + ".." + encodedSignature;
        assertThat(detachedCs, is(equalTo(reproducedDetachedCs)));
        assertThat(encodedJwsPayload, is(equalTo(jws.getEncodedPayload())));

        // now (expected 0.5.0) also have a method on jws for getting the compact serialization without the payload
        // check that it produces the expected result both on that same jws and on a new object
        String detachedContentCompactSerialization = jws.getDetachedContentCompactSerialization();
        assertThat(detachedCs, is(equalTo(detachedContentCompactSerialization)));
        assertThat(jws.getPayload(), equalTo(jwsPayload));
        assertThat(jws.getEncodedPayload(), equalTo(encodedJwsPayload));

        jws = new JsonWebSignature();
        jws.setPayload(jwsPayload);
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
        jws.setKeyIdHeaderValue(jwk.getKeyId());
        jws.setKey(jwk.getKey());
        detachedContentCompactSerialization = jws.getDetachedContentCompactSerialization();
        assertThat(detachedCs, is(equalTo(detachedContentCompactSerialization)));
        assertThat(jws.getPayload(), equalTo(jwsPayload));
        assertThat(jws.getEncodedPayload(), equalTo(encodedJwsPayload));
    }

    @Test
    public void encryptionRSAv1_5andAES_HMAC_SHA2_5_1() throws JoseException
    {
        String jwkJsonString =
                "{\n" +
                "  \"kty\": \"RSA\",\n" +
                "  \"kid\": \"frodo.baggins@hobbiton.example\",\n" +
                "  \"use\": \"enc\",\n" +
                "  \"n\": \"maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegT\n" +
                "      HVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx\n" +
                "      6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5U\n" +
                "      NwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4c\n" +
                "      R5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oy\n" +
                "      pBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYA\n" +
                "      VotGlvMQ\",\n" +
                "  \"e\": \"AQAB\",\n" +
                "  \"d\": \"Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wy\n" +
                "      bQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO\n" +
                "      5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6\n" +
                "      Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP\n" +
                "      1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PN\n" +
                "      miuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2v\n" +
                "      pzj85bQQ\",\n" +
                "  \"p\": \"2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaE\n" +
                "      oekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH\n" +
                "      7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ\n" +
                "      2VFmU\",\n" +
                "  \"q\": \"te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_V\n" +
                "      F099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb\n" +
                "      9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8\n" +
                "      d6Et0\",\n" +
                "  \"dp\": \"UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTH\n" +
                "      QmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JV\n" +
                "      RDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsf\n" +
                "      lo0rYU\",\n" +
                "  \"dq\": \"iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9Mb\n" +
                "      pFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87A\n" +
                "      CfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14\n" +
                "      TkXlHE\",\n" +
                "  \"qi\": \"kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZ\n" +
                "      lXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7\n" +
                "      Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx\n" +
                "      2bQ_mM\"\n" +
                "}";

        String jweCompactSerialization =
                "eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm" +
                "V4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" +
                "." +
                "laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF" +
                "vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G" +
                "Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG" +
                "TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl" +
                "zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh" +
                "MBs9M8XL223Fg47xlGsMXdfuY-4jaqVw" +
                "." +
                "bbd5sTkYwhAIqfHsx8DayA" +
                "." +
                "0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_r" +
                "aa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8O" +
                "WzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZV" +
                "yeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0" +
                "zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2" +
                "O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VW" +
                "i7lzA6BP430m" +
                "." +
                "kvKuFBXHe5mQr4lqgobAUg";

        PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(jwkJsonString);

        // verify that we can decrypt it
        JsonWebEncryption jwe = new JsonWebEncryption();
        jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.RSA1_5));
        jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));
        jwe.setCompactSerialization(jweCompactSerialization);
        jwe.setKey(jwk.getPrivateKey());
        assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));

        // verify that we can reproduce it (most of it) from the inputs
        jwe = new JsonWebEncryption();
        jwe.setPlaintext(jwePlaintext);
        jwe.setKey(jwk.getPublicKey());
        jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA1_5);
        jwe.setKeyIdHeaderValue(jwk.getKeyId());
        jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);

        // set the IV and cek per the example (you wouldn't usually do this but it makes the output more deterministic)
        jwe.setEncodedIv("bbd5sTkYwhAIqfHsx8DayA");
        jwe.setEncodedContentEncryptionKey("3qyTVhIWt5juqZUCpfRqpvauwB956MEJL2Rt-8qXKSo");

        // check that the header, iv, ciphertext and tag all match
        String[] deserializedExample = CompactSerializer.deserialize(jweCompactSerialization);
        String[] deserializedResults = CompactSerializer.deserialize(jwe.getCompactSerialization());
        assertThat(deserializedExample[0], equalTo(deserializedResults[0]));
        // RSA v1.5, due to random bytes being used to pad, is nondeterministic so the encrypted key
        // will be different each time in the JWE we're producing and so can't compare to the example
        assertThat(deserializedExample[2], equalTo(deserializedResults[2]));
        assertThat(deserializedExample[3], equalTo(deserializedResults[3]));
        assertThat(deserializedExample[4], equalTo(deserializedResults[4]));
    }

    @Test
    public void encryptionRSA_OAEPandAesGcm_5_2() throws Exception
    {
        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setEncryptionAlgsNeeded(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws Exception
            {
                String jweCompactSerialization =
                        "eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG" +
                        "9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0" +
                        "." +
                        "rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi" +
                        "beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu" +
                        "cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58" +
                        "-Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx" +
                        "KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK" +
                        "IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7" +
                        "pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ" +
                        "fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3" +
                        "8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU" +
                        "06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5" +
                        "Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR" +
                        "s" +
                        "." +
                        "-nBoKLH0YkLZPSI9" +
                        "." +
                        "o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgR" +
                        "L-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEw" +
                        "P7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8" +
                        "iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML" +
                        "7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSV" +
                        "maPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw" +
                        "." +
                        "UCGiqJxhBI3IFVdPalHHvA";

                PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(figure62RsaJwkJsonString);

                // verify that we can decrypt the encrypted key
                JsonWebEncryption jwe = new JsonWebEncryption();
                jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.RSA_OAEP));
                jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_256_GCM));
                jwe.setCompactSerialization(jweCompactSerialization);
                jwe.setKey(jwk.getPrivateKey());

                KeyManagementAlgorithm keyManagementModeAlg = jwe.getKeyManagementModeAlgorithm();

                ContentEncryptionKeyDescriptor cekDesc = new ContentEncryptionKeyDescriptor(32, AesKey.ALGORITHM);

                Key cek = keyManagementModeAlg.manageForDecrypt(jwe.getKey(), jwe.getEncryptedKey(), cekDesc, jwe.getHeaders(), ProviderContextTest.EMPTY_CONTEXT);

                String encodedExampleCek = "mYMfsggkTAm0TbvtlFh2hyoXnbEzJQjMxmgLN3d8xXA";
                assertArrayEquals(cek.getEncoded(), Base64Url.decode(encodedExampleCek));

                // and that we can decrypt the whole thing
                assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));


                // verify that we can reproduce it (most/some of it) from the inputs
                jwe = new JsonWebEncryption();
                jwe.setPlaintext(jwePlaintext);
                jwe.setKey(jwk.getPublicKey());
                jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.RSA_OAEP);
                jwe.setKeyIdHeaderValue(jwk.getKeyId());
                jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);

                // set the IV and cek per the example (you wouldn't usually do this but it makes the output more deterministic)
                jwe.setEncodedIv("-nBoKLH0YkLZPSI9");
                jwe.setEncodedContentEncryptionKey(encodedExampleCek);

                // check that the header, iv, ciphertext and tag all match
                String[] deserializedExample = CompactSerializer.deserialize(jweCompactSerialization);
                String[] deserializedResults = CompactSerializer.deserialize(jwe.getCompactSerialization());
                assertThat(deserializedExample[0], equalTo(deserializedResults[0]));
                // Some of the algorithms, RSAES OAEP being one of them, are probabilistic encryption schemes which incorporate
                // some element of randomness to yield a different output even when encrypting the same content multiple times.
                // so the encrypted key will be different each time in the JWE we're producing and so can't compare to the example
                // http://www.ietf.org/mail-archive/web/jose/current/msg04110.html
                assertThat(deserializedExample[2], equalTo(deserializedResults[2]));
                assertThat(deserializedExample[3], equalTo(deserializedResults[3]));
                assertThat(deserializedExample[4], equalTo(deserializedResults[4]));
            }
        });
    }

    @Test
    public void encryptionPbes_5_3() throws JoseException
    {
        String password = "entrap_o–peter_long–credit_tun";

        String exampleCompactSerialization =
                "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3" +
                "hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl" +
                "bmMiOiJBMTI4Q0JDLUhTMjU2In0" +
                "." +
                "d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g" +
                "." +
                "VBiCzVHNoLiR3F4V82uoTQ" +
                "." +
                "23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR" +
                "sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l" +
                "TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb" +
                "6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL" +
                "_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd" +
                "PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok" +
                "AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-" +
                "zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V" +
                "3kobXZ77ulMwDs4p" +
                "." +
                "0HlwodAhOCILG5SQ2LQ9dg";
        String plaintext =
                "{\"keys\":[" +
                        "{\"kty\":\"oct\",\"kid\":\"77c7e2b8-6e13-45cf-8672-617b5b45243a\",\"use\":\"enc\",\"alg\":\"A128GCM\",\"k\":\"XctOhJAkA-pD9Lh7ZgW_2A\"}," +
                        "{\"kty\":\"oct\",\"kid\":\"81b20965-8332-43d9-a468-82160ad91ac8\",\"use\":\"enc\",\"alg\":\"A128KW\",\"k\":\"GZy6sIZ6wl9NJOKB-jnmVQ\"}," +
                        "{\"kty\":\"oct\",\"kid\":\"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d\",\"use\":\"enc\",\"alg\":\"A256GCMKW\",\"k\":\"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8\"}]}";

        // verify that we can decrypt it
        JsonWebEncryption jwe = new JsonWebEncryption();
        jwe.setCompactSerialization(exampleCompactSerialization);
        jwe.setKey(new PbkdfKey(password));
        jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.PBES2_HS512_A256KW));
        jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));
        assertThat(plaintext, equalTo(jwe.getPlaintextString()));

        // verify that we can reproduce it from the inputs
        jwe = new JsonWebEncryption();
        jwe.setPlaintext(plaintext);
        jwe.setKey(new PbkdfKey(password));

        jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.PBES2_HS512_A256KW);
        Headers headers = jwe.getHeaders();
        headers.setStringHeaderValue(HeaderParameterNames.PBES2_SALT_INPUT, "8Q1SzinasR3xchYz6ZZcHA");
        headers.setObjectHeaderValue(HeaderParameterNames.PBES2_ITERATION_COUNT, 8192L);
        headers.setStringHeaderValue("cty", "jwk-set+json");
        jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);

        // set the IV and cek per the example (you wouldn't usually do this but it makes the output
        // more deterministic so it can be compared to the example)
        jwe.setEncodedContentEncryptionKey("uwsjJXaBK407Qaf0_zpcpmr1Cs0CC50hIUEyGNEt3m0");
        jwe.setEncodedIv("VBiCzVHNoLiR3F4V82uoTQ");

        assertThat(jwe.getCompactSerialization(), equalTo(exampleCompactSerialization));
    }

    @Test
    public void encryptionPbes_old_was_4_3_from_draft_2() throws JoseException
    {
        // draft -02 used, I think by accident, PBES2-HS256+A128KW, which was changed
        // to PBES2-HS512+A256KW in -03 but the content from -02 is still a useful example so keeping it here
        String password = "entrap_o_peter_long_credit_tun";

        String exampleCompactSerialization =
                "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiI4UTFTemluYXNSM3" +
                "hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl" +
                "bmMiOiJBMTI4Q0JDLUhTMjU2In0" +
                "." +
                "YKbKLsEoyw_JoNvhtuHo9aaeRNSEhhAW2OVHcuF_HLqS0n6hA_fgCA" +
                "." +
                "VBiCzVHNoLiR3F4V82uoTQ" +
                "." +
                "23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR" +
                "sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l" +
                "TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb" +
                "6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL" +
                "_SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd" +
                "PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok" +
                "AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-" +
                "zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V" +
                "3kobXZ77ulMwDs4p" +
                "." +
                "ALTKwxvAefeL-32NY7eTAQ";

        String plaintext =
                "{\"keys\":[" +
                 "{\"kty\":\"oct\",\"kid\":\"77c7e2b8-6e13-45cf-8672-617b5b45243a\",\"use\":\"enc\",\"alg\":\"A128GCM\",\"k\":\"XctOhJAkA-pD9Lh7ZgW_2A\"}," +
                 "{\"kty\":\"oct\",\"kid\":\"81b20965-8332-43d9-a468-82160ad91ac8\",\"use\":\"enc\",\"alg\":\"A128KW\",\"k\":\"GZy6sIZ6wl9NJOKB-jnmVQ\"}," +
                 "{\"kty\":\"oct\",\"kid\":\"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d\",\"use\":\"enc\",\"alg\":\"A256GCMKW\",\"k\":\"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8\"}]}";

        // verify that we can decrypt it
        JsonWebEncryption jwe = new JsonWebEncryption();
        jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.PBES2_HS256_A128KW));
        jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));
        jwe.setCompactSerialization(exampleCompactSerialization);
        jwe.setKey(new PbkdfKey(password));
        assertThat(plaintext, equalTo(jwe.getPlaintextString()));

        // verify that we can reproduce it from the inputs
        jwe = new JsonWebEncryption();
        jwe.setPlaintext(plaintext);
        jwe.setKey(new PbkdfKey(password));

        jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.PBES2_HS256_A128KW);
        Headers headers = jwe.getHeaders();
        headers.setStringHeaderValue(HeaderParameterNames.PBES2_SALT_INPUT, "8Q1SzinasR3xchYz6ZZcHA");
        headers.setObjectHeaderValue(HeaderParameterNames.PBES2_ITERATION_COUNT, 8192L);
        headers.setStringHeaderValue("cty", "jwk-set+json");
        jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);

        // set the IV and cek per the example (you wouldn't usually do this but it makes the output
        // more deterministic so it can be compared to the example)
        jwe.setEncodedContentEncryptionKey("uwsjJXaBK407Qaf0_zpcpmr1Cs0CC50hIUEyGNEt3m0");
        jwe.setEncodedIv("VBiCzVHNoLiR3F4V82uoTQ");

        assertThat(jwe.getCompactSerialization(), equalTo(exampleCompactSerialization));
    }

    @Test
    public void keyAgreementWithKeyWrapAndGcm5_4() throws Exception
    {
        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setEncryptionAlgsNeeded(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws Exception
            {
                String jwkJsonString =
                        "{\n" +
                        "  \"kty\": \"EC\",\n" +
                        "  \"kid\": \"peregrin.took@tuckborough.example\",\n" +
                        "  \"use\": \"enc\",\n" +
                        "  \"crv\": \"P-384\",\n" +
                        "  \"x\": \"YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQL\n" +
                        "      pe2FpxBmu2\",\n" +
                        "  \"y\": \"A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-\n" +
                        "      SkgaFL1ETP\",\n" +
                        "  \"d\": \"iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0Idn\n" +
                        "      YK2xDlZh-j\"\n" +
                        "}";


                String jweCompactSerialization =
                        "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH" +
                        "Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt" +
                        "Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH" +
                        "hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy" +
                        "ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT" +
                        "h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0" +
                        "." +
                        "0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2" +
                        "." +
                        "mH-G2zVqgztUtnW_" +
                        "." +
                        "tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cP" +
                        "WJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0" +
                        "IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkc" +
                        "Y9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w0" +
                        "3XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu" +
                        "07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ" +
                        "." +
                        "WuGzxmcreYjpHGJoa17EBg";

                final PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(jwkJsonString);

                JsonWebEncryption jwe = new JsonWebEncryption();
                jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.ECDH_ES_A128KW));
                jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_GCM));

                jwe.setCompactSerialization(jweCompactSerialization);
                jwe.setKey(jwk.getPrivateKey());
                assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));
                assertThat(KeyManagementAlgorithmIdentifiers.ECDH_ES_A128KW, equalTo(jwe.getAlgorithmHeaderValue()));
                assertThat(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM, equalTo(jwe.getEncryptionMethodHeaderParameter()));
            }
        });
    }

    @Test
    public void keyAgreementAndAesCbc_5_5() throws JoseException
    {
        String jwkJson =
                "{" +
                "  \"kty\": \"EC\"," +
                "  \"kid\": \"meriadoc.brandybuck@buckland.example\"," +
                "  \"use\": \"enc\"," +
                "  \"crv\": \"P-256\"," +
                "  \"x\": \"Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0\"," +
                "  \"y\": \"HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw\"," +
                "  \"d\": \"r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8\"" +
                "}";

        String exampleCompactSerialization =
                "eyJhbGciOiJFQ0RILUVTIiwia2lkIjoibWVyaWFkb2MuYnJhbmR5YnVja0BidW" +
                "NrbGFuZC5leGFtcGxlIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYi" +
                "LCJ4IjoibVBVS1RfYkFXR0hJaGcwVHBqanFWc1AxclhXUXVfdndWT0hIdE5rZF" +
                "lvQSIsInkiOiI4QlFBc0ltR2VBUzQ2ZnlXdzVNaFlmR1RUMElqQnBGdzJTUzM0" +
                "RHY0SXJzIn0sImVuYyI6IkExMjhDQkMtSFMyNTYifQ" +
                "." +
                "." +
                "yc9N8v5sYyv3iGQT926IUg" +
                "." +
                "BoDlwPnTypYq-ivjmQvAYJLb5Q6l-F3LIgQomlz87yW4OPKbWE1zSTEFjDfhU9" +
                "IPIOSA9Bml4m7iDFwA-1ZXvHteLDtw4R1XRGMEsDIqAYtskTTmzmzNa-_q4F_e" +
                "vAPUmwlO-ZG45Mnq4uhM1fm_D9rBtWolqZSF3xGNNkpOMQKF1Cl8i8wjzRli7-" +
                "IXgyirlKQsbhhqRzkv8IcY6aHl24j03C-AR2le1r7URUhArM79BY8soZU0lzwI" +
                "-sD5PZ3l4NDCCei9XkoIAfsXJWmySPoeRb2Ni5UZL4mYpvKDiwmyzGd65KqVw7" +
                "MsFfI_K767G9C9Azp73gKZD0DyUn1mn0WW5LmyX_yJ-3AROq8p1WZBfG-ZyJ61" +
                "95_JGG2m9Csg" +
                "." +
                "WCCkNa-x4BeB9hIDIfFuhg";

        PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(jwkJson);

        // verify that we can decrypt it
        JsonWebEncryption jwe = new JsonWebEncryption();
        jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.ECDH_ES));
        jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));
        jwe.setCompactSerialization(exampleCompactSerialization);
        jwe.setKey(jwk.getPrivateKey());
        assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));
    }

    @Test
    public void directEncryptionUsingAESGCM_5_6() throws Exception
    {
        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setEncryptionAlgsNeeded(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws Exception
            {
                JsonWebKey jwk = JsonWebKey.Factory.newJwk(
                        "   {\n" +
                        "     \"kty\": \"oct\",\n" +
                        "     \"kid\": \"77c7e2b8-6e13-45cf-8672-617b5b45243a\",\n" +
                        "     \"use\": \"enc\",\n" +
                        "     \"alg\": \"A128GCM\",\n" +
                        "     \"k\": \"XctOhJAkA-pD9Lh7ZgW_2A\"\n" +
                        "   }");

                // {"alg":"dir","kid":"77c7e2b8-6e13-45cf-8672-617b5b45243a","enc":"A128GCM"}
                String cs =
                        "eyJhbGciOiJkaXIiLCJraWQiOiI3N2M3ZTJiOC02ZTEzLTQ1Y2YtODY3Mi02MT" +
                        "diNWI0NTI0M2EiLCJlbmMiOiJBMTI4R0NNIn0" +
                        "." +
                        "." +
                        "refa467QzzKx6QAB" +
                        "." +
                        "JW_i_f52hww_ELQPGaYyeAB6HYGcR559l9TYnSovc23XJoBcW29rHP8yZOZG7Y" +
                        "hLpT1bjFuvZPjQS-m0IFtVcXkZXdH_lr_FrdYt9HRUYkshtrMmIUAyGmUnd9zM" +
                        "DB2n0cRDIHAzFVeJUDxkUwVAE7_YGRPdcqMyiBoCO-FBdE-Nceb4h3-FtBP-c_" +
                        "BIwCPTjb9o0SbdcdREEMJMyZBH8ySWMVi1gPD9yxi-aQpGbSv_F9N4IZAxscj5" +
                        "g-NJsUPbjk29-s7LJAGb15wEBtXphVCgyy53CoIKLHHeJHXex45Uz9aKZSRSIn" +
                        "ZI-wjsY0yu3cT4_aQ3i1o-tiE-F8Ios61EKgyIQ4CWao8PFMj8TTnp" +
                        "." +
                        "vbb32Xvllea2OtmHAdccRQ";

                // decrypt the example
                JsonWebEncryption jwe = new JsonWebEncryption();
                jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.DIRECT));
                jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_GCM));
                jwe.setKey(jwk.getKey());
                jwe.setCompactSerialization(cs);
                assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));


                // verify that we can reproduce it
                jwe = new JsonWebEncryption();
                jwe.setPlaintext(jwePlaintext);
                jwe.setKey(jwk.getKey());
                jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.DIRECT);
                jwe.setKeyIdHeaderValue(jwk.getKeyId());
                jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM);

            }
        });
    }

    @Test
    public void gcmKeyWrapWithAesCbcContentEncryption_5_7() throws Exception
    {
        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setKeyManagementAlgsNeeded(KeyManagementAlgorithmIdentifiers.A256GCMKW);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws Exception
            {
                JsonWebKey jwk = JsonWebKey.Factory.newJwk(
                        "   {\n" +
                        "     \"kty\": \"oct\",\n" +
                        "     \"kid\": \"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d\",\n" +
                        "     \"use\": \"enc\",\n" +
                        "     \"alg\": \"A256GCMKW\",\n" +
                        "     \"k\": \"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8\"\n" +
                        "   }");

                // {"alg":"dir","kid":"77c7e2b8-6e13-45cf-8672-617b5b45243a","enc":"A128GCM"}
                String cs =
                        "eyJhbGciOiJBMjU2R0NNS1ciLCJraWQiOiIxOGVjMDhlMS1iZmE5LTRkOTUtYj" +
                        "IwNS0yYjRkZDFkNDMyMWQiLCJ0YWciOiJrZlBkdVZRM1QzSDZ2bmV3dC0ta3N3" +
                        "IiwiaXYiOiJLa1lUMEdYXzJqSGxmcU5fIiwiZW5jIjoiQTEyOENCQy1IUzI1Ni" +
                        "J9" +
                        "." +
                        "lJf3HbOApxMEBkCMOoTnnABxs_CvTWUmZQ2ElLvYNok" +
                        "." +
                        "gz6NjyEFNm_vm8Gj6FwoFQ" +
                        "." +
                        "Jf5p9-ZhJlJy_IQ_byKFmI0Ro7w7G1QiaZpI8OaiVgD8EqoDZHyFKFBupS8iaE" +
                        "eVIgMqWmsuJKuoVgzR3YfzoMd3GxEm3VxNhzWyWtZKX0gxKdy6HgLvqoGNbZCz" +
                        "LjqcpDiF8q2_62EVAbr2uSc2oaxFmFuIQHLcqAHxy51449xkjZ7ewzZaGV3eFq" +
                        "hpco8o4DijXaG5_7kp3h2cajRfDgymuxUbWgLqaeNQaJtvJmSMFuEOSAzw9Hde" +
                        "b6yhdTynCRmu-kqtO5Dec4lT2OMZKpnxc_F1_4yDJFcqb5CiDSmA-psB2k0Jtj" +
                        "xAj4UPI61oONK7zzFIu4gBfjJCndsZfdvG7h8wGjV98QhrKEnR7xKZ3KCr0_qR" +
                        "1B-gxpNk3xWU" +
                        "." +
                        "DKW7jrb4WaRSNfbXVPlT5g";


                // verify recreating it (or what we can)
                JsonWebEncryption jwe = new JsonWebEncryption();
                jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A256GCMKW);
                jwe.setKeyIdHeaderValue(jwk.getKeyId());
                jwe.setHeader(HeaderParameterNames.INITIALIZATION_VECTOR, "KkYT0GX_2jHlfqN_");
                jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);

                jwe.setKey(jwk.getKey());
                jwe.setEncodedIv("gz6NjyEFNm_vm8Gj6FwoFQ");
                jwe.setEncodedContentEncryptionKey("UWxARpat23nL9ReIj4WG3D1ee9I4r-Mv5QLuFXdy_rE");
                jwe.setPayload(jwePlaintext);


                String compactSerialization = jwe.getCompactSerialization();
                String[] exampleParts = CompactSerializer.deserialize(cs);
                String[] producedParts = CompactSerializer.deserialize(compactSerialization);

                // [0] header is different because of the order of params
                assertThat(exampleParts[1], equalTo(producedParts[1]));
                assertThat(exampleParts[2], equalTo(producedParts[2]));
                assertThat(exampleParts[3], equalTo(producedParts[3]));
                // [4] tag is different because of the header being different


                // verify decrypting it
                jwe = new JsonWebEncryption();
                jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.A256GCMKW));
                jwe.setContentEncryptionAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256));

                jwe.setKey(jwk.getKey());
                jwe.setCompactSerialization(cs);
                assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));
            }
        });
    }

    @Test
    public void aesKeyWrapAESGCM_5_8() throws Exception
    {
        final String cs =
                "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" +
                "04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIn0" +
                "." +
                "CBI6oDw8MydIx1IBntf_lQcw2MmJKIQx" +
                "." +
                "Qx0pmsDa8KnJc9Jo" +
                "." +
                "AwliP-KmWgsZ37BvzCefNen6VTbRK3QMA4TkvRkH0tP1bTdhtFJgJxeVmJkLD6" +
                "1A1hnWGetdg11c9ADsnWgL56NyxwSYjU1ZEHcGkd3EkU0vjHi9gTlb90qSYFfe" +
                "F0LwkcTtjbYKCsiNJQkcIp1yeM03OmuiYSoYJVSpf7ej6zaYcMv3WwdxDFl8RE" +
                "wOhNImk2Xld2JXq6BR53TSFkyT7PwVLuq-1GwtGHlQeg7gDT6xW0JqHDPn_H-p" +
                "uQsmthc9Zg0ojmJfqqFvETUxLAF-KjcBTS5dNy6egwkYtOt8EIHK-oEsKYtZRa" +
                "a8Z7MOZ7UGxGIMvEmxrGCPeJa14slv2-gaqK0kEThkaSqdYw0FkQZF" +
                "." +
                "ER7MWJZ1FBI_NKvn7Zb1Lw";

        common_5_8_and_5_9(cs);
    }

    private void common_5_8_and_5_9(final String cs) throws Exception
    {
        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setEncryptionAlgsNeeded(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws Exception
            {
                JsonWebKey jwk = JsonWebKey.Factory.newJwk(
                        "   {\n" +
                        "     \"kty\": \"oct\",\n" +
                        "     \"kid\": \"81b20965-8332-43d9-a468-82160ad91ac8\",\n" +
                        "     \"use\": \"enc\",\n" +
                        "     \"alg\": \"A128KW\",\n" +
                        "     \"k\": \"GZy6sIZ6wl9NJOKB-jnmVQ\"\n" +
                        "   }\n");

                JsonWebEncryption jwe = new JsonWebEncryption();
                jwe.setAlgorithmConstraints(new AlgorithmConstraints(WHITELIST, KeyManagementAlgorithmIdentifiers.A128KW));
                jwe.setKey(jwk.getKey());
                jwe.setCompactSerialization(cs);
                assertThat(jwePlaintext, equalTo(jwe.getPlaintextString()));
            }
        });
    }

    @Test
    public void aesKeyWrapAESGCMWithCompressedContent_5_9() throws Exception
    {
        String cs =
                "eyJhbGciOiJBMTI4S1ciLCJraWQiOiI4MWIyMDk2NS04MzMyLTQzZDktYTQ2OC" +
                "04MjE2MGFkOTFhYzgiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0" +
                "." +
                "5vUT2WOtQxKWcekM_IzVQwkGgzlFDwPi" +
                "." +
                "p9pUq6XHY0jfEZIl" +
                "." +
                "HbDtOsdai1oYziSx25KEeTxmwnh8L8jKMFNc1k3zmMI6VB8hry57tDZ61jXyez" +
                "SPt0fdLVfe6Jf5y5-JaCap_JQBcb5opbmT60uWGml8blyiMQmOn9J--XhhlYg0" +
                "m-BHaqfDO5iTOWxPxFMUedx7WCy8mxgDHj0aBMG6152PsM-w5E_o2B3jDbrYBK" +
                "hpYA7qi3AyijnCJ7BP9rr3U8kxExCpG3mK420TjOw" +
                "." +
                "VILuUwuIxaLVmh5X-T7kmA";
        common_5_8_and_5_9(cs);
    }

    @Test
    public void nestingSignaturesAndEncryption() throws Exception
    {
        final String expectedPayload = "{\"iss\":\"hobbiton.example\",\"exp\":1300819380,\"http://example.com/is_root\":true}";

        final String sigJwkJson =
                "{\n" +
                "  \"kty\": \"RSA\",\n" +
                "  \"kid\": \"hobbiton.example\",\n" +
                "  \"use\": \"sig\",\n" +
                "  \"n\": \"kNrPIBDXMU6fcyv5i-QHQAQ-K8gsC3HJb7FYhYaw8hXbNJa-t8q0lD\n" +
                "       KwLZgQXYV-ffWxXJv5GGrlZE4GU52lfMEegTDzYTrRQ3tepgKFjMGg6I\n" +
                "       y6fkl1ZNsx2gEonsnlShfzA9GJwRTmtKPbk1s-hwx1IU5AT-AIelNqBg\n" +
                "      cF2vE5W25_SGGBoaROVdUYxqETDggM1z5cKV4ZjDZ8-lh4oVB07bkac6\n" +
                "      LQdHpJUUySH_Er20DXx30Kyi97PciXKTS-QKXnmm8ivyRCmux22ZoPUi\n" +
                "      nd2BKC5OiG4MwALhaL2Z2k8CsRdfy-7dg7z41Rp6D0ZeEvtaUp4bX4aK\n" +
                "      raL4rTfw\",\n" +
                "  \"e\": \"AQAB\",\n" +
                "  \"d\": \"ZLe_TIxpE9-W_n2VBa-HWvuYPtjvxwVXClJFOpJsdea8g9RMx34qEO\n" +
                "      EtnoYc2un3CZ3LtJi-mju5RAT8YSc76YJds3ZVw0UiO8mMBeG6-iOnvg\n" +
                "      obobNx7K57-xjTJZU72EjOr9kB7z6ZKwDDq7HFyCDhUEcYcHFVc7iL_6\n" +
                "      TibVhAhOFONWlqlJgEgwVYd0rybNGKifdnpEbwyHoMwY6HM1qvnEFgP7\n" +
                "      iZ0YzHUT535x6jj4VKcdA7ZduFkhUauysySEW7mxZM6fj1vdjJIy9LD1\n" +
                "      fIz30Xv4ckoqhKF5GONU6tNmMmNgAD6gIViyEle1PrIxl1tBhCI14bRW\n" +
                "      -zrpHgAQ\",\n" +
                "  \"p\": \"yKWYoNIAqwMRQlgIBOdT1NIcbDNUUs2Rh-pBaxD_mIkweMt4Mg-0-B\n" +
                "      2iSYvMrs8horhonV7vxCQagcBAATGW-hAafUehWjxWSH-3KccRM8toL4\n" +
                "      e0q7M-idRDOBXSoe7Z2-CV2x_ZCY3RP8qp642R13WgXqGDIM4MbUkZSj\n" +
                "      cY9-c\",\n" +
                "  \"q\": \"uND4o15V30KDzf8vFJw589p1vlQVQ3NEilrinRUPHkkxaAzDzccGgr\n" +
                "      WMWpGxGFFnNL3w5CqPLeU76-5IVYQq0HwYVl0hVXQHr7sgaGu-483Ad3\n" +
                "      ENcL23FrOnF45m7_2ooAstJDe49MeLTTQKrSIBl_SKvqpYvfSPTczPcZ\n" +
                "      kh9Kk\",\n" +
                "  \"dp\": \"jmTnEoq2qqa8ouaymjhJSCnsveUXnMQC2gAneQJRQkFqQu-zV2PKP\n" +
                "      KNbPvKVyiF5b2-L3tM3OW2d2iNDyRUWXlT7V5l0KwPTABSTOnTqAmYCh\n" +
                "      Gi8kXXdlhcrtSvXldBakC6saxwI_TzGGY2MVXzc2ZnCvCXHV4qjSxOrf\n" +
                "      P3pHFU\",\n" +
                "  \"dq\": \"R9FUvU88OVzEkTkXl3-5-WusE4DjHmndeZIlu3rifBdfLpq_P-iWP\n" +
                "      BbGaq9wzQ1c-J7SzCdJqkEJDv5yd2C7rnZ6kpzwBh_nmL8zscAk1qsun\n" +
                "      nt9CJGAYz7-sGWy1JGShFazfP52ThB4rlCJ0YuEaQMrIzpY77_oLAhpm\n" +
                "      DA0hLk\",\n" +
                "  \"qi\": \"S8tC7ZknW6hPITkjcwttQOPLVmRfwirRlFAViuDb8NW9CrV_7F2Oq\n" +
                "      UZCqmzHTYAumwGFHI1WVRep7anleWaJjxC_1b3fq_al4qH3Pe-EKiHg6\n" +
                "      IMazuRtZLUROcThrExDbF5dYbsciDnfRUWLErZ4N1Be0bnxYuPqxwKd9\n" +
                "      QZwMo0\"\n" +
                "}";

        final String jweCs =
                "eyJhbGciOiJSU0EtT0FFUCIsImN0eSI6IkpXVCIsImVuYyI6IkExMjhHQ00ifQ" +
                "." +
                "a0JHRoITfpX4qRewImjlStn8m3CPxBV1ueYlVhjurCyrBg3I7YhCRYjphDOOS4" +
                "E7rXbr2Fn6NyQq-A-gqT0FXqNjVOGrG-bi13mwy7RoYhjTkBEC6P7sMYMXXx4g" +
                "zMedpiJHQVeyI-zkZV7A9matpgevAJWrXzOUysYGTtwoSN6gtUVtlLaivjvb21" +
                "O0ul4YxSHV-ByK1kyeetRp_fuYJxHoKLQL9P424sKx2WGYb4zsBIPF4ssl_e5I" +
                "R7nany-25_UmC2urosNkoFz9cQ82MypZP8gqbQJyPN-Fpp4Z-5o6yV64x6yzDU" +
                "F_5JCIdl-Qv6H5dMVIY7q1eKpXcV1lWO_2FefEBqXxXvIjLeZivjNkzogCq3-I" +
                "apSjVFnMjBxjpYLT8muaawo1yy1XXMuinIpNcOY3n4KKrXLrCcteX85m4IIHMZ" +
                "a38s1Hpr56fPPseMA-Jltmt-a9iEDtOzhtxz8AXy9tsCAZV2XBWNG8c3kJusAa" +
                "mBKOYwfk7JhLRDgOnJjlJLhn7TI4UxDp9dCmUXEN6z0v23W15qJIEXNJtqnblp" +
                "ymooeWAHCT4e_Owbim1g0AEpTHUdA2iiLNs9WTX_H_TXuPC8yDDhi1smxS_X_x" +
                "pkIHkiIHWDOLx03BpqDTivpKkBYwqP2UZkcxqX2Fo_GnVrNwlK7Lgxw6FSQvDO" +
                "0" +
                "." +
                "GbX1i9kXz0sxXPmA" +
                "." +
                "SZI4IvKHmwpazl_pJQXX3mHv1ANnOU4Wf9-utWYUcKrBNgCe2OFMf66cSJ8k2Q" +
                "kxaQD3_R60MGE9ofomwtky3GFxMeGRjtpMt9OAvVLsAXB0_UTCBGyBg3C2bWLX" +
                "qZlfJAAoJRUPRk-BimYZY81zVBuIhc7HsQePCpu33SzMsFHjn4lP_idrJz_glZ" +
                "TNgKDt8zdnUPauKTKDNOH1DD4fuzvDYfDIAfqGPyL5sVRwbiXpXdGokEszM-9C" +
                "hMPqW1QNhzuX_Zul3bvrJwr7nuGZs4cUScY3n8yE3AHCLurgls-A9mz1X38xEa" +
                "ulV18l4Fg9tLejdkAuQZjPbqeHQBJe4IwGD5Ee0dQ-Mtz4NnhkIWx-YKBb_Xo2" +
                "zI3Q_1sYjKUuis7yWW-HTr_vqvFt0bj7WJf2vzB0TZ3dvsoGaTvPH2dyWwumUr" +
                "lx4gmPUzBdwTO6ubfYSDUEEz5py0d_OtWeUSYcCYBKD-aM7tXg26qJo21gYjLf" +
                "hn9zy-W19sOCZGuzgFjPhawXHpvnj_t-0_ES96kogjJLxS1IMU9Y5XmnwZMyNc" +
                "9EIwnogsCg-hVuvzyP0sIruktmI94_SL1xgMl7o03phcTMxtlMizR88NKU1WkB" +
                "siXMCjy1Noue7MD-ShDp5dmM" +
                "." +
                "KnIKEhN8U-3C9s4gtSpjSw";

        JceProviderTestSupport jceProviderTestSupport = new JceProviderTestSupport();
        jceProviderTestSupport.setSignatureAlgsNeeded(AlgorithmIdentifiers.RSA_PSS_USING_SHA256);
        jceProviderTestSupport.setKeyManagementAlgsNeeded(KeyManagementAlgorithmIdentifiers.A128GCMKW);
        jceProviderTestSupport.runWithBouncyCastleProviderIfNeeded(new RunnableTest()
        {
            @Override
            public void runTest() throws Exception
            {
                PublicJsonWebKey encJwk = PublicJsonWebKey.Factory.newPublicJwk(figure62RsaJwkJsonString);

                JsonWebEncryption jwe = new JsonWebEncryption();
                jwe.setCompactSerialization(jweCs);
                jwe.setKey(encJwk.getPrivateKey());
                String jwePayload = jwe.getPayload();

                PublicJsonWebKey sigJwk = PublicJsonWebKey.Factory.newPublicJwk(sigJwkJson);

                JsonWebSignature jws = new JsonWebSignature();
                jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_PSS_USING_SHA256));
                jws.setCompactSerialization(jwePayload);
                jws.setKey(sigJwk.getPublicKey());
                assertTrue(jws.verifySignature());
                String payload = jws.getPayload();
                assertThat(expectedPayload, equalTo(payload));

                // also test this one w/ the jwt consumer stuff
                JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                        .setVerificationKey(sigJwk.getPublicKey())
                        .setDecryptionKey(encJwk.getPrivateKey())
                        .setRequireExpirationTime()
                        .setExpectedIssuer("hobbiton.example")
                        .setEvaluationTime(NumericDate.fromSeconds(1300819320))
                        .build();
                JwtClaims jwtClaims = jwtConsumer.processToClaims(jweCs);

                assertThat("hobbiton.example", equalTo(jwtClaims.getIssuer()));
                assertThat(NumericDate.fromSeconds(1300819380), equalTo(jwtClaims.getExpirationTime()));
                assertTrue(jwtClaims.getClaimValue("http://example.com/is_root", Boolean.class));
            }
        });
    }
}
