/** {@inheritDoc} */
  @Override
  public byte[] encrypt(final byte[] bytes) throws EncyptionException {
    try {
      final javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
      cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, this.secretKey);

      final AlgorithmParameters params = cipher.getParameters();
      final byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
      final byte[] dataBytes = cipher.doFinal(bytes);
      final byte[] secret = ArrayOfBytesType.TYPE.toBytes(ivBytes, dataBytes);
      return secret;
    } catch (final NoSuchAlgorithmException problem) {
      throw new EncyptionException(problem);
    } catch (final NoSuchPaddingException problem) {
      throw new EncyptionException(problem);
    } catch (final InvalidKeyException problem) {
      throw new EncyptionException(problem);
    } catch (final InvalidParameterSpecException problem) {
      throw new EncyptionException(problem);
    } catch (final IllegalBlockSizeException problem) {
      throw new EncyptionException(problem);
    } catch (final BadPaddingException problem) {
      throw new EncyptionException(problem);
    }
  }
Example #2
0
  public static Message encrypt(PublicKey pubKey, byte[] input) throws CryptoException {
    Message message = new Message();
    message.pubKey = pubKey.getEncoded();

    KeyGenerator keyGen;
    try {
      keyGen = KeyGenerator.getInstance("AES");
    } catch (NoSuchAlgorithmException e) {
      throw new CryptoException(e);
    }
    keyGen.init(128);
    SecretKey secretKey = keyGen.generateKey();

    try {
      Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
      rsaCipher.init(Cipher.ENCRYPT_MODE, pubKey);
      message.sessionKey = rsaCipher.doFinal(secretKey.getEncoded());

      Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      aesCipher.init(Cipher.ENCRYPT_MODE, secretKey);
      AlgorithmParameters params = aesCipher.getParameters();
      message.iv = params.getParameterSpec(IvParameterSpec.class).getIV();
      message.ciphertext = aesCipher.doFinal(input);
    } catch (NoSuchAlgorithmException
        | NoSuchPaddingException
        | InvalidKeyException
        | IllegalBlockSizeException
        | BadPaddingException
        | InvalidParameterSpecException e) {
      throw new CryptoException(e);
    }

    return message;
  }
  /**
   * @tests javax.crypto.Cipher#getParameters()
   * @tests javax.crypto.Cipher#init(int, java.security.Key, java.security.AlgorithmParameters,
   *     java.security.SecureRandom)
   */
  public void test_getParameters() throws Exception {

    /*
     * If this test is changed, implement the following:
     * test_initILjava_security_KeyLjava_security_AlgorithmParametersLjava_security_SecureRandom()
     */

    SecureRandom sr = new SecureRandom();
    Cipher cipher = null;

    byte[] apEncoding = null;

    byte[] iv = new byte[8];
    sr.nextBytes(iv);

    AlgorithmParameters ap = AlgorithmParameters.getInstance("DESede");
    ap.init(iv, "RAW");
    apEncoding = ap.getEncoded("ASN.1");

    cipher = Cipher.getInstance(algorithm + "/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, cipherKey, ap, sr);

    byte[] cipherParmsEnc = cipher.getParameters().getEncoded("ASN.1");
    assertTrue("Parameters differ", Arrays.equals(apEncoding, cipherParmsEnc));
  }
 public static String encrypt(String value) {
   try {
     SecretKeySpec sks = new SecretKeySpec(hexStringToByteArray(Config.ENCRYPTION_KEY), "AES");
     Cipher cipher = Cipher.getInstance("AES");
     cipher.init(Cipher.ENCRYPT_MODE, sks, cipher.getParameters());
     byte[] encrypted = cipher.doFinal(value.getBytes());
     return byteArrayToHexString(encrypted);
   } catch (Exception e) {
     throw new RuntimeException(e);
   }
 }
Example #5
0
  private void testGCMGeneric(byte[] K, byte[] N, byte[] A, byte[] P, byte[] C)
      throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
          IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException,
          NoSuchProviderException, IOException, InvalidParameterSpecException {
    Cipher eax = Cipher.getInstance("AES/GCM/NoPadding", "BC");
    SecretKeySpec key = new SecretKeySpec(K, "AES");

    // GCMParameterSpec mapped to AEADParameters and overrides default MAC
    // size
    GCMParameterSpec spec = new GCMParameterSpec(128, N);
    eax.init(Cipher.ENCRYPT_MODE, key, spec);

    eax.updateAAD(A);
    byte[] c = eax.doFinal(P);

    if (!areEqual(C, c)) {
      fail("JCE encrypt with additional data and GCMParameterSpec failed.");
    }

    eax = Cipher.getInstance("GCM", "BC");
    eax.init(Cipher.DECRYPT_MODE, key, spec);
    eax.updateAAD(A);
    byte[] p = eax.doFinal(C);

    if (!areEqual(P, p)) {
      fail("JCE decrypt with additional data and GCMParameterSpec failed.");
    }

    AlgorithmParameters algParams = eax.getParameters();

    byte[] encParams = algParams.getEncoded();

    GCMParameters gcmParameters = GCMParameters.getInstance(encParams);

    if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce())
        || spec.getTLen() != gcmParameters.getIcvLen()) {
      fail("parameters mismatch");
    }

    GCMParameterSpec gcmSpec = algParams.getParameterSpec(GCMParameterSpec.class);

    if (!Arrays.areEqual(gcmSpec.getIV(), gcmParameters.getNonce())
        || gcmSpec.getTLen() != gcmParameters.getIcvLen() * 8) {
      fail("spec parameters mismatch");
    }

    if (!Arrays.areEqual(eax.getIV(), gcmParameters.getNonce())) {
      fail("iv mismatch");
    }
  }
Example #6
0
 public AESEncryption() throws Exception {
   try {
     this.provider = new BouncyCastleProvider();
     SecretKeySpec skey = getSKeySpec(passphrase, true);
     encryptCipher = Cipher.getInstance(AES_ENCRYPTION_SCHEME, provider);
     decryptCipher = Cipher.getInstance(AES_ENCRYPTION_SCHEME, provider);
     encryptCipher.init(Cipher.ENCRYPT_MODE, skey);
     AlgorithmParameters ap = encryptCipher.getParameters();
     decryptCipher.init(Cipher.DECRYPT_MODE, skey, ap);
   } catch (Exception e) {
     e.printStackTrace();
     throw e;
   }
 }
Example #7
0
    private AlgorithmParameters checkParameters(Cipher c, byte[] salt, int iCount)
        throws InvalidParameterSpecException {
      AlgorithmParameters param = c.getParameters();
      PBEParameterSpec spec = (PBEParameterSpec) param.getParameterSpec(PBEParameterSpec.class);

      if (!Arrays.areEqual(salt, spec.getSalt())) {
        fail("" + algorithm + "failed salt test");
      }

      if (iCount != spec.getIterationCount()) {
        fail("" + algorithm + "failed count test");
      }
      return param;
    }
Example #8
0
  private void testGCMParameterSpecWithRepeatKey(byte[] K, byte[] N, byte[] A, byte[] P, byte[] C)
      throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
          IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException,
          NoSuchProviderException, IOException {
    Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
    SecretKeySpec key = new SecretKeySpec(K, "AES");
    GCMParameterSpec spec = new GCMParameterSpec(128, N);
    eax.init(Cipher.ENCRYPT_MODE, key, spec);

    eax.updateAAD(A);
    byte[] c = eax.doFinal(P);

    if (!areEqual(C, c)) {
      fail("JCE encrypt with additional data and RepeatedSecretKeySpec failed.");
    }

    // Check GCMParameterSpec handling knows about RepeatedSecretKeySpec
    eax.init(Cipher.DECRYPT_MODE, new RepeatedSecretKeySpec("AES"), spec);
    eax.updateAAD(A);
    byte[] p = eax.doFinal(C);

    if (!areEqual(P, p)) {
      fail("JCE decrypt with additional data and RepeatedSecretKeySpec failed.");
    }

    AlgorithmParameters algParams = eax.getParameters();

    byte[] encParams = algParams.getEncoded();

    GCMParameters gcmParameters = GCMParameters.getInstance(encParams);

    if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce())
        || spec.getTLen() != gcmParameters.getIcvLen()) {
      fail("parameters mismatch");
    }
  }
    CRMFOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
        throws CRMFException {
      KeyGenerator keyGen = helper.createKeyGenerator(encryptionOID);

      if (random == null) {
        random = new SecureRandom();
      }

      if (keySize < 0) {
        keyGen.init(random);
      } else {
        keyGen.init(keySize, random);
      }

      cipher = helper.createCipher(encryptionOID);
      encKey = keyGen.generateKey();
      AlgorithmParameters params = helper.generateParameters(encryptionOID, encKey, random);

      try {
        cipher.init(Cipher.ENCRYPT_MODE, encKey, params, random);
      } catch (InvalidKeyException e) {
        throw new CRMFException("unable to initialize cipher: " + e.getMessage(), e);
      } catch (GeneralSecurityException e) {
        throw new CRMFException("unable to initialize cipher: " + e.getMessage(), e);
      }

      //
      // If params are null we try and second guess on them as some providers don't provide
      // algorithm parameter generation explicity but instead generate them under the hood.
      //
      if (params == null) {
        params = cipher.getParameters();
      }

      algorithmIdentifier = helper.getAlgorithmIdentifier(encryptionOID, params);
    }
  public void prepare() {
    try {
      System.out.println(
          "\nalgorithm="
              + algorithm
              + ", mode="
              + mode
              + ", paddingStr="
              + paddingStr
              + ", msgSize="
              + msgSize
              + ", keySize="
              + keySize
              + ", noReinit="
              + noReinit
              + ", checkOutput="
              + checkOutput
              + ", encInputOffset="
              + encInputOffset
              + ", encOutputOffset="
              + encOutputOffset
              + ", decOutputOffset="
              + decOutputOffset
              + ", lastChunkSize="
              + lastChunkSize);

      if (encInputOffset % ALIGN != 0
          || encOutputOffset % ALIGN != 0
          || decOutputOffset % ALIGN != 0) testingMisalignment = true;

      int keyLenBytes = (keySize == 0 ? 16 : keySize / 8);
      byte keyBytes[] = new byte[keyLenBytes];
      if (keySize == 128)
        keyBytes = new byte[] {-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
      else random.nextBytes(keyBytes);

      key = new SecretKeySpec(keyBytes, algorithm);
      if (threadId == 0) {
        System.out.println(
            "Algorithm: " + key.getAlgorithm() + "(" + key.getEncoded().length * 8 + "bit)");
      }

      cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + paddingStr, "SunJCE");
      dCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + paddingStr, "SunJCE");

      if (mode.equals("CBC")) {
        int ivLen = (algorithm.equals("AES") ? 16 : algorithm.equals("DES") ? 8 : 0);
        IvParameterSpec initVector = new IvParameterSpec(new byte[ivLen]);
        cipher.init(Cipher.ENCRYPT_MODE, key, initVector);
      } else {
        algParams = cipher.getParameters();
        cipher.init(Cipher.ENCRYPT_MODE, key, algParams);
      }
      algParams = cipher.getParameters();
      dCipher.init(Cipher.DECRYPT_MODE, key, algParams);
      if (threadId == 0) {
        childShowCipher();
      }

      inputLength = msgSize + encInputOffset;
      if (testingMisalignment) {
        encodeLength = cipher.getOutputSize(msgSize - lastChunkSize) + encOutputOffset;
        encodeLength += cipher.getOutputSize(lastChunkSize);
        decodeLength = dCipher.getOutputSize(encodeLength - lastChunkSize) + decOutputOffset;
        decodeLength += dCipher.getOutputSize(lastChunkSize);
      } else {
        encodeLength = cipher.getOutputSize(msgSize) + encOutputOffset;
        decodeLength = dCipher.getOutputSize(encodeLength) + decOutputOffset;
      }

      input = new byte[inputLength];
      for (int i = encInputOffset, j = 0; i < inputLength; i++, j++) {
        input[i] = (byte) (j & 0xff);
      }

      // do one encode and decode in preparation
      encode = new byte[encodeLength];
      decode = new byte[decodeLength];
      if (testingMisalignment) {
        decodeMsgSize =
            cipher.update(
                input, encInputOffset, (msgSize - lastChunkSize), encode, encOutputOffset);
        decodeMsgSize +=
            cipher.doFinal(
                input,
                (encInputOffset + msgSize - lastChunkSize),
                lastChunkSize,
                encode,
                (encOutputOffset + decodeMsgSize));

        int tempSize =
            dCipher.update(
                encode, encOutputOffset, (decodeMsgSize - lastChunkSize), decode, decOutputOffset);
        dCipher.doFinal(
            encode,
            (encOutputOffset + decodeMsgSize - lastChunkSize),
            lastChunkSize,
            decode,
            (decOutputOffset + tempSize));
      } else {
        decodeMsgSize = cipher.doFinal(input, encInputOffset, msgSize, encode, encOutputOffset);
        dCipher.doFinal(encode, encOutputOffset, decodeMsgSize, decode, decOutputOffset);
      }
      if (checkOutput) {
        expectedEncode = (byte[]) encode.clone();
        expectedDecode = (byte[]) decode.clone();
        showArray(key.getEncoded(), "key:    ");
        showArray(input, "input:  ");
        showArray(encode, "encode: ");
        showArray(decode, "decode: ");
      }
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
    }
  }
  private void run(String mode) throws Exception {

    DHParameterSpec dhSkipParamSpec;

    if (mode.equals("GENERATE_DH_PARAMS")) {
      // Some central authority creates new DH parameters
      System.out.println("Creating Diffie-Hellman parameters (takes VERY long) ...");
      AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH");
      paramGen.init(512);
      AlgorithmParameters params = paramGen.generateParameters();
      dhSkipParamSpec = (DHParameterSpec) params.getParameterSpec(DHParameterSpec.class);
    } else {
      // use some pre-generated, default DH parameters
      System.out.println("Using SKIP Diffie-Hellman parameters");
      dhSkipParamSpec = new DHParameterSpec(skip1024Modulus, skip1024Base);
    }

    /*
     * Alice creates her own DH key pair, using the DH parameters from
     * above
     */
    System.out.println("ALICE: Generate DH keypair ...");
    KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");
    aliceKpairGen.initialize(dhSkipParamSpec);
    KeyPair aliceKpair = aliceKpairGen.generateKeyPair();

    // Alice creates and initializes her DH KeyAgreement object
    System.out.println("ALICE: Initialization ...");
    KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
    aliceKeyAgree.init(aliceKpair.getPrivate());

    // Alice encodes her public key, and sends it over to Bob.
    byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();

    /*
     * Let's turn over to Bob. Bob has received Alice's public key
     * in encoded format.
     * He instantiates a DH public key from the encoded key material.
     */
    KeyFactory bobKeyFac = KeyFactory.getInstance("DH");
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEnc);
    PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);

    /*
     * Bob gets the DH parameters associated with Alice's public key.
     * He must use the same parameters when he generates his own key
     * pair.
     */
    DHParameterSpec dhParamSpec = ((DHPublicKey) alicePubKey).getParams();

    // Bob creates his own DH key pair
    System.out.println("BOB: Generate DH keypair ...");
    KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
    bobKpairGen.initialize(dhParamSpec);
    KeyPair bobKpair = bobKpairGen.generateKeyPair();

    // Bob creates and initializes his DH KeyAgreement object
    System.out.println("BOB: Initialization ...");
    KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
    bobKeyAgree.init(bobKpair.getPrivate());

    // Bob encodes his public key, and sends it over to Alice.
    byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();

    /*
     * Alice uses Bob's public key for the first (and only) phase
     * of her version of the DH
     * protocol.
     * Before she can do so, she has to instantiate a DH public key
     * from Bob's encoded key material.
     */
    KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");
    x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
    PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
    System.out.println("ALICE: Execute PHASE1 ...");
    aliceKeyAgree.doPhase(bobPubKey, true);

    /*
     * Bob uses Alice's public key for the first (and only) phase
     * of his version of the DH
     * protocol.
     */
    System.out.println("BOB: Execute PHASE1 ...");
    bobKeyAgree.doPhase(alicePubKey, true);

    /*
     * At this stage, both Alice and Bob have completed the DH key
     * agreement protocol.
     * Both generate the (same) shared secret.
     */
    byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
    int aliceLen = aliceSharedSecret.length;

    byte[] bobSharedSecret = new byte[aliceLen];
    int bobLen;
    try {
      // show example of what happens if you
      // provide an output buffer that is too short
      bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 1);
    } catch (ShortBufferException e) {
      System.out.println(e.getMessage());
    }
    // provide output buffer of required size
    bobLen = bobKeyAgree.generateSecret(bobSharedSecret, 0);

    System.out.println("Alice secret: " + toHexString(aliceSharedSecret));
    System.out.println("Bob secret: " + toHexString(bobSharedSecret));

    if (!java.util.Arrays.equals(aliceSharedSecret, bobSharedSecret))
      throw new Exception("Shared secrets differ");
    System.out.println("Shared secrets are the same");

    /*
     * Now let's return the shared secret as a SecretKey object
     * and use it for encryption. First, we generate SecretKeys for the
     * "DES" algorithm (based on the raw shared secret data) and
     * then we use DES in ECB mode
     * as the encryption algorithm. DES in ECB mode does not require any
     * parameters.
     *
     * Then we use DES in CBC mode, which requires an initialization
     * vector (IV) parameter. In CBC mode, you need to initialize the
     * Cipher object with an IV, which can be supplied using the
     * javax.crypto.spec.IvParameterSpec class. Note that you have to use
     * the same IV for encryption and decryption: If you use a different
     * IV for decryption than you used for encryption, decryption will
     * fail.
     *
     * NOTE: If you do not specify an IV when you initialize the
     * Cipher object for encryption, the underlying implementation
     * will generate a random one, which you have to retrieve using the
     * javax.crypto.Cipher.getParameters() method, which returns an
     * instance of java.security.AlgorithmParameters. You need to transfer
     * the contents of that object (e.g., in encoded format, obtained via
     * the AlgorithmParameters.getEncoded() method) to the party who will
     * do the decryption. When initializing the Cipher for decryption,
     * the (reinstantiated) AlgorithmParameters object must be passed to
     * the Cipher.init() method.
     */
    System.out.println("Return shared secret as SecretKey object ...");
    // Bob
    // NOTE: The call to bobKeyAgree.generateSecret above reset the key
    // agreement object, so we call doPhase again prior to another
    // generateSecret call
    bobKeyAgree.doPhase(alicePubKey, true);
    SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");

    // Alice
    // NOTE: The call to aliceKeyAgree.generateSecret above reset the key
    // agreement object, so we call doPhase again prior to another
    // generateSecret call
    aliceKeyAgree.doPhase(bobPubKey, true);
    SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");

    /*
     * Bob encrypts, using DES in ECB mode
     */
    Cipher bobCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);

    byte[] cleartext = "This is just an example".getBytes();
    byte[] ciphertext = bobCipher.doFinal(cleartext);

    /*
     * Alice decrypts, using DES in ECB mode
     */
    Cipher aliceCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);
    byte[] recovered = aliceCipher.doFinal(ciphertext);

    if (!java.util.Arrays.equals(cleartext, recovered))
      throw new Exception("DES in CBC mode recovered text is " + "different from cleartext");
    System.out.println("DES in ECB mode recovered text is " + "same as cleartext");

    /*
     * Bob encrypts, using DES in CBC mode
     */
    bobCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);

    cleartext = "This is just an example".getBytes();
    ciphertext = bobCipher.doFinal(cleartext);
    // Retrieve the parameter that was used, and transfer it to Alice in
    // encoded format
    byte[] encodedParams = bobCipher.getParameters().getEncoded();

    /*
     * Alice decrypts, using DES in CBC mode
     */
    // Instantiate AlgorithmParameters object from parameter encoding
    // obtained from Bob
    AlgorithmParameters params = AlgorithmParameters.getInstance("DES");
    params.init(encodedParams);
    aliceCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey, params);
    recovered = aliceCipher.doFinal(ciphertext);

    if (!java.util.Arrays.equals(cleartext, recovered))
      throw new Exception("DES in CBC mode recovered text is " + "different from cleartext");
    System.out.println("DES in CBC mode recovered text is " + "same as cleartext");
  }
Example #12
0
  public void performTest() throws Exception {
    KeyFactory fact;
    byte[] input =
        new byte[] {
          (byte) 0x54,
          (byte) 0x85,
          (byte) 0x9b,
          (byte) 0x34,
          (byte) 0x2c,
          (byte) 0x49,
          (byte) 0xea,
          (byte) 0x2a
        };
    byte[][] output =
        new byte[][] {
          Hex.decode(
              "8b427f781a2e59dd9def386f1956b996ee07f48c96880e65a368055ed8c0a8831669ef7250b40918b2b1d488547e72c84540e42bd07b03f14e226f04fbc2d929"),
          Hex.decode(
              "2ec6e1a1711b6c7b8cd3f6a25db21ab8bb0a5f1d6df2ef375fa708a43997730ffc7c98856dbbe36edddcdd1b2d2a53867d8355af94fea3aeec128da908e08f4c"),
          Hex.decode(
              "0850ac4e5a8118323200c8ed1e5aaa3d5e635172553ccac66a8e4153d35c79305c4440f11034ab147fccce21f18a50cf1c0099c08a577eb68237a91042278965"),
          Hex.decode(
              "1c9649bdccb51056751fe43837f4eb43bada472accf26f65231666d5de7d11950d8379b3596dfdf75c6234274896fa8d18ad0865d3be2ac4d6687151abdf01e93941dcef18fa63186c9351d1506c89d09733c5ff4304208c812bdd21a50f56fde115e629e0e973721c9fcc87e89295a79853dee613962a0b2f2fc57163fd99057a3c776f13c20c26407eb8863998d7e53b543ba8d0a295a9a68d1a149833078c9809ad6a6dad7fc22a95ad615a73138c54c018f40d99bf8eeecd45f5be526f2d6b01aeb56381991c1ab31a2e756f15e052b9cd5638b2eff799795c5bae493307d5eb9f8c21d438de131fe505a4e7432547ab19224094f9e4be1968bd0793b79d"),
          Hex.decode(
              "4c4afc0c24dddaedd4f9a3b23be30d35d8e005ffd36b3defc5d18acc830c3ed388ce20f43a00e614fd087c814197bc9fc2eff9ad4cc474a7a2ef3ed9c0f0a55eb23371e41ee8f2e2ed93ea3a06ca482589ab87e0d61dcffda5eea1241408e43ea1108726cdb87cc3aa5e9eaaa9f72507ca1352ac54a53920c94dccc768147933d8c50aefd9d1da10522a40133cd33dbc0524669e70f771a88d65c4716d471cd22b08b9f01f24e4e9fc7ffbcfa0e0a7aed47b345826399b26a73be112eb9c5e06fc6742fc3d0ef53d43896403c5105109cfc12e6deeaf4a48ba308e039774b9bdb31a9b9e133c81c321630cf0b4b2d1f90717b24c3268e1fea681ea9cdc709342"),
          Hex.decode(
              "06b5b26bd13515f799e5e37ca43cace15cd82fd4bf36b25d285a6f0998d97c8cb0755a28f0ae66618b1cd03e27ac95eaaa4882bc6dc0078cd457d4f7de4154173a9c7a838cfc2ac2f74875df462aae0cfd341645dc51d9a01da9bdb01507f140fa8a016534379d838cc3b2a53ac33150af1b242fc88013cb8d914e66c8182864ee6de88ce2879d4c05dd125409620a96797c55c832fb2fb31d4310c190b8ed2c95fdfda2ed87f785002faaec3f35ec05cf70a3774ce185e4882df35719d582dd55ac31257344a9cba95189dcbea16e8c6cb7a235a0384bc83b6183ca8547e670fe33b1b91725ae0c250c9eca7b5ba78bd77145b70270bf8ac31653006c02ca9c"),
          Hex.decode(
              "135f1be3d045526235bf9d5e43499d4ee1bfdf93370769ae56e85dbc339bc5b7ea3bee49717497ee8ac3f7cd6adb6fc0f17812390dcd65ac7b87fef7970d9ff9"),
          Hex.decode(
              "03c05add1e030178c352face07cafc9447c8f369b8f95125c0d311c16b6da48ca2067104cce6cd21ae7b163cd18ffc13001aecebdc2eb02b9e92681f84033a98"),
          Hex.decode(
              "00319bb9becb49f3ed1bca26d0fcf09b0b0a508e4d0bd43b350f959b72cd25b3af47d608fdcd248eada74fbe19990dbeb9bf0da4b4e1200243a14e5cab3f7e610c")
        };
    SecureRandom rand = new FixedSecureRandom();

    fact = KeyFactory.getInstance("RSA", "BC");

    PrivateKey privKey = fact.generatePrivate(privKeySpec);
    PublicKey pubKey = fact.generatePublic(pubKeySpec);

    PrivateKey priv2048Key = fact.generatePrivate(priv2048KeySpec);
    PublicKey pub2048Key = fact.generatePublic(pub2048KeySpec);

    //
    // key without CRT coefficients
    //
    PrivateKeyInfo keyInfo = PrivateKeyInfo.getInstance(privKey.getEncoded());
    BigInteger zero = BigInteger.valueOf(0);
    PKCS8EncodedKeySpec noCrtSpec =
        new PKCS8EncodedKeySpec(
            new PrivateKeyInfo(
                    keyInfo.getPrivateKeyAlgorithm(),
                    new org.mightyfish.asn1.pkcs.RSAPrivateKey(
                        privKeySpec.getModulus(),
                        privKeySpec.getPublicExponent(),
                        privKeySpec.getPrivateExponent(),
                        zero,
                        zero,
                        zero,
                        zero,
                        zero))
                .getEncoded());

    PrivateKey noCrtKey = fact.generatePrivate(noCrtSpec);
    if (noCrtKey instanceof RSAPrivateCrtKey) {
      fail("private key without CRT coefficients returned as CRT key");
    }

    //
    // No Padding
    //
    Cipher c = Cipher.getInstance("RSA", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    byte[] out = c.doFinal(input);

    if (!areEqual(out, output[0])) {
      fail(
          "NoPadding test failed on encrypt expected "
              + new String(Hex.encode(output[0]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "NoPadding test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // No Padding - incremental
    //
    c = Cipher.getInstance("RSA", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    c.update(input);

    out = c.doFinal();

    if (!areEqual(out, output[0])) {
      fail(
          "NoPadding test failed on encrypt expected "
              + new String(Hex.encode(output[0]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "NoPadding test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // No Padding - incremental - explicit use of NONE in mode.
    //
    c = Cipher.getInstance("RSA/NONE/NoPadding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    c.update(input);

    out = c.doFinal();

    if (!areEqual(out, output[0])) {
      fail(
          "NoPadding test failed on encrypt expected "
              + new String(Hex.encode(output[0]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "NoPadding test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // No Padding - maximum length
    //
    c = Cipher.getInstance("RSA", "BC");

    byte[] modBytes = ((RSAPublicKey) pubKey).getModulus().toByteArray();
    byte[] maxInput = new byte[modBytes.length - 1];

    maxInput[0] |= 0x7f;

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.doFinal(maxInput);

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, maxInput)) {
      fail(
          "NoPadding test failed on decrypt expected "
              + new String(Hex.encode(maxInput))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // PKCS1 V 1.5
    //
    c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[1])) {
      fail(
          "PKCS1 test failed on encrypt expected "
              + new String(Hex.encode(output[1]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "PKCS1 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // PKCS1 V 1.5 - NONE
    //
    c = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[1])) {
      fail(
          "PKCS1 test failed on encrypt expected "
              + new String(Hex.encode(output[1]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "PKCS1 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // OAEP - SHA1
    //
    c = Cipher.getInstance("RSA/NONE/OAEPPadding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[2])) {
      fail(
          "OAEP test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c = Cipher.getInstance("RSA/NONE/OAEPWithSHA1AndMGF1Padding", "BC");

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    AlgorithmParameters oaepP = c.getParameters();

    if (!areEqual(
        oaepP.getEncoded(),
        new RSAESOAEPparams(
                new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_mgf1,
                    new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE)),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0])))
            .getEncoded())) {
      fail("OAEP test failed default sha-1 parameters");
    }

    //
    // OAEP - SHA224
    //
    c = Cipher.getInstance("RSA/NONE/OAEPWithSHA224AndMGF1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pub2048Key, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[3])) {
      fail(
          "OAEP SHA-224 test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, priv2048Key);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP SHA-224 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    oaepP = c.getParameters();

    if (!areEqual(
        oaepP.getEncoded(),
        new RSAESOAEPparams(
                new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_mgf1,
                    new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha224, DERNull.INSTANCE)),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0])))
            .getEncoded())) {
      fail("OAEP test failed default sha-224 parameters");
    }

    //
    // OAEP - SHA 256
    //
    c = Cipher.getInstance("RSA/NONE/OAEPWithSHA256AndMGF1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pub2048Key, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[4])) {
      fail(
          "OAEP SHA-256 test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, priv2048Key);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP SHA-256 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    oaepP = c.getParameters();

    if (!areEqual(
        oaepP.getEncoded(),
        new RSAESOAEPparams(
                new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_mgf1,
                    new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256, DERNull.INSTANCE)),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0])))
            .getEncoded())) {
      fail("OAEP test failed default sha-256 parameters");
    }

    //
    // OAEP - SHA 384
    //
    c = Cipher.getInstance("RSA/NONE/OAEPWithSHA384AndMGF1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pub2048Key, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[5])) {
      fail(
          "OAEP SHA-384 test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, priv2048Key);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP SHA-384 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    oaepP = c.getParameters();

    if (!areEqual(
        oaepP.getEncoded(),
        new RSAESOAEPparams(
                new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_mgf1,
                    new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384, DERNull.INSTANCE)),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0])))
            .getEncoded())) {
      fail("OAEP test failed default sha-384 parameters");
    }

    //
    // OAEP - MD5
    //
    c = Cipher.getInstance("RSA/NONE/OAEPWithMD5AndMGF1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[6])) {
      fail(
          "OAEP MD5 test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP MD5 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    oaepP = c.getParameters();

    if (!areEqual(
        oaepP.getEncoded(),
        new RSAESOAEPparams(
                new AlgorithmIdentifier(PKCSObjectIdentifiers.md5, DERNull.INSTANCE),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_mgf1,
                    new AlgorithmIdentifier(PKCSObjectIdentifiers.md5, DERNull.INSTANCE)),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(new byte[0])))
            .getEncoded())) {
      fail("OAEP test failed default md5 parameters");
    }

    //
    // OAEP - SHA1 with default parameters
    //
    c = Cipher.getInstance("RSA/NONE/OAEPPadding", "BC");

    c.init(Cipher.ENCRYPT_MODE, pubKey, OAEPParameterSpec.DEFAULT, rand);

    out = c.doFinal(input);

    if (!areEqual(out, output[2])) {
      fail(
          "OAEP test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c = Cipher.getInstance("RSA/NONE/OAEPWithSHA1AndMGF1Padding", "BC");

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    oaepP = c.getParameters();

    if (!areEqual(oaepP.getEncoded(), new byte[] {0x30, 0x00})) {
      fail("OAEP test failed default parameters");
    }

    //
    // OAEP - SHA1 with specified string
    //
    c = Cipher.getInstance("RSA/NONE/OAEPPadding", "BC");

    c.init(
        Cipher.ENCRYPT_MODE,
        pubKey,
        new OAEPParameterSpec(
            "SHA1",
            "MGF1",
            new MGF1ParameterSpec("SHA1"),
            new PSource.PSpecified(new byte[] {1, 2, 3, 4, 5})),
        rand);

    out = c.doFinal(input);

    oaepP = c.getParameters();

    if (!areEqual(
        oaepP.getEncoded(),
        new RSAESOAEPparams(
                new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_mgf1,
                    new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE)),
                new AlgorithmIdentifier(
                    PKCSObjectIdentifiers.id_pSpecified,
                    new DEROctetString(new byte[] {1, 2, 3, 4, 5})))
            .getEncoded())) {
      fail("OAEP test failed changed sha-1 parameters");
    }

    if (!areEqual(out, output[7])) {
      fail(
          "OAEP test failed on encrypt expected "
              + new String(Hex.encode(output[2]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c = Cipher.getInstance("RSA/NONE/OAEPWithSHA1AndMGF1Padding", "BC");

    c.init(Cipher.DECRYPT_MODE, privKey, oaepP);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "OAEP test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // ISO9796-1
    //
    byte[] isoInput =
        Hex.decode("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
    PrivateKey isoPrivKey = fact.generatePrivate(isoPrivKeySpec);
    PublicKey isoPubKey = fact.generatePublic(isoPubKeySpec);

    c = Cipher.getInstance("RSA/NONE/ISO9796-1Padding", "BC");

    c.init(Cipher.ENCRYPT_MODE, isoPrivKey);

    out = c.doFinal(isoInput);

    if (!areEqual(out, output[8])) {
      fail(
          "ISO9796-1 test failed on encrypt expected "
              + new String(Hex.encode(output[3]))
              + " got "
              + new String(Hex.encode(out)));
    }

    c.init(Cipher.DECRYPT_MODE, isoPubKey);

    out = c.doFinal(out);

    if (!areEqual(out, isoInput)) {
      fail(
          "ISO9796-1 test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    //
    // generation with parameters test.
    //
    KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", "BC");

    //
    // 768 bit RSA with e = 2^16-1
    //
    keyPairGen.initialize(
        new RSAKeyGenParameterSpec(768, BigInteger.valueOf(65537)), new SecureRandom());

    KeyPair kp = keyPairGen.generateKeyPair();

    pubKey = kp.getPublic();
    privKey = kp.getPrivate();

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.doFinal(input);

    c.init(Cipher.DECRYPT_MODE, privKey);

    out = c.doFinal(out);

    if (!areEqual(out, input)) {
      fail(
          "key generation test failed on decrypt expected "
              + new String(Hex.encode(input))
              + " got "
              + new String(Hex.encode(out)));
    }

    //
    // comparison check
    //
    KeyFactory keyFact = KeyFactory.getInstance("RSA", "BC");

    RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey) keyFact.translateKey(privKey);

    if (!privKey.equals(crtKey)) {
      fail("private key equality check failed");
    }

    crtKey =
        (RSAPrivateCrtKey) keyFact.generatePrivate(new PKCS8EncodedKeySpec(privKey.getEncoded()));

    if (!privKey.equals(crtKey)) {
      fail("private key equality check failed");
    }

    crtKey = (RSAPrivateCrtKey) serializeDeserialize(privKey);

    if (!privKey.equals(crtKey)) {
      fail("private key equality check failed");
    }

    if (privKey.hashCode() != crtKey.hashCode()) {
      fail("private key hashCode check failed");
    }

    RSAPublicKey copyKey = (RSAPublicKey) keyFact.translateKey(pubKey);

    if (!pubKey.equals(copyKey)) {
      fail("public key equality check failed");
    }

    copyKey = (RSAPublicKey) keyFact.generatePublic(new X509EncodedKeySpec(pubKey.getEncoded()));

    if (!pubKey.equals(copyKey)) {
      fail("public key equality check failed");
    }

    copyKey = (RSAPublicKey) serializeDeserialize(pubKey);

    if (!pubKey.equals(copyKey)) {
      fail("public key equality check failed");
    }

    if (pubKey.hashCode() != copyKey.hashCode()) {
      fail("public key hashCode check failed");
    }

    //
    // test an OAEP key
    //
    SubjectPublicKeyInfo oaepKey =
        new SubjectPublicKeyInfo(
            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, new RSAESOAEPparams()),
            SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()).parsePublicKey());

    copyKey =
        (RSAPublicKey)
            serializeDeserialize(
                keyFact.generatePublic(new X509EncodedKeySpec(oaepKey.getEncoded())));

    if (!pubKey.equals(copyKey)) {
      fail("public key equality check failed");
    }

    if (pubKey.hashCode() != copyKey.hashCode()) {
      fail("public key hashCode check failed");
    }

    if (!Arrays.areEqual(copyKey.getEncoded(), oaepKey.getEncoded())) {
      fail("encoding does not match");
    }

    oaepCompatibilityTest("SHA-1", priv2048Key, pub2048Key);
    // TODO: oaepCompatibilityTest("SHA-224", priv2048Key, pub2048Key);      commented out as fails
    // in JDK 1.7
    oaepCompatibilityTest("SHA-256", priv2048Key, pub2048Key);
    oaepCompatibilityTest("SHA-384", priv2048Key, pub2048Key);
    oaepCompatibilityTest("SHA-512", priv2048Key, pub2048Key);

    SecureRandom random = new SecureRandom();
    rawModeTest("SHA1withRSA", X509ObjectIdentifiers.id_SHA1, priv2048Key, pub2048Key, random);
    rawModeTest("MD5withRSA", PKCSObjectIdentifiers.md5, priv2048Key, pub2048Key, random);
    rawModeTest(
        "RIPEMD128withRSA", TeleTrusTObjectIdentifiers.ripemd128, priv2048Key, pub2048Key, random);

    // init reset test
    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.update(new byte[40]);

    c.init(Cipher.ENCRYPT_MODE, pubKey, rand);

    out = c.update(new byte[40]);
  }
  protected boolean handleLocalTunnel(
      TrackerWebPageRequest request, TrackerWebPageResponse response) throws IOException {
    start();

    if (SRP_VERIFIER == null || !active) {

      throw (new IOException("Secure pairing is not enabled"));
    }

    boolean good_request = false;

    try {
      // remove /pairing/tunnel/

      String url = request.getURL().substring(16);

      int q_pos = url.indexOf('?');

      Map<String, String> args = new HashMap<String, String>();

      if (q_pos != -1) {

        String args_str = url.substring(q_pos + 1);

        String[] bits = args_str.split("&");

        for (String arg : bits) {

          String[] x = arg.split("=");

          if (x.length == 2) {

            args.put(x[0].toLowerCase(), x[1]);
          }
        }

        url = url.substring(0, q_pos);
      }

      if (url.startsWith("create")) {

        String ac = args.get("ac");
        String sid = args.get("sid");

        if (ac == null || sid == null) {

          throw (new IOException("Access code or service id missing"));
        }

        if (!ac.equals(manager.peekAccessCode())) {

          throw (new IOException("Invalid access code"));
        }

        PairedServiceImpl ps = manager.getService(sid);

        if (ps == null) {

          good_request = true;

          throw (new IOException("Service '" + sid + "' not registered"));
        }

        PairedServiceRequestHandler handler = ps.getHandler();

        if (handler == null) {

          good_request = true;

          throw (new IOException("Service '" + sid + "' has no handler registered"));
        }

        JSONObject json = new JSONObject();

        JSONObject result = new JSONObject();

        json.put("result", result);

        byte[] ss = new byte[] {SRP_SALT[0], SRP_SALT[1], SRP_SALT[2], SRP_SALT[3]};

        long tunnel_id = RandomUtils.nextSecureAbsoluteLong();

        String tunnel_name = Base32.encode(ss) + "_" + tunnel_id;

        synchronized (local_server_map) {
          long diff = SystemTime.getMonotonousTime() - last_local_server_create_time;

          if (diff < 5000) {

            try {
              long sleep = 5000 - diff;

              System.out.println("Sleeping for " + sleep + " before starting srp");

              Thread.sleep(sleep);

            } catch (Throwable e) {
            }
          }

          SRP6Server server = new SRP6Server();

          server.init(N_3072, G_3072, SRP_VERIFIER, new SHA256Digest(), RandomUtils.SECURE_RANDOM);

          BigInteger B = server.generateServerCredentials();

          local_server_map.put(tunnel_name, new Object[] {server, handler, null, null});

          last_local_server_create_time = SystemTime.getMonotonousTime();

          total_local_servers++;

          result.put("srp_salt", Base32.encode(SRP_SALT));

          result.put("srp_b", Base32.encode(B.toByteArray()));

          Map<String, String> headers = request.getHeaders();

          String host = headers.get("host");

          // remove port number

          int pos = host.lastIndexOf("]");

          if (pos != -1) {

            // ipv6 literal

            host = host.substring(0, pos + 1);

          } else {

            pos = host.indexOf(':');

            if (pos != -1) {

              host = host.substring(0, pos);
            }
          }

          String abs_url = request.getAbsoluteURL().toString();

          // unfortunately there is some nasty code that uses a configured tracker
          // address as the default host

          abs_url = UrlUtils.setHost(new URL(abs_url), host).toExternalForm();

          pos = abs_url.indexOf("/create");

          String tunnel_url = abs_url.substring(0, pos) + "/id/" + tunnel_name;

          result.put("url", tunnel_url);
        }

        response.getOutputStream().write(JSONUtils.encodeToJSON(json).getBytes("UTF-8"));

        response.setContentType("application/json; charset=UTF-8");

        response.setGZIP(true);

        good_request = true;

        return (true);

      } else if (url.startsWith("id/")) {

        String tunnel_name = url.substring(3);

        Object[] entry;

        synchronized (local_server_map) {
          entry = local_server_map.get(tunnel_name);

          if (entry == null) {

            good_request = true;

            throw (new IOException("Unknown tunnel id"));
          }
        }

        String srp_a = args.get("srp_a");
        String enc_data = args.get("enc_data");
        String enc_iv = args.get("enc_iv");

        if (srp_a != null && enc_data != null && enc_iv != null) {

          try {
            synchronized (local_server_map) {
              long diff = SystemTime.getMonotonousTime() - last_local_server_agree_time;

              if (diff < 5000) {

                try {
                  long sleep = 5000 - diff;

                  System.out.println("Sleeping for " + sleep + " before completing srp");

                  Thread.sleep(sleep);

                } catch (Throwable e) {
                }
              }
            }

            JSONObject json = new JSONObject();

            JSONObject result = new JSONObject();

            json.put("result", result);

            SRP6Server server = (SRP6Server) entry[0];

            BigInteger A = new BigInteger(Base32.decode(srp_a));

            BigInteger serverS = server.calculateSecret(A);

            byte[] shared_secret = serverS.toByteArray();

            Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            byte[] key = new byte[16];

            System.arraycopy(shared_secret, 0, key, 0, 16);

            SecretKeySpec secret = new SecretKeySpec(key, "AES");

            decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(Base32.decode(enc_iv)));

            byte[] dec = decipher.doFinal(Base32.decode(enc_data));

            JSONObject dec_json = (JSONObject) JSONUtils.decodeJSON(new String(dec, "UTF-8"));

            String tunnel_url = (String) dec_json.get("url");

            if (!tunnel_url.contains(tunnel_name)) {

              throw (new IOException("Invalid tunnel url"));
            }

            String endpoint_url = (String) dec_json.get("endpoint");

            entry[2] = secret;
            entry[3] = endpoint_url;

            result.put("state", "activated");

            response.getOutputStream().write(JSONUtils.encodeToJSON(json).getBytes("UTF-8"));

            response.setContentType("application/json; charset=UTF-8");

            response.setGZIP(true);

            good_request = true;

            return (true);

          } catch (Throwable e) {

            throw (new IOException(Debug.getNestedExceptionMessage(e)));

          } finally {

            last_local_server_agree_time = SystemTime.getMonotonousTime();
          }
        } else if (args.containsKey("close")) {

          synchronized (local_server_map) {
            local_server_map.remove(tunnel_name);
          }

          good_request = true;

          return (true);

        } else {

          PairedServiceRequestHandler request_handler = (PairedServiceRequestHandler) entry[1];

          SecretKeySpec secret = (SecretKeySpec) entry[2];

          String endpoint_url = (String) entry[3];

          if (secret == null) {

            throw (new IOException("auth not completed"));
          }

          byte[] request_data = FileUtil.readInputStreamAsByteArray(request.getInputStream());

          try {
            byte[] decrypted;

            {
              byte[] IV = new byte[16];

              System.arraycopy(request_data, 0, IV, 0, IV.length);

              Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

              decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));

              decrypted = decipher.doFinal(request_data, 16, request_data.length - 16);
            }

            byte[] reply_bytes =
                request_handler.handleRequest(
                    request.getClientAddress2().getAddress(), endpoint_url, decrypted);

            {
              Cipher encipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

              encipher.init(Cipher.ENCRYPT_MODE, secret);

              AlgorithmParameters params = encipher.getParameters();

              byte[] IV = params.getParameterSpec(IvParameterSpec.class).getIV();

              byte[] enc = encipher.doFinal(reply_bytes);

              byte[] rep_bytes = new byte[IV.length + enc.length];

              System.arraycopy(IV, 0, rep_bytes, 0, IV.length);
              System.arraycopy(enc, 0, rep_bytes, IV.length, enc.length);

              response.getOutputStream().write(rep_bytes);

              response.setContentType("application/octet-stream");

              good_request = true;

              return (true);
            }
          } catch (Throwable e) {

            throw (new IOException(Debug.getNestedExceptionMessage(e)));
          }
        }
      }

      throw (new IOException("Unknown tunnel operation"));

    } finally {

      if (!good_request) {

        manager.recordRequest(
            "SRP", request.getClientAddress2().getAddress().getHostAddress(), false);
      }
    }
  }
  public OutputEncryptor build(final char[] password) throws OperatorCreationException {
    final Cipher cipher;
    SecretKey key;

    if (random == null) {
      random = new SecureRandom();
    }

    final AlgorithmIdentifier encryptionAlg;
    final byte[] salt = new byte[20];
    final int iterationCount = 1024;

    random.nextBytes(salt);

    try {
      if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)) {
        PBEKeySpec pbeSpec = new PBEKeySpec(password);

        SecretKeyFactory keyFact = helper.createSecretKeyFactory(algorithm.getId());

        PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount);

        key = keyFact.generateSecret(pbeSpec);

        cipher = helper.createCipher(algorithm.getId());

        cipher.init(Cipher.ENCRYPT_MODE, key, defParams);

        encryptionAlg =
            new AlgorithmIdentifier(algorithm, new PKCS12PBEParams(salt, iterationCount));
      } else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) {
        SecretKeyFactory keyFact =
            helper.createSecretKeyFactory(PKCSObjectIdentifiers.id_PBKDF2.getId());

        key =
            keyFact.generateSecret(
                new PBEKeySpec(
                    password,
                    salt,
                    iterationCount,
                    keySizeProvider.getKeySize(new AlgorithmIdentifier(keyEncAlgorithm))));

        cipher = helper.createCipher(keyEncAlgorithm.getId());

        cipher.init(Cipher.ENCRYPT_MODE, key, random);

        PBES2Parameters algParams =
            new PBES2Parameters(
                new KeyDerivationFunc(
                    PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount)),
                new EncryptionScheme(
                    keyEncAlgorithm,
                    ASN1Primitive.fromByteArray(cipher.getParameters().getEncoded())));

        encryptionAlg = new AlgorithmIdentifier(algorithm, algParams);
      } else {
        throw new OperatorCreationException("unrecognised algorithm");
      }

      return new OutputEncryptor() {
        public AlgorithmIdentifier getAlgorithmIdentifier() {
          return encryptionAlg;
        }

        public OutputStream getOutputStream(OutputStream out) {
          return new CipherOutputStream(out, cipher);
        }

        public GenericKey getKey() {
          if (isPKCS12(encryptionAlg.getAlgorithm())) {
            return new GenericKey(
                encryptionAlg, PBEParametersGenerator.PKCS5PasswordToBytes(password));
          } else {
            return new GenericKey(
                encryptionAlg, PBEParametersGenerator.PKCS12PasswordToBytes(password));
          }
        }
      };
    } catch (Exception e) {
      throw new OperatorCreationException("unable to create OutputEncryptor: " + e.getMessage(), e);
    }
  }