From 12692e66cb04afcf2dfa16f38b5e9a9f0ff0ce69 Mon Sep 17 00:00:00 2001
From: Jesus Federico <jesus@123it.ca>
Date: Fri, 29 Aug 2014 10:47:46 -0400
Subject: [PATCH] Updated OAuth library

---
 .../org/bigbluebutton/ToolController.groovy   |   53 +-
 .../java/net/oauth/ConsumerProperties.java    |  260 +--
 bbb-lti/src/java/net/oauth/OAuth.java         |  703 ++++----
 bbb-lti/src/java/net/oauth/OAuthAccessor.java |  200 +--
 bbb-lti/src/java/net/oauth/OAuthConsumer.java |  138 +-
 bbb-lti/src/java/net/oauth/OAuthMessage.java  |  941 +++++------
 .../java/net/oauth/OAuthProblemException.java |  209 ++-
 .../java/net/oauth/OAuthServiceProvider.java  |   82 +-
 .../src/java/net/oauth/ParameterStyle.java    |   64 +-
 .../java/net/oauth/SimpleOAuthValidator.java  |    4 +-
 .../net/oauth/client/ExcerptInputStream.java  |  166 +-
 .../java/net/oauth/client/OAuthClient.java    |  632 ++++----
 .../oauth/client/OAuthResponseMessage.java    |  232 +--
 .../net/oauth/client/URLConnectionClient.java |  244 +--
 .../java/net/oauth/consumer.properties.sample |   32 +-
 .../src/java/net/oauth/http/HttpClient.java   |  110 +-
 .../src/java/net/oauth/http/HttpMessage.java  |  382 +++--
 .../net/oauth/http/HttpMessageDecoder.java    |  190 +--
 .../net/oauth/http/HttpResponseMessage.java   |  118 +-
 .../net/oauth/server/HttpRequestMessage.java  |  184 +--
 .../java/net/oauth/server/OAuthServlet.java   |  317 ++--
 .../src/java/net/oauth/signature/Base64.java  | 1428 ++++++++---------
 .../java/net/oauth/signature/HMAC_SHA1.java   |  204 +--
 .../oauth/signature/OAuthSignatureMethod.java |    1 -
 .../java/net/oauth/signature/PLAINTEXT.java   |  128 +-
 .../java/net/oauth/signature/RSA_SHA1.java    |  211 ++-
 .../net/oauth/signature/pem/Asn1Object.java   |  300 ++--
 .../net/oauth/signature/pem/DerParser.java    |  340 ++--
 .../net/oauth/signature/pem/PEMReader.java    |  268 ++--
 .../signature/pem/PKCS1EncodedKeySpec.java    |  232 +--
 30 files changed, 4353 insertions(+), 4020 deletions(-)

diff --git a/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy b/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy
index e31091d955..36fcb2f105 100644
--- a/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy
+++ b/bbb-lti/grails-app/controllers/org/bigbluebutton/ToolController.groovy
@@ -57,7 +57,6 @@ class ToolController {
             def endPoint = (request.isSecure()?"https":"http") + "://" + ltiService.endPoint + "/" + grailsApplication.metadata['app.name'] + "/" + params.get("controller") + (params.get("format") != null? "." + params.get("format"): "")
             Map<String, String> result = new HashMap<String, String>()
             ArrayList<String> missingParams = new ArrayList<String>()
-            log.debug "Checking for required parameters"
 
             if (hasAllRequiredParams(params, missingParams)) {
                 def sanitizedParams = sanitizePrametersForBaseString(params)
@@ -283,7 +282,6 @@ class ToolController {
      * @return the key:val pairs needed for Basic LTI
      */
     private Properties sanitizePrametersForBaseString(params) {
-        log.debug params
         Properties reqProp = new Properties();
         for (String key : params.keySet()) {
             if (key == "action" || key == "controller" || key == "format") {
@@ -299,7 +297,6 @@ class ToolController {
 
             reqProp.setProperty(key, params.get(key));
         }
-
         return reqProp
     }
 
@@ -309,20 +306,22 @@ class ToolController {
      * @param missingParams - a list of missing parameters
      * @return - true if all required parameters have been passed in
      */
-    private boolean hasAllRequiredParams(Object params, Object missingParams) {
+    private boolean hasAllRequiredParams(Map<String, String> params, ArrayList<String> missingParams) {
+        log.debug "Checking for required parameters"
+
         boolean hasAllParams = true
-        if (! ((Map<String, String>)params).containsKey(Parameter.CONSUMER_ID)) {
-            ((ArrayList<String>)missingParams).add(Parameter.CONSUMER_ID);
+        if ( !params.containsKey(Parameter.CONSUMER_ID) ) {
+            missingParams.add(Parameter.CONSUMER_ID);
             hasAllParams = false;
         }
 
-        if (! ((Map<String, String>)params).containsKey(Parameter.OAUTH_SIGNATURE)) {
-            ((ArrayList<String>)missingParams).add(Parameter.OAUTH_SIGNATURE);
+        if ( !params.containsKey(Parameter.OAUTH_SIGNATURE)) {
+            missingParams.add(Parameter.OAUTH_SIGNATURE);
             hasAllParams = false;
         }
 
-        if (! ((Map<String, String>)params).containsKey(Parameter.RESOURCE_LINK_ID)) {
-            ((ArrayList<String>)missingParams).add(Parameter.RESOURCE_LINK_ID);
+        if ( !params.containsKey(Parameter.RESOURCE_LINK_ID) ) {
+            missingParams.add(Parameter.RESOURCE_LINK_ID);
             hasAllParams = false;
         }
 
@@ -338,18 +337,28 @@ class ToolController {
      * @param signature - the passed in signature calculated from the client
      * @return - TRUE if the signatures matches the calculated signature
      */
-    private boolean checkValidSignature(String method, String URL, String conSecret, Object postProp, String signature) {
-		log.debug( "Starting checkValidSignature()" )
-        OAuthMessage oam = new OAuthMessage(method, URL, ((Properties)postProp).entrySet())
-		log.debug( "OAuthMessage oam = " + oam.toString() )
-        HMAC_SHA1 hmac = new HMAC_SHA1()
-		log.debug( "HMAC_SHA1 hmac = " + hmac.toString() )
-        hmac.setConsumerSecret(conSecret)
-
-        log.debug("Base Message String = [ " + hmac.getBaseString(oam) + " ]\n")
-        String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
-        log.debug("Calculated: " + calculatedSignature + " Received: " + signature)
-        return calculatedSignature.equals(signature)
+    private boolean checkValidSignature(String method, String url, String conSecret, Properties postProp, String signature) {
+        def validSignature = false
+
+        try {
+            OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet())
+            //log.debug "OAuthMessage oam = " + oam.toString()
+
+            HMAC_SHA1 hmac = new HMAC_SHA1()
+            //log.debug "HMAC_SHA1 hmac = " + hmac.toString()
+
+            hmac.setConsumerSecret(conSecret)
+
+            log.debug "Base Message String = [ " + hmac.getBaseString(oam) + " ]\n"
+            String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
+            log.debug "Calculated: " + calculatedSignature + " Received: " + signature
+
+            validSignature = calculatedSignature.equals(signature)
+        } catch( Exception e ) {
+            log.debug "Exception error: " + e.message
+        }
+
+        return validSignature
     }
 
     private String getCartridgeXML(){
diff --git a/bbb-lti/src/java/net/oauth/ConsumerProperties.java b/bbb-lti/src/java/net/oauth/ConsumerProperties.java
index 23dc2271cd..7da36daa8a 100644
--- a/bbb-lti/src/java/net/oauth/ConsumerProperties.java
+++ b/bbb-lti/src/java/net/oauth/ConsumerProperties.java
@@ -1,130 +1,130 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * A pool of OAuthConsumers that are constructed from Properties. Each consumer
- * has a name, which is a property of the OAuthConsumer. Other properties come
- * from Properties whose names are prefixed with the consumer's name. For
- * example, a consumer's credentials come from properties named
- * [name].consumerKey and [name].consumerSecret.
- * 
- * @author John Kristian
- */
-public class ConsumerProperties {
-
-    public static URL getResource(String name, ClassLoader loader)
-            throws IOException {
-        URL resource = loader.getResource(name);
-        if (resource == null) {
-            throw new IOException("resource not found: " + name);
-        }
-        return resource;
-    }
-
-    public static Properties getProperties(URL source) throws IOException {
-        InputStream input = source.openStream();
-        try {
-            Properties p = new Properties();
-            p.load(input);
-            return p;
-        } finally {
-            input.close();
-        }
-    }
-
-    public ConsumerProperties(String resourceName, ClassLoader loader)
-            throws IOException {
-        this(getProperties(getResource(resourceName, loader)));
-    }
-
-    public ConsumerProperties(Properties consumerProperties) {
-        this.consumerProperties = consumerProperties;
-    }
-
-    private final Properties consumerProperties;
-
-    private final Map<String, OAuthConsumer> pool = new HashMap<String, OAuthConsumer>();
-
-    /** Get the consumer with the given name. */
-    public OAuthConsumer getConsumer(String name) throws MalformedURLException {
-        OAuthConsumer consumer;
-        synchronized (pool) {
-            consumer = pool.get(name);
-        }
-        if (consumer == null) {
-            consumer = newConsumer(name);
-        }
-        synchronized (pool) {
-            OAuthConsumer first = pool.get(name);
-            if (first == null) {
-                pool.put(name, consumer);
-            } else {
-                /*
-                 * Another thread just constructed an identical OAuthConsumer.
-                 * Use that one (and discard the one we just constructed).
-                 */
-                consumer = first;
-            }
-        }
-        return consumer;
-    }
-
-    protected OAuthConsumer newConsumer(String name)
-            throws MalformedURLException {
-        String base = consumerProperties.getProperty(name
-                + ".serviceProvider.baseURL");
-        URL baseURL = (base == null) ? null : new URL(base);
-        OAuthServiceProvider serviceProvider = new OAuthServiceProvider(getURL(
-                baseURL, name + ".serviceProvider.requestTokenURL"), getURL(
-                baseURL, name + ".serviceProvider.userAuthorizationURL"),
-                getURL(baseURL, name + ".serviceProvider.accessTokenURL"));
-        OAuthConsumer consumer = new OAuthConsumer(consumerProperties
-                .getProperty(name + ".callbackURL"), consumerProperties
-                .getProperty(name + ".consumerKey"), consumerProperties
-                .getProperty(name + ".consumerSecret"), serviceProvider);
-        consumer.setProperty("name", name);
-        if (baseURL != null) {
-            consumer.setProperty("serviceProvider.baseURL", baseURL);
-        }
-        for (Map.Entry prop : consumerProperties.entrySet()) {
-            String propName = (String) prop.getKey();
-            if (propName.startsWith(name + ".consumer.")) {
-                String c = propName.substring(name.length() + 10);
-                consumer.setProperty(c, prop.getValue());
-            }
-        }
-        return consumer;
-    }
-
-    private String getURL(URL base, String name) throws MalformedURLException {
-        String url = consumerProperties.getProperty(name);
-        if (base != null) {
-            url = (new URL(base, url)).toExternalForm();
-        }
-        return url;
-    }
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * A pool of OAuthConsumers that are constructed from Properties. Each consumer
+ * has a name, which is a property of the OAuthConsumer. Other properties come
+ * from Properties whose names are prefixed with the consumer's name. For
+ * example, a consumer's credentials come from properties named
+ * [name].consumerKey and [name].consumerSecret.
+ * 
+ * @author John Kristian
+ */
+public class ConsumerProperties {
+
+    public static URL getResource(String name, ClassLoader loader)
+            throws IOException {
+        URL resource = loader.getResource(name);
+        if (resource == null) {
+            throw new IOException("resource not found: " + name);
+        }
+        return resource;
+    }
+
+    public static Properties getProperties(URL source) throws IOException {
+        InputStream input = source.openStream();
+        try {
+            Properties p = new Properties();
+            p.load(input);
+            return p;
+        } finally {
+            input.close();
+        }
+    }
+
+    public ConsumerProperties(String resourceName, ClassLoader loader)
+            throws IOException {
+        this(getProperties(getResource(resourceName, loader)));
+    }
+
+    public ConsumerProperties(Properties consumerProperties) {
+        this.consumerProperties = consumerProperties;
+    }
+
+    private final Properties consumerProperties;
+
+    private final Map<String, OAuthConsumer> pool = new HashMap<String, OAuthConsumer>();
+
+    /** Get the consumer with the given name. */
+    public OAuthConsumer getConsumer(String name) throws MalformedURLException {
+        OAuthConsumer consumer;
+        synchronized (pool) {
+            consumer = pool.get(name);
+        }
+        if (consumer == null) {
+            consumer = newConsumer(name);
+        }
+        synchronized (pool) {
+            OAuthConsumer first = pool.get(name);
+            if (first == null) {
+                pool.put(name, consumer);
+            } else {
+                /*
+                 * Another thread just constructed an identical OAuthConsumer.
+                 * Use that one (and discard the one we just constructed).
+                 */
+                consumer = first;
+            }
+        }
+        return consumer;
+    }
+
+    protected OAuthConsumer newConsumer(String name)
+            throws MalformedURLException {
+        String base = consumerProperties.getProperty(name
+                + ".serviceProvider.baseURL");
+        URL baseURL = (base == null) ? null : new URL(base);
+        OAuthServiceProvider serviceProvider = new OAuthServiceProvider(getURL(
+                baseURL, name + ".serviceProvider.requestTokenURL"), getURL(
+                baseURL, name + ".serviceProvider.userAuthorizationURL"),
+                getURL(baseURL, name + ".serviceProvider.accessTokenURL"));
+        OAuthConsumer consumer = new OAuthConsumer(consumerProperties
+                .getProperty(name + ".callbackURL"), consumerProperties
+                .getProperty(name + ".consumerKey"), consumerProperties
+                .getProperty(name + ".consumerSecret"), serviceProvider);
+        consumer.setProperty("name", name);
+        if (baseURL != null) {
+            consumer.setProperty("serviceProvider.baseURL", baseURL);
+        }
+        for (Map.Entry prop : consumerProperties.entrySet()) {
+            String propName = (String) prop.getKey();
+            if (propName.startsWith(name + ".consumer.")) {
+                String c = propName.substring(name.length() + 10);
+                consumer.setProperty(c, prop.getValue());
+            }
+        }
+        return consumer;
+    }
+
+    private String getURL(URL base, String name) throws MalformedURLException {
+        String url = consumerProperties.getProperty(name);
+        if (base != null) {
+            url = (new URL(base, url)).toExternalForm();
+        }
+        return url;
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/OAuth.java b/bbb-lti/src/java/net/oauth/OAuth.java
index ea71b5d867..e042aa3319 100644
--- a/bbb-lti/src/java/net/oauth/OAuth.java
+++ b/bbb-lti/src/java/net/oauth/OAuth.java
@@ -1,351 +1,352 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author John Kristian
- */
-public class OAuth {
-
-    public static final String VERSION_1_0 = "1.0";
-
-    /** The encoding used to represent characters as bytes. */
-    public static final String ENCODING = "UTF-8";
-
-    /** The MIME type for a sequence of OAuth parameters. */
-    public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
-
-    public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
-    public static final String OAUTH_TOKEN = "oauth_token";
-    public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";
-    public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
-    public static final String OAUTH_SIGNATURE = "oauth_signature";
-    public static final String OAUTH_TIMESTAMP = "oauth_timestamp";
-    public static final String OAUTH_NONCE = "oauth_nonce";
-    public static final String OAUTH_VERSION = "oauth_version";
-    public static final String OAUTH_CALLBACK = "oauth_callback";
-
-    public static final String HMAC_SHA1 = "HMAC-SHA1";
-    public static final String RSA_SHA1 = "RSA-SHA1";
-
-    /**
-     * Strings used for <a href="http://wiki.oauth.net/ProblemReporting">problem
-     * reporting</a>.
-     */
-    public static class Problems {
-        public static final String VERSION_REJECTED = "version_rejected";
-        public static final String PARAMETER_ABSENT = "parameter_absent";
-        public static final String PARAMETER_REJECTED = "parameter_rejected";
-        public static final String TIMESTAMP_REFUSED = "timestamp_refused";
-        public static final String NONCE_USED = "nonce_used";
-        public static final String SIGNATURE_METHOD_REJECTED = "signature_method_rejected";
-        public static final String SIGNATURE_INVALID = "signature_invalid";
-        public static final String CONSUMER_KEY_UNKNOWN = "consumer_key_unknown";
-        public static final String CONSUMER_KEY_REJECTED = "consumer_key_rejected";
-        public static final String CONSUMER_KEY_REFUSED = "consumer_key_refused";
-        public static final String TOKEN_USED = "token_used";
-        public static final String TOKEN_EXPIRED = "token_expired";
-        public static final String TOKEN_REVOKED = "token_revoked";
-        public static final String TOKEN_REJECTED = "token_rejected";
-        public static final String ADDITIONAL_AUTHORIZATION_REQUIRED = "additional_authorization_required";
-        public static final String PERMISSION_UNKNOWN = "permission_unknown";
-        public static final String PERMISSION_DENIED = "permission_denied";
-        public static final String USER_REFUSED = "user_refused";
-
-        public static final String OAUTH_ACCEPTABLE_VERSIONS = "oauth_acceptable_versions";
-        public static final String OAUTH_ACCEPTABLE_TIMESTAMPS = "oauth_acceptable_timestamps";
-        public static final String OAUTH_PARAMETERS_ABSENT = "oauth_parameters_absent";
-        public static final String OAUTH_PARAMETERS_REJECTED = "oauth_parameters_rejected";
-        public static final String OAUTH_PROBLEM_ADVICE = "oauth_problem_advice";
-
-        /**
-         * A map from an <a
-         * href="http://wiki.oauth.net/ProblemReporting">oauth_problem</a> value to
-         * the appropriate HTTP response code.
-         */
-        public static final Map<String, Integer> TO_HTTP_CODE = mapToHttpCode();
-
-        private static Map<String, Integer> mapToHttpCode() {
-            Integer badRequest = new Integer(400);
-            Integer unauthorized = new Integer(401);
-            Integer serviceUnavailable = new Integer(503);
-            Map<String, Integer> map = new HashMap<String, Integer>();
-
-            map.put(Problems.VERSION_REJECTED, badRequest);
-            map.put(Problems.PARAMETER_ABSENT, badRequest);
-            map.put(Problems.PARAMETER_REJECTED, badRequest);
-            map.put(Problems.TIMESTAMP_REFUSED, badRequest);
-            map.put(Problems.SIGNATURE_METHOD_REJECTED, badRequest);
-
-            map.put(Problems.NONCE_USED, unauthorized);
-            map.put(Problems.TOKEN_USED, unauthorized);
-            map.put(Problems.TOKEN_EXPIRED, unauthorized);
-            map.put(Problems.TOKEN_REVOKED, unauthorized);
-            map.put(Problems.TOKEN_REJECTED, unauthorized);
-            map.put("token_not_authorized", unauthorized);
-            map.put(Problems.SIGNATURE_INVALID, unauthorized);
-            map.put(Problems.CONSUMER_KEY_UNKNOWN, unauthorized);
-            map.put(Problems.CONSUMER_KEY_REJECTED, unauthorized);
-            map.put(Problems.ADDITIONAL_AUTHORIZATION_REQUIRED, unauthorized);
-            map.put(Problems.PERMISSION_UNKNOWN, unauthorized);
-            map.put(Problems.PERMISSION_DENIED, unauthorized);
-
-            map.put(Problems.USER_REFUSED, serviceUnavailable);
-            map.put(Problems.CONSUMER_KEY_REFUSED, serviceUnavailable);
-            return Collections.unmodifiableMap(map);
-        }
-
-    }
-    
-    /** Return true if the given Content-Type header means FORM_ENCODED. */
-    public static boolean isFormEncoded(String contentType) {
-        if (contentType == null) {
-            return false;
-        }
-        int semi = contentType.indexOf(";");
-        if (semi >= 0) {
-            contentType = contentType.substring(0, semi);
-        }
-        return FORM_ENCODED.equalsIgnoreCase(contentType.trim());
-    }
-
-    /**
-     * Construct a form-urlencoded document containing the given sequence of
-     * name/value pairs. Use OAuth percent encoding (not exactly the encoding
-     * mandated by HTTP).
-     */
-    public static String formEncode(Iterable<? extends Map.Entry> parameters)
-            throws IOException {
-        ByteArrayOutputStream b = new ByteArrayOutputStream();
-        formEncode(parameters, b);
-        return new String(b.toByteArray());
-    }
-
-    /**
-     * Write a form-urlencoded document into the given stream, containing the
-     * given sequence of name/value pairs.
-     */
-    public static void formEncode(Iterable<? extends Map.Entry> parameters,
-            OutputStream into) throws IOException {
-        if (parameters != null) {
-            boolean first = true;
-            for (Map.Entry parameter : parameters) {
-                if (first) {
-                    first = false;
-                } else {
-                    into.write('&');
-                }
-                into.write(percentEncode(toString(parameter.getKey()))
-                        .getBytes());
-                into.write('=');
-                into.write(percentEncode(toString(parameter.getValue()))
-                        .getBytes());
-            }
-        }
-    }
-
-    /** Parse a form-urlencoded document. */
-    public static List<Parameter> decodeForm(String form) {
-        List<Parameter> list = new ArrayList<Parameter>();
-        if (!isEmpty(form)) {
-            for (String nvp : form.split("\\&")) {
-                int equals = nvp.indexOf('=');
-                String name;
-                String value;
-                if (equals < 0) {
-                    name = decodePercent(nvp);
-                    value = null;
-                } else {
-                    name = decodePercent(nvp.substring(0, equals));
-                    value = decodePercent(nvp.substring(equals + 1));
-                }
-                list.add(new Parameter(name, value));
-            }
-        }
-        return list;
-    }
-
-    /** Construct a &-separated list of the given values, percentEncoded. */
-    public static String percentEncode(Iterable values) {
-        StringBuilder p = new StringBuilder();
-        for (Object v : values) {
-            if (p.length() > 0) {
-                p.append("&");
-            }
-            p.append(OAuth.percentEncode(toString(v)));
-        }
-        return p.toString();
-    }
-
-    public static String percentEncode(String s) {
-        if (s == null) {
-            return "";
-        }
-        try {
-            return URLEncoder.encode(s, ENCODING)
-                    // OAuth encodes some characters differently:
-                    .replace("+", "%20").replace("*", "%2A")
-                    .replace("%7E", "~");
-            // This could be done faster with more hand-crafted code.
-        } catch (UnsupportedEncodingException wow) {
-            throw new RuntimeException(wow.getMessage(), wow);
-        }
-    }
-
-    public static String decodePercent(String s) {
-        try {
-            return URLDecoder.decode(s, ENCODING);
-            // This implements http://oauth.pbwiki.com/FlexibleDecoding
-        } catch (java.io.UnsupportedEncodingException wow) {
-            throw new RuntimeException(wow.getMessage(), wow);
-        }
-    }
-
-    /**
-     * Construct a Map containing a copy of the given parameters. If several
-     * parameters have the same name, the Map will contain the first value,
-     * only.
-     */
-    public static Map<String, String> newMap(Iterable<? extends Map.Entry> from) {
-        Map<String, String> map = new HashMap<String, String>();
-        if (from != null) {
-            for (Map.Entry f : from) {
-                String key = toString(f.getKey());
-                if (!map.containsKey(key)) {
-                    map.put(key, toString(f.getValue()));
-                }
-            }
-        }
-        return map;
-    }
-
-    /** Construct a list of Parameters from name, value, name, value... */
-    public static List<Parameter> newList(String... parameters) {
-        List<Parameter> list = new ArrayList<Parameter>(parameters.length / 2);
-        for (int p = 0; p + 1 < parameters.length; p += 2) {
-            list.add(new Parameter(parameters[p], parameters[p + 1]));
-        }
-        return list;
-    }
-
-    /** A name/value pair. */
-    public static class Parameter implements Map.Entry<String, String> {
-
-        public Parameter(String key, String value) {
-            this.key = key;
-            this.value = value;
-        }
-
-        private final String key;
-
-        private String value;
-
-        public String getKey() {
-            return key;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        public String setValue(String value) {
-            try {
-                return this.value;
-            } finally {
-                this.value = value;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return percentEncode(getKey()) + '=' + percentEncode(getValue());
-        }
-
-        @Override
-        public int hashCode()
-        {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((key == null) ? 0 : key.hashCode());
-            result = prime * result + ((value == null) ? 0 : value.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj)
-        {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            final Parameter that = (Parameter) obj;
-            if (key == null) {
-                if (that.key != null)
-                    return false;
-            } else if (!key.equals(that.key))
-                return false;
-            if (value == null) {
-                if (that.value != null)
-                    return false;
-            } else if (!value.equals(that.value))
-                return false;
-            return true;
-        }
-    }
-
-    private static final String toString(Object from) {
-        return (from == null) ? null : from.toString();
-    }
-
-    /**
-     * Construct a URL like the given one, but with the given parameters added
-     * to its query string.
-     */
-    public static String addParameters(String url, String... parameters)
-            throws IOException {
-        return addParameters(url, newList(parameters));
-    }
-
-    public static String addParameters(String url,
-            Iterable<? extends Map.Entry<String, String>> parameters)
-            throws IOException {
-        String form = formEncode(parameters);
-        if (form == null || form.length() <= 0) {
-            return url;
-        } else {
-            return url + ((url.indexOf("?") < 0) ? '?' : '&') + form;
-        }
-    }
-
-    public static boolean isEmpty(String str) {
-	return (str == null) || (str.length() == 0);
-    }
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author John Kristian
+ */
+public class OAuth {
+
+    public static final String VERSION_1_0 = "1.0";
+
+    /** The encoding used to represent characters as bytes. */
+    public static final String ENCODING = "UTF-8";
+
+    /** The MIME type for a sequence of OAuth parameters. */
+    public static final String FORM_ENCODED = "application/x-www-form-urlencoded";
+
+    public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
+    public static final String OAUTH_TOKEN = "oauth_token";
+    public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret";
+    public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
+    public static final String OAUTH_SIGNATURE = "oauth_signature";
+    public static final String OAUTH_TIMESTAMP = "oauth_timestamp";
+    public static final String OAUTH_NONCE = "oauth_nonce";
+    public static final String OAUTH_VERSION = "oauth_version";
+    public static final String OAUTH_CALLBACK = "oauth_callback";
+    public static final String OAUTH_BODY_HASH = "oauth_body_hash"; 
+
+    public static final String HMAC_SHA1 = "HMAC-SHA1";
+    public static final String RSA_SHA1 = "RSA-SHA1";
+
+    /**
+     * Strings used for <a href="http://wiki.oauth.net/ProblemReporting">problem
+     * reporting</a>.
+     */
+    public static class Problems {
+        public static final String VERSION_REJECTED = "version_rejected";
+        public static final String PARAMETER_ABSENT = "parameter_absent";
+        public static final String PARAMETER_REJECTED = "parameter_rejected";
+        public static final String TIMESTAMP_REFUSED = "timestamp_refused";
+        public static final String NONCE_USED = "nonce_used";
+        public static final String SIGNATURE_METHOD_REJECTED = "signature_method_rejected";
+        public static final String SIGNATURE_INVALID = "signature_invalid";
+        public static final String CONSUMER_KEY_UNKNOWN = "consumer_key_unknown";
+        public static final String CONSUMER_KEY_REJECTED = "consumer_key_rejected";
+        public static final String CONSUMER_KEY_REFUSED = "consumer_key_refused";
+        public static final String TOKEN_USED = "token_used";
+        public static final String TOKEN_EXPIRED = "token_expired";
+        public static final String TOKEN_REVOKED = "token_revoked";
+        public static final String TOKEN_REJECTED = "token_rejected";
+        public static final String ADDITIONAL_AUTHORIZATION_REQUIRED = "additional_authorization_required";
+        public static final String PERMISSION_UNKNOWN = "permission_unknown";
+        public static final String PERMISSION_DENIED = "permission_denied";
+        public static final String USER_REFUSED = "user_refused";
+
+        public static final String OAUTH_ACCEPTABLE_VERSIONS = "oauth_acceptable_versions";
+        public static final String OAUTH_ACCEPTABLE_TIMESTAMPS = "oauth_acceptable_timestamps";
+        public static final String OAUTH_PARAMETERS_ABSENT = "oauth_parameters_absent";
+        public static final String OAUTH_PARAMETERS_REJECTED = "oauth_parameters_rejected";
+        public static final String OAUTH_PROBLEM_ADVICE = "oauth_problem_advice";
+
+        /**
+         * A map from an <a
+         * href="http://wiki.oauth.net/ProblemReporting">oauth_problem</a> value to
+         * the appropriate HTTP response code.
+         */
+        public static final Map<String, Integer> TO_HTTP_CODE = mapToHttpCode();
+
+        private static Map<String, Integer> mapToHttpCode() {
+            Integer badRequest = new Integer(400);
+            Integer unauthorized = new Integer(401);
+            Integer serviceUnavailable = new Integer(503);
+            Map<String, Integer> map = new HashMap<String, Integer>();
+
+            map.put(Problems.VERSION_REJECTED, badRequest);
+            map.put(Problems.PARAMETER_ABSENT, badRequest);
+            map.put(Problems.PARAMETER_REJECTED, badRequest);
+            map.put(Problems.TIMESTAMP_REFUSED, badRequest);
+            map.put(Problems.SIGNATURE_METHOD_REJECTED, badRequest);
+
+            map.put(Problems.NONCE_USED, unauthorized);
+            map.put(Problems.TOKEN_USED, unauthorized);
+            map.put(Problems.TOKEN_EXPIRED, unauthorized);
+            map.put(Problems.TOKEN_REVOKED, unauthorized);
+            map.put(Problems.TOKEN_REJECTED, unauthorized);
+            map.put("token_not_authorized", unauthorized);
+            map.put(Problems.SIGNATURE_INVALID, unauthorized);
+            map.put(Problems.CONSUMER_KEY_UNKNOWN, unauthorized);
+            map.put(Problems.CONSUMER_KEY_REJECTED, unauthorized);
+            map.put(Problems.ADDITIONAL_AUTHORIZATION_REQUIRED, unauthorized);
+            map.put(Problems.PERMISSION_UNKNOWN, unauthorized);
+            map.put(Problems.PERMISSION_DENIED, unauthorized);
+
+            map.put(Problems.USER_REFUSED, serviceUnavailable);
+            map.put(Problems.CONSUMER_KEY_REFUSED, serviceUnavailable);
+            return Collections.unmodifiableMap(map);
+        }
+
+    }
+    
+    /** Return true if the given Content-Type header means FORM_ENCODED. */
+    public static boolean isFormEncoded(String contentType) {
+        if (contentType == null) {
+            return false;
+        }
+        int semi = contentType.indexOf(";");
+        if (semi >= 0) {
+            contentType = contentType.substring(0, semi);
+        }
+        return FORM_ENCODED.equalsIgnoreCase(contentType.trim());
+    }
+
+    /**
+     * Construct a form-urlencoded document containing the given sequence of
+     * name/value pairs. Use OAuth percent encoding (not exactly the encoding
+     * mandated by HTTP).
+     */
+    public static String formEncode(Iterable<? extends Map.Entry> parameters)
+            throws IOException {
+        ByteArrayOutputStream b = new ByteArrayOutputStream();
+        formEncode(parameters, b);
+        return new String(b.toByteArray());
+    }
+
+    /**
+     * Write a form-urlencoded document into the given stream, containing the
+     * given sequence of name/value pairs.
+     */
+    public static void formEncode(Iterable<? extends Map.Entry> parameters,
+            OutputStream into) throws IOException {
+        if (parameters != null) {
+            boolean first = true;
+            for (Map.Entry parameter : parameters) {
+                if (first) {
+                    first = false;
+                } else {
+                    into.write('&');
+                }
+                into.write(percentEncode(toString(parameter.getKey()))
+                        .getBytes());
+                into.write('=');
+                into.write(percentEncode(toString(parameter.getValue()))
+                        .getBytes());
+            }
+        }
+    }
+
+    /** Parse a form-urlencoded document. */
+    public static List<Parameter> decodeForm(String form) {
+        List<Parameter> list = new ArrayList<Parameter>();
+        if (!isEmpty(form)) {
+            for (String nvp : form.split("\\&")) {
+                int equals = nvp.indexOf('=');
+                String name;
+                String value;
+                if (equals < 0) {
+                    name = decodePercent(nvp);
+                    value = null;
+                } else {
+                    name = decodePercent(nvp.substring(0, equals));
+                    value = decodePercent(nvp.substring(equals + 1));
+                }
+                list.add(new Parameter(name, value));
+            }
+        }
+        return list;
+    }
+
+    /** Construct a &-separated list of the given values, percentEncoded. */
+    public static String percentEncode(Iterable values) {
+        StringBuilder p = new StringBuilder();
+        for (Object v : values) {
+            if (p.length() > 0) {
+                p.append("&");
+            }
+            p.append(OAuth.percentEncode(toString(v)));
+        }
+        return p.toString();
+    }
+
+    public static String percentEncode(String s) {
+        if (s == null) {
+            return "";
+        }
+        try {
+            return URLEncoder.encode(s, ENCODING)
+                    // OAuth encodes some characters differently:
+                    .replace("+", "%20").replace("*", "%2A")
+                    .replace("%7E", "~");
+            // This could be done faster with more hand-crafted code.
+        } catch (UnsupportedEncodingException wow) {
+            throw new RuntimeException(wow.getMessage(), wow);
+        }
+    }
+
+    public static String decodePercent(String s) {
+        try {
+            return URLDecoder.decode(s, ENCODING);
+            // This implements http://oauth.pbwiki.com/FlexibleDecoding
+        } catch (java.io.UnsupportedEncodingException wow) {
+            throw new RuntimeException(wow.getMessage(), wow);
+        }
+    }
+
+    /**
+     * Construct a Map containing a copy of the given parameters. If several
+     * parameters have the same name, the Map will contain the first value,
+     * only.
+     */
+    public static Map<String, String> newMap(Iterable<? extends Map.Entry> from) {
+        Map<String, String> map = new HashMap<String, String>();
+        if (from != null) {
+            for (Map.Entry f : from) {
+                String key = toString(f.getKey());
+                if (!map.containsKey(key)) {
+                    map.put(key, toString(f.getValue()));
+                }
+            }
+        }
+        return map;
+    }
+
+    /** Construct a list of Parameters from name, value, name, value... */
+    public static List<Parameter> newList(String... parameters) {
+        List<Parameter> list = new ArrayList<Parameter>(parameters.length / 2);
+        for (int p = 0; p + 1 < parameters.length; p += 2) {
+            list.add(new Parameter(parameters[p], parameters[p + 1]));
+        }
+        return list;
+    }
+
+    /** A name/value pair. */
+    public static class Parameter implements Map.Entry<String, String> {
+
+        public Parameter(String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        private final String key;
+
+        private String value;
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public String setValue(String value) {
+            try {
+                return this.value;
+            } finally {
+                this.value = value;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return percentEncode(getKey()) + '=' + percentEncode(getValue());
+        }
+
+        @Override
+        public int hashCode()
+        {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((key == null) ? 0 : key.hashCode());
+            result = prime * result + ((value == null) ? 0 : value.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            final Parameter that = (Parameter) obj;
+            if (key == null) {
+                if (that.key != null)
+                    return false;
+            } else if (!key.equals(that.key))
+                return false;
+            if (value == null) {
+                if (that.value != null)
+                    return false;
+            } else if (!value.equals(that.value))
+                return false;
+            return true;
+        }
+    }
+
+    private static final String toString(Object from) {
+        return (from == null) ? null : from.toString();
+    }
+
+    /**
+     * Construct a URL like the given one, but with the given parameters added
+     * to its query string.
+     */
+    public static String addParameters(String url, String... parameters)
+            throws IOException {
+        return addParameters(url, newList(parameters));
+    }
+
+    public static String addParameters(String url,
+            Iterable<? extends Map.Entry<String, String>> parameters)
+            throws IOException {
+        String form = formEncode(parameters);
+        if (form == null || form.length() <= 0) {
+            return url;
+        } else {
+            return url + ((url.indexOf("?") < 0) ? '?' : '&') + form;
+        }
+    }
+
+    public static boolean isEmpty(String str) {
+	return (str == null) || (str.length() == 0);
+    }
+}
diff --git a/bbb-lti/src/java/net/oauth/OAuthAccessor.java b/bbb-lti/src/java/net/oauth/OAuthAccessor.java
index b2545eb7cb..198fc84f13 100644
--- a/bbb-lti/src/java/net/oauth/OAuthAccessor.java
+++ b/bbb-lti/src/java/net/oauth/OAuthAccessor.java
@@ -1,100 +1,100 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.net.URISyntaxException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Properties of one User of an OAuthConsumer. Properties may be added freely,
- * e.g. to support extensions.
- * 
- * @author John Kristian
- */
-public class OAuthAccessor implements Cloneable, Serializable {
-
-    private static final long serialVersionUID = 5590788443138352999L;
-
-    public final OAuthConsumer consumer;
-    public String requestToken;
-    public String accessToken;
-    public String tokenSecret;
-
-    public OAuthAccessor(OAuthConsumer consumer) {
-        this.consumer = consumer;
-        this.requestToken = null;
-        this.accessToken = null;
-        this.tokenSecret = null;
-    }
-
-    private final Map<String, Object> properties = new HashMap<String, Object>();
-
-    @Override
-    public OAuthAccessor clone() {
-        try {
-            return (OAuthAccessor) super.clone();
-        } catch (CloneNotSupportedException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public Object getProperty(String name) {
-        return properties.get(name);
-    }
-
-    public void setProperty(String name, Object value) {
-        properties.put(name, value);
-    }
-
-    /**
-     * Construct a request message containing the given parameters but no body.
-     * Don't send the message, merely construct it. The caller will ordinarily
-     * send it, for example by calling OAuthClient.invoke or access.
-     * 
-     * @param method
-     *            the HTTP request method. If this is null, use the default
-     *            method; that is getProperty("httpMethod") or (if that's null)
-     *            consumer.getProperty("httpMethod") or (if that's null)
-     *            OAuthMessage.GET.
-     */
-    public OAuthMessage newRequestMessage(String method, String url, Collection<? extends Map.Entry> parameters,
-            InputStream body) throws OAuthException, IOException, URISyntaxException {
-        if (method == null) {
-            method = (String) this.getProperty("httpMethod");
-            if (method == null) {
-                method = (String) this.consumer.getProperty("httpMethod");
-                if (method == null) {
-                    method = OAuthMessage.GET;
-                }
-            }
-        }
-        OAuthMessage message = new OAuthMessage(method, url, parameters, body);
-        message.addRequiredParameters(this);
-        return message;
-    }
-
-    public OAuthMessage newRequestMessage(String method, String url, Collection<? extends Map.Entry> parameters)
-            throws OAuthException, IOException, URISyntaxException {
-        return newRequestMessage(method, url, parameters, null);
-    }
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Properties of one User of an OAuthConsumer. Properties may be added freely,
+ * e.g. to support extensions.
+ * 
+ * @author John Kristian
+ */
+public class OAuthAccessor implements Cloneable, Serializable {
+
+    private static final long serialVersionUID = 5590788443138352999L;
+
+    public final OAuthConsumer consumer;
+    public String requestToken;
+    public String accessToken;
+    public String tokenSecret;
+
+    public OAuthAccessor(OAuthConsumer consumer) {
+        this.consumer = consumer;
+        this.requestToken = null;
+        this.accessToken = null;
+        this.tokenSecret = null;
+    }
+
+    private final Map<String, Object> properties = new HashMap<String, Object>();
+
+    @Override
+    public OAuthAccessor clone() {
+        try {
+            return (OAuthAccessor) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /**
+     * Construct a request message containing the given parameters but no body.
+     * Don't send the message, merely construct it. The caller will ordinarily
+     * send it, for example by calling OAuthClient.invoke or access.
+     * 
+     * @param method
+     *            the HTTP request method. If this is null, use the default
+     *            method; that is getProperty("httpMethod") or (if that's null)
+     *            consumer.getProperty("httpMethod") or (if that's null)
+     *            OAuthMessage.GET.
+     */
+    public OAuthMessage newRequestMessage(String method, String url, Collection<? extends Map.Entry> parameters,
+            InputStream body) throws OAuthException, IOException, URISyntaxException {
+        if (method == null) {
+            method = (String) this.getProperty("httpMethod");
+            if (method == null) {
+                method = (String) this.consumer.getProperty("httpMethod");
+                if (method == null) {
+                    method = OAuthMessage.GET;
+                }
+            }
+        }
+        OAuthMessage message = new OAuthMessage(method, url, parameters, body);
+        message.addRequiredParameters(this);
+        return message;
+    }
+
+    public OAuthMessage newRequestMessage(String method, String url, Collection<? extends Map.Entry> parameters)
+            throws OAuthException, IOException, URISyntaxException {
+        return newRequestMessage(method, url, parameters, null);
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/OAuthConsumer.java b/bbb-lti/src/java/net/oauth/OAuthConsumer.java
index 94d7809f12..d79a2d418a 100644
--- a/bbb-lti/src/java/net/oauth/OAuthConsumer.java
+++ b/bbb-lti/src/java/net/oauth/OAuthConsumer.java
@@ -1,69 +1,69 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import net.oauth.http.HttpMessage;
-
-/**
- * Properties of an OAuth Consumer. Properties may be added freely, e.g. to
- * support extensions.
- * 
- * @author John Kristian
- */
-public class OAuthConsumer implements Serializable {
-
-    private static final long serialVersionUID = -2258581186977818580L;
-
-    public final String callbackURL;
-    public final String consumerKey;
-    public final String consumerSecret;
-    public final OAuthServiceProvider serviceProvider;
-
-    public OAuthConsumer(String callbackURL, String consumerKey,
-            String consumerSecret, OAuthServiceProvider serviceProvider) {
-        this.callbackURL = callbackURL;
-        this.consumerKey = consumerKey;
-        this.consumerSecret = consumerSecret;
-        this.serviceProvider = serviceProvider;
-    }
-
-    private final Map<String, Object> properties = new HashMap<String, Object>();
-
-    public Object getProperty(String name) {
-        return properties.get(name);
-    }
-
-    public void setProperty(String name, Object value) {
-        properties.put(name, value);
-    }
-
-    /**
-     * The name of the property whose value is the Accept-Encoding header in
-     * HTTP requests.
-     */
-    public static final String ACCEPT_ENCODING = "HTTP.header." + HttpMessage.ACCEPT_ENCODING;
-
-    /**
-     * The name of the property whose value is the <a
-     * href="http://oauth.pbwiki.com/AccessorSecret">Accessor Secret</a>.
-     */
-    public static final String ACCESSOR_SECRET = "oauth_accessor_secret";
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import net.oauth.http.HttpMessage;
+
+/**
+ * Properties of an OAuth Consumer. Properties may be added freely, e.g. to
+ * support extensions.
+ * 
+ * @author John Kristian
+ */
+public class OAuthConsumer implements Serializable {
+
+    private static final long serialVersionUID = -2258581186977818580L;
+
+    public final String callbackURL;
+    public final String consumerKey;
+    public final String consumerSecret;
+    public final OAuthServiceProvider serviceProvider;
+
+    public OAuthConsumer(String callbackURL, String consumerKey,
+            String consumerSecret, OAuthServiceProvider serviceProvider) {
+        this.callbackURL = callbackURL;
+        this.consumerKey = consumerKey;
+        this.consumerSecret = consumerSecret;
+        this.serviceProvider = serviceProvider;
+    }
+
+    private final Map<String, Object> properties = new HashMap<String, Object>();
+
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /**
+     * The name of the property whose value is the Accept-Encoding header in
+     * HTTP requests.
+     */
+    public static final String ACCEPT_ENCODING = "HTTP.header." + HttpMessage.ACCEPT_ENCODING;
+
+    /**
+     * The name of the property whose value is the <a
+     * href="http://oauth.pbwiki.com/AccessorSecret">Accessor Secret</a>.
+     */
+    public static final String ACCESSOR_SECRET = "oauth_accessor_secret";
+
+}
diff --git a/bbb-lti/src/java/net/oauth/OAuthMessage.java b/bbb-lti/src/java/net/oauth/OAuthMessage.java
index a1efd88e2a..6c9fc29955 100644
--- a/bbb-lti/src/java/net/oauth/OAuthMessage.java
+++ b/bbb-lti/src/java/net/oauth/OAuthMessage.java
@@ -1,456 +1,485 @@
-/*
- * Copyright 2007, 2008 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import net.oauth.ParameterStyle;
-import net.oauth.http.HttpMessage;
-import net.oauth.signature.OAuthSignatureMethod;
-
-/**
- * A request or response message used in the OAuth protocol.
- * <p>
- * The parameters in this class are not percent-encoded. Methods like
- * OAuthClient.invoke and OAuthResponseMessage.completeParameters are
- * responsible for percent-encoding parameters before transmission and decoding
- * them after reception.
- * 
- * @author John Kristian
- */
-public class OAuthMessage {
-
-    public OAuthMessage(String method, String URL, Collection<? extends Map.Entry> parameters) {
-        this(method, URL, parameters, null);
-    }
-
-    public OAuthMessage(String method, String URL, Collection<? extends Map.Entry> parameters,
-            InputStream bodyAsStream) {
-        this.method = method;
-        this.URL = URL;
-        this.bodyAsStream = bodyAsStream;
-        if (parameters == null) {
-            this.parameters = new ArrayList<Map.Entry<String, String>>();
-        } else {
-            this.parameters = new ArrayList<Map.Entry<String, String>>(parameters.size());
-            for (Map.Entry p : parameters) {
-                this.parameters.add(new OAuth.Parameter(
-                        toString(p.getKey()), toString(p.getValue())));
-            }
-        }
-    }
-
-    public String method;
-    public String URL;
-
-    private final List<Map.Entry<String, String>> parameters;
-    private Map<String, String> parameterMap;
-    private boolean parametersAreComplete = false;
-    private final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>();
-    private final InputStream bodyAsStream;
-    
-    public String toString() {
-        return "OAuthMessage(" + method + ", " + URL + ", " + parameters + ")";
-    }
-
-    /** A caller is about to get a parameter. */
-    private void beforeGetParameter() throws IOException {
-        if (!parametersAreComplete) {
-            completeParameters();
-            parametersAreComplete = true;
-        }
-    }
-
-    /**
-     * Finish adding parameters; for example read an HTTP response body and
-     * parse parameters from it.
-     */
-    protected void completeParameters() throws IOException {
-    }
-
-    public List<Map.Entry<String, String>> getParameters() throws IOException {
-        beforeGetParameter();
-        return Collections.unmodifiableList(parameters);
-    }
-
-    public void addParameter(String key, String value) {
-        addParameter(new OAuth.Parameter(key, value));
-    }
-
-    public void addParameter(Map.Entry<String, String> parameter) {
-        parameters.add(parameter);
-        parameterMap = null;
-    }
-    
-    public void addParameters(
-            Collection<? extends Map.Entry<String, String>> parameters) {
-        this.parameters.addAll(parameters);
-        parameterMap = null;
-    }
-
-    public String getParameter(String name) throws IOException {
-        return getParameterMap().get(name);
-    }
-
-    public String getConsumerKey() throws IOException {
-        return getParameter(OAuth.OAUTH_CONSUMER_KEY);
-    }
-
-    public String getToken() throws IOException {
-        return getParameter(OAuth.OAUTH_TOKEN);
-    }
-
-    public String getSignatureMethod() throws IOException {
-        return getParameter(OAuth.OAUTH_SIGNATURE_METHOD);
-    }
-
-    public String getSignature() throws IOException {
-        return getParameter(OAuth.OAUTH_SIGNATURE);
-    }
-
-    protected Map<String, String> getParameterMap() throws IOException {
-        beforeGetParameter();
-        if (parameterMap == null) {
-            parameterMap = OAuth.newMap(parameters);
-        }
-        return parameterMap;
-    }
-
-    /**
-     * The MIME type of the body of this message.
-     * 
-     * @return the MIME type, or null to indicate the type is unknown.
-     */
-    public String getBodyType() {
-        return getHeader(HttpMessage.CONTENT_TYPE);
-    }
-
-    /**
-     * The character encoding of the body of this message.
-     * 
-     * @return the name of an encoding, or "ISO-8859-1" if no charset has been
-     *         specified.
-     */
-    public String getBodyEncoding() {
-        return HttpMessage.DEFAULT_CHARSET;
-    }
-
-    /**
-     * The value of the last HTTP header with the given name. The name is case
-     * insensitive.
-     * 
-     * @return the value of the last header, or null to indicate that there is
-     *         no such header in this message.
-     */
-    public final String getHeader(String name) {
-        String value = null; // no such header
-        for (Map.Entry<String, String> header : getHeaders()) {
-            if (name.equalsIgnoreCase(header.getKey())) {
-                value = header.getValue();
-            }
-        }
-        return value;
-    }
-
-    /** All HTTP headers.  You can add headers to this list. */
-    public final List<Map.Entry<String, String>> getHeaders() {
-        return headers;
-    }
-
-    /**
-     * Read the body of the HTTP request or response and convert it to a String.
-     * This method isn't repeatable, since it consumes and closes getBodyAsStream.
-     * 
-     * @return the body, or null to indicate there is no body.
-     */
-    public final String readBodyAsString() throws IOException
-    {
-        InputStream body = getBodyAsStream();
-        return readAll(body, getBodyEncoding());
-    }
-
-    /**
-     * Get a stream from which to read the body of the HTTP request or response.
-     * This is designed to support efficient streaming of a large message.
-     * The caller must close the returned stream, to release the underlying
-     * resources such as the TCP connection for an HTTP response.
-     * 
-     * @return a stream from which to read the body, or null to indicate there
-     *         is no body.
-     */
-    public InputStream getBodyAsStream() throws IOException {
-        return bodyAsStream;
-    }
-
-    /** Construct a verbose description of this message and its origins. */
-    public Map<String, Object> getDump() throws IOException {
-        Map<String, Object> into = new HashMap<String, Object>();
-        dump(into);
-        return into;
-    }
-
-    protected void dump(Map<String, Object> into) throws IOException {
-        into.put("URL", URL);
-        if (parametersAreComplete) {
-            try {
-                into.putAll(getParameterMap());
-            } catch (Exception ignored) {
-            }
-        }
-    }
-
-    /**
-     * Verify that the required parameter names are contained in the actual
-     * collection.
-     * 
-     * @throws OAuthProblemException
-     *                 one or more parameters are absent.
-     * @throws IOException
-     */
-    public void requireParameters(String... names)
-            throws OAuthProblemException, IOException {
-        Set<String> present = getParameterMap().keySet();
-        List<String> absent = new ArrayList<String>();
-        for (String required : names) {
-            if (!present.contains(required)) {
-                absent.add(required);
-            }
-        }
-        if (!absent.isEmpty()) {
-            OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_ABSENT);
-            problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_ABSENT, OAuth.percentEncode(absent));
-            throw problem;
-        }
-    }
-
-    /**
-     * Add some of the parameters needed to request access to a protected
-     * resource, if they aren't already in the message.
-     * 
-     * @throws IOException
-     * @throws URISyntaxException
-     */
-    public void addRequiredParameters(OAuthAccessor accessor)
-            throws OAuthException, IOException, URISyntaxException {
-        final Map<String, String> pMap = OAuth.newMap(parameters);
-        if (pMap.get(OAuth.OAUTH_TOKEN) == null && accessor.accessToken != null) {
-            addParameter(OAuth.OAUTH_TOKEN, accessor.accessToken);
-        }
-        final OAuthConsumer consumer = accessor.consumer;
-        if (pMap.get(OAuth.OAUTH_CONSUMER_KEY) == null) {
-            addParameter(OAuth.OAUTH_CONSUMER_KEY, consumer.consumerKey);
-        }
-        String signatureMethod = pMap.get(OAuth.OAUTH_SIGNATURE_METHOD);
-        if (signatureMethod == null) {
-            signatureMethod = (String) consumer.getProperty(OAuth.OAUTH_SIGNATURE_METHOD);
-            if (signatureMethod == null) {
-                signatureMethod = OAuth.HMAC_SHA1;
-            }
-            addParameter(OAuth.OAUTH_SIGNATURE_METHOD, signatureMethod);
-        }
-        if (pMap.get(OAuth.OAUTH_TIMESTAMP) == null) {
-            addParameter(OAuth.OAUTH_TIMESTAMP, (System.currentTimeMillis() / 1000) + "");
-        }
-        if (pMap.get(OAuth.OAUTH_NONCE) == null) {
-            addParameter(OAuth.OAUTH_NONCE, System.nanoTime() + "");
-        }
-        if (pMap.get(OAuth.OAUTH_VERSION) == null) {
-        	addParameter(OAuth.OAUTH_VERSION, OAuth.VERSION_1_0);
-        }
-        this.sign(accessor);
-    }
-
-    /**
-     * Add a signature to the message.
-     * 
-     * @throws URISyntaxException
-     */
-    public void sign(OAuthAccessor accessor) throws IOException,
-            OAuthException, URISyntaxException {
-        OAuthSignatureMethod.newSigner(this, accessor).sign(this);
-    }
-
-    /**
-     * Construct an HTTP request from this OAuth message.
-     * 
-     * @param style
-     *            where to put the OAuth parameters, within the HTTP request
-     */
-    public HttpMessage toHttpRequest(ParameterStyle style) throws IOException {
-        final boolean isPost = POST.equalsIgnoreCase(method);
-        InputStream body = getBodyAsStream();
-        if (style == ParameterStyle.BODY && !(isPost && body == null)) {
-            style = ParameterStyle.QUERY_STRING;
-        }
-        String url = this.URL;
-        final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>(getHeaders());
-        switch (style) {
-        case QUERY_STRING:
-            url = OAuth.addParameters(url, getParameters());
-            break;
-        case BODY: {
-            byte[] form = OAuth.formEncode(getParameters()).getBytes(getBodyEncoding());
-            headers.add(new OAuth.Parameter(HttpMessage.CONTENT_TYPE, OAuth.FORM_ENCODED));
-            headers.add(new OAuth.Parameter(HttpMessage.CONTENT_LENGTH, form.length + ""));
-            body = new ByteArrayInputStream(form);
-            break;
-        }
-        case AUTHORIZATION_HEADER:
-            headers.add(new OAuth.Parameter("Authorization", getAuthorizationHeader(null)));
-            // Find the non-OAuth parameters:
-            List<Map.Entry<String, String>> others = getParameters();
-            if (others != null && !others.isEmpty()) {
-                others = new ArrayList<Map.Entry<String, String>>(others);
-                for (Iterator<Map.Entry<String, String>> p = others.iterator(); p.hasNext();) {
-                    if (p.next().getKey().startsWith("oauth_")) {
-                        p.remove();
-                    }
-                }
-                // Place the non-OAuth parameters elsewhere in the request:
-                if (isPost && body == null) {
-                    byte[] form = OAuth.formEncode(others).getBytes(getBodyEncoding());
-                    headers.add(new OAuth.Parameter(HttpMessage.CONTENT_TYPE, OAuth.FORM_ENCODED));
-                    headers.add(new OAuth.Parameter(HttpMessage.CONTENT_LENGTH, form.length + ""));
-                    body = new ByteArrayInputStream(form);
-                } else {
-                    url = OAuth.addParameters(url, others);
-                }
-            }
-            break;
-        }
-        HttpMessage httpRequest = new HttpMessage(method, new URL(url), body);
-        httpRequest.headers.addAll(headers);
-        return httpRequest;
-    }
-
-    /**
-     * Check that the message is valid.
-     * 
-     * @throws IOException
-     * @throws URISyntaxException
-     * 
-     * @throws OAuthProblemException
-     *                 the message is invalid
-     */
-    public void validateMessage(OAuthAccessor accessor, OAuthValidator validator)
-            throws OAuthException, IOException, URISyntaxException {
-        validator.validateMessage(this, accessor);
-    }
-
-    /**
-     * Construct a WWW-Authenticate or Authentication header value, containing
-     * the given realm plus all the parameters whose names begin with "oauth_".
-     */
-    public String getAuthorizationHeader(String realm) throws IOException {
-        StringBuilder into = new StringBuilder();
-        if (realm != null) {
-            into.append(" realm=\"").append(OAuth.percentEncode(realm)).append('"');
-        }
-        beforeGetParameter();
-        if (parameters != null) {
-            for (Map.Entry parameter : parameters) {
-                String name = toString(parameter.getKey());
-                if (name.startsWith("oauth_")) {
-                    if (into.length() > 0) into.append(",");
-                    into.append(" ");
-                    into.append(OAuth.percentEncode(name)).append("=\"");
-                    into.append(OAuth.percentEncode(toString(parameter.getValue()))).append('"');
-                }
-            }
-        }
-        return AUTH_SCHEME + into.toString();
-    }
-
-    /**
-     * Read all the data from the given stream, and close it.
-     * 
-     * @return null if from is null, or the data from the stream converted to a
-     *         String
-     */
-    public static String readAll(InputStream from, String encoding) throws IOException
-    {
-        if (from == null) {
-            return null;
-        }
-        try {
-            StringBuilder into = new StringBuilder();
-            Reader r = new InputStreamReader(from, encoding);
-            char[] s = new char[512];
-            for (int n; 0 < (n = r.read(s));) {
-                into.append(s, 0, n);
-            }
-            return into.toString();
-        } finally {
-            from.close();
-        }
-    }
-
-    /**
-     * Parse the parameters from an OAuth Authorization or WWW-Authenticate
-     * header. The realm is included as a parameter. If the given header doesn't
-     * start with "OAuth ", return an empty list.
-     */
-    public static List<OAuth.Parameter> decodeAuthorization(String authorization) {
-        List<OAuth.Parameter> into = new ArrayList<OAuth.Parameter>();
-        if (authorization != null) {
-            Matcher m = AUTHORIZATION.matcher(authorization);
-            if (m.matches()) {
-                if (AUTH_SCHEME.equalsIgnoreCase(m.group(1))) {
-                    for (String nvp : m.group(2).split("\\s*,\\s*")) {
-                        m = NVP.matcher(nvp);
-                        if (m.matches()) {
-                            String name = OAuth.decodePercent(m.group(1));
-                            String value = OAuth.decodePercent(m.group(2));
-                            into.add(new OAuth.Parameter(name, value));
-                        }
-                    }
-                }
-            }
-        }
-        return into;
-    }
-
-    public static final String AUTH_SCHEME = "OAuth";
-
-    public static final String GET = "GET";
-    public static final String POST = "POST";
-    public static final String PUT = "PUT";
-    public static final String DELETE = "DELETE";
-
-    private static final Pattern AUTHORIZATION = Pattern.compile("\\s*(\\w*)\\s+(.*)");
-    private static final Pattern NVP = Pattern.compile("(\\S*)\\s*\\=\\s*\"([^\"]*)\"");
-
-    private static final String toString(Object from) {
-        return (from == null) ? null : from.toString();
-    }
-
-}
+/*
+ * Copyright 2007, 2008 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URISyntaxException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import net.oauth.client.OAuthClient;
+import net.oauth.http.HttpMessage;
+import net.oauth.http.HttpMessageDecoder;
+import net.oauth.signature.OAuthSignatureMethod;
+
+/**
+ * A request or response message used in the OAuth protocol.
+ * <p>
+ * The parameters in this class are not percent-encoded. Methods like
+ * OAuthClient.invoke and OAuthResponseMessage.completeParameters are
+ * responsible for percent-encoding parameters before transmission and decoding
+ * them after reception.
+ * 
+ * @author John Kristian
+ */
+public class OAuthMessage {
+
+    public OAuthMessage(String method, String URL, Collection<? extends Map.Entry> parameters) {
+        this(method, URL, parameters, null);
+    }
+
+    public OAuthMessage(String method, String URL, Collection<? extends Map.Entry> parameters,
+            InputStream bodyAsStream) {
+        this(method, URL, parameters, bodyAsStream, null);
+    }
+
+    public OAuthMessage(String method, String URL, Collection<? extends Map.Entry> parameters,
+            InputStream bodyAsStream, String contentType) {
+        this.method = method;
+        this.URL = URL;
+        if (parameters == null) {
+            this.parameters = new ArrayList<Map.Entry<String, String>>();
+        } else {
+            this.parameters = new ArrayList<Map.Entry<String, String>>(parameters.size());
+            for (Map.Entry p : parameters) {
+                this.parameters.add(new OAuth.Parameter(
+                        toString(p.getKey()), toString(p.getValue())));
+            }
+        }
+
+        ByteArrayInputStream bais = null;
+        byte[] body = null;
+        if( bodyAsStream != null ) {
+            try{
+                body = getBodyAsByteArray(bodyAsStream);
+
+                Collection<Map.Entry<String, String>> headers = getHeaders();
+                headers.add(new OAuth.Parameter(HttpMessage.CONTENT_LENGTH, String.valueOf(body.length)));
+                headers.add(new OAuth.Parameter(HttpMessage.CONTENT_TYPE, contentType != null? contentType: "null"));
+
+                bais = new ByteArrayInputStream(body);
+            }catch(Exception e){
+            }
+        }
+        this.bodyAsStream = bais;
+        this.bodyAsByteArray = body;
+
+    }
+
+    private byte[] getBodyAsByteArray(InputStream bodyAsStream) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        int reads;
+
+        try {
+            reads = bodyAsStream.read();
+            while(reads != -1){
+                baos.write(reads);
+                reads = bodyAsStream.read();
+            }
+        } finally {
+            bodyAsStream.close();
+        }
+        return baos.toByteArray();
+    }
+
+    public String method;
+    public String URL;
+
+    private final List<Map.Entry<String, String>> parameters;
+    private Map<String, String> parameterMap;
+    private boolean parametersAreComplete = false;
+    private final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>();
+    private final InputStream bodyAsStream;
+    private final byte[] bodyAsByteArray;
+    
+    public String toString() {
+        return "OAuthMessage(" + method + ", " + URL + ", " + parameters + ")";
+    }
+
+    /** A caller is about to get a parameter. */
+    private void beforeGetParameter() throws IOException {
+        if (!parametersAreComplete) {
+            completeParameters();
+            parametersAreComplete = true;
+        }
+    }
+
+    /**
+     * Finish adding parameters; for example read an HTTP response body and
+     * parse parameters from it.
+     */
+    protected void completeParameters() throws IOException {
+    }
+
+    public List<Map.Entry<String, String>> getParameters() throws IOException {
+        beforeGetParameter();
+        return Collections.unmodifiableList(parameters);
+    }
+
+    public void addParameter(String key, String value) {
+        addParameter(new OAuth.Parameter(key, value));
+    }
+
+    public void addParameter(Map.Entry<String, String> parameter) {
+        parameters.add(parameter);
+        parameterMap = null;
+    }
+
+    public void addParameters(
+            Collection<? extends Map.Entry<String, String>> parameters) {
+        this.parameters.addAll(parameters);
+        parameterMap = null;
+    }
+
+    public String getParameter(String name) throws IOException {
+        return getParameterMap().get(name);
+    }
+
+    public String getConsumerKey() throws IOException {
+        return getParameter(OAuth.OAUTH_CONSUMER_KEY);
+    }
+
+    public String getToken() throws IOException {
+        return getParameter(OAuth.OAUTH_TOKEN);
+    }
+
+    public String getSignatureMethod() throws IOException {
+        return getParameter(OAuth.OAUTH_SIGNATURE_METHOD);
+    }
+
+    public String getSignature() throws IOException {
+        return getParameter(OAuth.OAUTH_SIGNATURE);
+    }
+
+    protected Map<String, String> getParameterMap() throws IOException {
+        beforeGetParameter();
+        if (parameterMap == null) {
+            parameterMap = OAuth.newMap(parameters);
+        }
+        return parameterMap;
+    }
+
+    /**
+     * The MIME type of the body of this message.
+     * 
+     * @return the MIME type, or null to indicate the type is unknown.
+     */
+    public String getBodyType() {
+        return getHeader(HttpMessage.CONTENT_TYPE);
+    }
+
+    /**
+     * The character encoding of the body of this message.
+     * 
+     * @return the name of an encoding, or "ISO-8859-1" if no charset has been
+     *         specified.
+     */
+    public String getBodyEncoding() {
+        return HttpMessage.DEFAULT_CHARSET;
+    }
+
+    /**
+     * The value of the last HTTP header with the given name. The name is case
+     * insensitive.
+     * 
+     * @return the value of the last header, or null to indicate that there is
+     *         no such header in this message.
+     */
+    public final String getHeader(String name) {
+        String value = null; // no such header
+        for (Map.Entry<String, String> header : getHeaders()) {
+            if (name.equalsIgnoreCase(header.getKey())) {
+                value = header.getValue();
+            }
+        }
+        return value;
+    }
+
+    /** All HTTP headers.  You can add headers to this list. */
+    public final List<Map.Entry<String, String>> getHeaders() {
+        return headers;
+    }
+
+    /**
+     * Read the body of the HTTP request or response and convert it to a String.
+     * 
+     * @return the body, or null to indicate there is no body.
+     */
+    public final String readBodyAsString() throws IOException
+    {
+        return getBodyAsString();
+    }
+
+    /**
+     * Get a stream from which to read the body of the HTTP request or response.
+     * This is designed to support efficient streaming of a large message.
+     * The caller must close the returned stream, to release the underlying
+     * resources such as the TCP connection for an HTTP response.
+     * 
+     * @return a stream from which to read the body, or null to indicate there
+     *         is no body.
+     */
+    public InputStream getBodyAsStream() throws IOException {
+        return bodyAsStream;
+    }
+
+    /**
+     * @return the body, or null to indicate there is no body.
+     */
+    public byte[] getBodyAsByteArray(){
+        return bodyAsByteArray;
+    }
+
+    /**
+     * @return the body, or null to indicate there is no body.
+     */
+    public String getBodyAsString(){
+        return new String(bodyAsByteArray);
+    }
+
+    /** Construct a verbose description of this message and its origins. */
+    public Map<String, Object> getDump() throws IOException {
+        Map<String, Object> into = new HashMap<String, Object>();
+        dump(into);
+        return into;
+    }
+
+    protected void dump(Map<String, Object> into) throws IOException {
+        into.put("URL", URL);
+        if (parametersAreComplete) {
+            try {
+                into.putAll(getParameterMap());
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
+    /**
+     * Verify that the required parameter names are contained in the actual
+     * collection.
+     * 
+     * @throws OAuthProblemException
+     *                 one or more parameters are absent.
+     * @throws IOException
+     */
+    public void requireParameters(String... names)
+            throws OAuthProblemException, IOException {
+        Set<String> present = getParameterMap().keySet();
+        List<String> absent = new ArrayList<String>();
+        for (String required : names) {
+            if (!present.contains(required)) {
+                absent.add(required);
+            }
+        }
+        if (!absent.isEmpty()) {
+            OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_ABSENT);
+            problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_ABSENT, OAuth.percentEncode(absent));
+            throw problem;
+        }
+    }
+
+    /**
+     * Add some of the parameters needed to request access to a protected
+     * resource, if they aren't already in the message.
+     * 
+     * @throws IOException
+     * @throws URISyntaxException
+     */
+    public void addRequiredParameters(OAuthAccessor accessor)
+            throws OAuthException, IOException, URISyntaxException {
+        final Map<String, String> pMap = OAuth.newMap(parameters);
+        if (pMap.get(OAuth.OAUTH_TOKEN) == null && accessor.accessToken != null) {
+            addParameter(OAuth.OAUTH_TOKEN, accessor.accessToken);
+        }
+        final OAuthConsumer consumer = accessor.consumer;
+        if (pMap.get(OAuth.OAUTH_CONSUMER_KEY) == null) {
+            addParameter(OAuth.OAUTH_CONSUMER_KEY, consumer.consumerKey);
+        }
+        String signatureMethod = pMap.get(OAuth.OAUTH_SIGNATURE_METHOD);
+        if (signatureMethod == null) {
+            signatureMethod = (String) consumer.getProperty(OAuth.OAUTH_SIGNATURE_METHOD);
+            if (signatureMethod == null) {
+                signatureMethod = OAuth.HMAC_SHA1;
+            }
+            addParameter(OAuth.OAUTH_SIGNATURE_METHOD, signatureMethod);
+        }
+        if (pMap.get(OAuth.OAUTH_TIMESTAMP) == null) {
+            addParameter(OAuth.OAUTH_TIMESTAMP, (System.currentTimeMillis() / 1000) + "");
+        }
+        if (pMap.get(OAuth.OAUTH_NONCE) == null) {
+            addParameter(OAuth.OAUTH_NONCE, System.nanoTime() + "");
+        }
+        if (pMap.get(OAuth.OAUTH_VERSION) == null) {
+        	addParameter(OAuth.OAUTH_VERSION, OAuth.VERSION_1_0);
+        }
+        if (pMap.get(OAuth.OAUTH_BODY_HASH) == null && bodyAsStream != null) {
+            addParameter(OAuth.OAUTH_BODY_HASH, getBodyHash() );
+        }
+        this.sign(accessor);
+    }
+
+    public String getBodyHash()
+            throws OAuthException{
+        byte[] output;
+        try{
+            MessageDigest md = MessageDigest.getInstance("SHA1");
+            md.update(bodyAsByteArray);
+            output = OAuthSignatureMethod.base64Encode(md.digest()).getBytes();
+        }catch(Exception e){
+            throw new OAuthException("Could not compute body hash: " + e.getMessage());
+        }
+
+        return new String(output);
+    }
+
+    /**
+     * Add a signature to the message.
+     * 
+     * @throws URISyntaxException
+     */
+    public void sign(OAuthAccessor accessor) throws IOException,
+            OAuthException, URISyntaxException {
+        OAuthSignatureMethod.newSigner(this, accessor).sign(this);
+    }
+
+    /**
+     * Construct an HTTP request from this OAuth message.
+     * 
+     * @param style
+     *            where to put the OAuth parameters, within the HTTP request
+     * @deprecated use HttpMessage.newRequest
+     */
+    public HttpMessage toHttpRequest(OAuthClient.ParameterStyle style) throws IOException {
+        return HttpMessage.newRequest(this, style.getReplacement());
+    }
+
+    /**
+     * Check that the message is valid.
+     * 
+     * @throws IOException
+     * @throws URISyntaxException
+     * 
+     * @throws OAuthProblemException
+     *                 the message is invalid
+     */
+    public void validateMessage(OAuthAccessor accessor, OAuthValidator validator)
+            throws OAuthException, IOException, URISyntaxException {
+        validator.validateMessage(this, accessor);
+    }
+
+    /**
+     * Construct a WWW-Authenticate or Authentication header value, containing
+     * the given realm plus all the parameters whose names begin with "oauth_".
+     */
+    public String getAuthorizationHeader(String realm) throws IOException {
+        StringBuilder into = new StringBuilder();
+        if (realm != null) {
+            into.append(" realm=\"").append(OAuth.percentEncode(realm)).append('"');
+        } else {
+            into.append(" realm=\"\"");
+        }
+        beforeGetParameter();
+        if (parameters != null) {
+            for (Map.Entry parameter : parameters) {
+                String name = toString(parameter.getKey());
+                if (name.startsWith("oauth_")) {
+                    if (into.length() > 0) into.append(",");
+                    into.append(OAuth.percentEncode(name)).append("=\"");
+                    into.append(OAuth.percentEncode(toString(parameter.getValue()))).append('"');
+                }
+            }
+        }
+        return AUTH_SCHEME + into.toString();
+    }
+
+    /**
+     * Read all the data from the given stream, and close it.
+     * 
+     * @return null if from is null, or the data from the stream converted to a
+     *         String
+     */
+    public static String readAll(InputStream from, String encoding) throws IOException
+    {
+        if (from == null) {
+            return null;
+        }
+        try {
+            StringBuilder into = new StringBuilder();
+            Reader r = new InputStreamReader(from, encoding);
+            char[] s = new char[512];
+            for (int n; 0 < (n = r.read(s));) {
+                into.append(s, 0, n);
+            }
+            return into.toString();
+        } finally {
+            from.close();
+        }
+    }
+
+    /**
+     * Parse the parameters from an OAuth Authorization or WWW-Authenticate
+     * header. The realm is included as a parameter. If the given header doesn't
+     * start with "OAuth ", return an empty list.
+     */
+    public static List<OAuth.Parameter> decodeAuthorization(String authorization) {
+        List<OAuth.Parameter> into = new ArrayList<OAuth.Parameter>();
+        if (authorization != null) {
+            Matcher m = AUTHORIZATION.matcher(authorization);
+            if (m.matches()) {
+                if (AUTH_SCHEME.equalsIgnoreCase(m.group(1))) {
+                    for (String nvp : m.group(2).split("\\s*,\\s*")) {
+                        m = NVP.matcher(nvp);
+                        if (m.matches()) {
+                            String name = OAuth.decodePercent(m.group(1));
+                            String value = OAuth.decodePercent(m.group(2));
+                            into.add(new OAuth.Parameter(name, value));
+                        }
+                    }
+                }
+            }
+        }
+        return into;
+    }
+
+    public static final String AUTH_SCHEME = "OAuth";
+
+    public static final String GET = "GET";
+    public static final String POST = "POST";
+    public static final String PUT = "PUT";
+    public static final String DELETE = "DELETE";
+
+    private static final Pattern AUTHORIZATION = Pattern.compile("\\s*(\\w*)\\s+(.*)");
+    private static final Pattern NVP = Pattern.compile("(\\S*)\\s*\\=\\s*\"([^\"]*)\"");
+
+    private static final String toString(Object from) {
+        return (from == null) ? null : from.toString();
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/OAuthProblemException.java b/bbb-lti/src/java/net/oauth/OAuthProblemException.java
index fa4468ed80..a985031329 100644
--- a/bbb-lti/src/java/net/oauth/OAuthProblemException.java
+++ b/bbb-lti/src/java/net/oauth/OAuthProblemException.java
@@ -1,105 +1,104 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.util.HashMap;
-import java.util.Map;
-import net.oauth.http.HttpMessage;
-import net.oauth.http.HttpResponseMessage;
-
-/**
- * Describes an OAuth-related problem, using a set of named parameters. One
- * parameter identifies the basic problem, and the others provide supplementary
- * diagnostic information. This can be used to capture information from a
- * response that conforms to the OAuth <a
- * href="http://wiki.oauth.net/ProblemReporting">Problem Reporting
- * extension</a>.
- * 
- * @author John Kristian
- */
-public class OAuthProblemException extends OAuthException {
-
-    public static final String OAUTH_PROBLEM = "oauth_problem";
-
-    public OAuthProblemException() {
-    }
-
-    public OAuthProblemException(String problem) {
-        super(problem);
-        if (problem != null) {
-            parameters.put(OAUTH_PROBLEM, problem);
-        }
-    }
-
-    private final Map<String, Object> parameters = new HashMap<String, Object>();
-
-    @Override
-    public String getMessage() {
-        String msg = super.getMessage();
-        if (msg != null)
-            return msg;
-        msg = getProblem();
-        if (msg != null)
-            return msg;
-        Object response = getParameters().get(HttpMessage.RESPONSE);
-        if (response != null) {
-            msg = response.toString();
-            int eol = msg.indexOf("\n");
-            if (eol < 0) {
-                eol = msg.indexOf("\r");
-            }
-            if (eol >= 0) {
-                msg = msg.substring(0, eol);
-            }
-            msg = msg.trim();
-            if (msg.length() > 0) {
-                return msg;
-            }
-        }
-        response = getHttpStatusCode();
-        if (response != null) {
-            return HttpResponseMessage.STATUS_CODE + " " + response;
-        }
-        return null;
-    }
-
-    public void setParameter(String name, Object value) {
-        getParameters().put(name, value);
-    }
-
-    public Map<String, Object> getParameters() {
-        return parameters;
-    }
-
-    public String getProblem() {
-        return (String) getParameters().get(OAUTH_PROBLEM);
-    }
-
-    public int getHttpStatusCode() {
-        Object code = getParameters().get(HttpResponseMessage.STATUS_CODE);
-        if (code == null) {
-            return 200;
-        } else if (code instanceof Number) { // the usual case
-            return ((Number) code).intValue();
-        } else {
-            return Integer.parseInt(code.toString());
-        }
-    }
-
-    private static final long serialVersionUID = 1L;
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.util.HashMap;
+import java.util.Map;
+import net.oauth.http.HttpMessage;
+
+/**
+ * Describes an OAuth-related problem, using a set of named parameters. One
+ * parameter identifies the basic problem, and the others provide supplementary
+ * diagnostic information. This can be used to capture information from a
+ * response that conforms to the OAuth <a
+ * href="http://wiki.oauth.net/ProblemReporting">Problem Reporting
+ * extension</a>.
+ * 
+ * @author John Kristian
+ */
+public class OAuthProblemException extends OAuthException {
+
+    public static final String OAUTH_PROBLEM = "oauth_problem";
+
+    public OAuthProblemException() {
+    }
+
+    public OAuthProblemException(String problem) {
+        super(problem);
+        if (problem != null) {
+            parameters.put(OAUTH_PROBLEM, problem);
+        }
+    }
+
+    private final Map<String, Object> parameters = new HashMap<String, Object>();
+
+    @Override
+    public String getMessage() {
+        String msg = super.getMessage();
+        if (msg != null)
+            return msg;
+        msg = getProblem();
+        if (msg != null)
+            return msg;
+        Object response = getParameters().get(HttpMessage.RESPONSE);
+        if (response != null) {
+            msg = response.toString();
+            int eol = msg.indexOf("\n");
+            if (eol < 0) {
+                eol = msg.indexOf("\r");
+            }
+            if (eol >= 0) {
+                msg = msg.substring(0, eol);
+            }
+            msg = msg.trim();
+            if (msg.length() > 0) {
+                return msg;
+            }
+        }
+        response = getHttpStatusCode();
+        if (response != null) {
+            return HttpMessage.STATUS_CODE + " " + response;
+        }
+        return null;
+    }
+
+    public void setParameter(String name, Object value) {
+        getParameters().put(name, value);
+    }
+
+    public Map<String, Object> getParameters() {
+        return parameters;
+    }
+
+    public String getProblem() {
+        return (String) getParameters().get(OAUTH_PROBLEM);
+    }
+
+    public int getHttpStatusCode() {
+        Object code = getParameters().get(HttpMessage.STATUS_CODE);
+        if (code == null) {
+            return 200;
+        } else if (code instanceof Number) { // the usual case
+            return ((Number) code).intValue();
+        } else {
+            return Integer.parseInt(code.toString());
+        }
+    }
+
+    private static final long serialVersionUID = 1L;
+
+}
diff --git a/bbb-lti/src/java/net/oauth/OAuthServiceProvider.java b/bbb-lti/src/java/net/oauth/OAuthServiceProvider.java
index 1196feacb7..82d5f7c28c 100644
--- a/bbb-lti/src/java/net/oauth/OAuthServiceProvider.java
+++ b/bbb-lti/src/java/net/oauth/OAuthServiceProvider.java
@@ -1,41 +1,41 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth;
-
-import java.io.Serializable;
-
-/**
- * Properties of an OAuth Service Provider.
- * 
- * @author John Kristian
- */
-public class OAuthServiceProvider implements Serializable {
-
-    private static final long serialVersionUID = 3306534392621038574L;
-
-    public final String requestTokenURL;
-    public final String userAuthorizationURL;
-    public final String accessTokenURL;
-
-    public OAuthServiceProvider(String requestTokenURL,
-            String userAuthorizationURL, String accessTokenURL) {
-        this.requestTokenURL = requestTokenURL;
-        this.userAuthorizationURL = userAuthorizationURL;
-        this.accessTokenURL = accessTokenURL;
-    }
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth;
+
+import java.io.Serializable;
+
+/**
+ * Properties of an OAuth Service Provider.
+ * 
+ * @author John Kristian
+ */
+public class OAuthServiceProvider implements Serializable {
+
+    private static final long serialVersionUID = 3306534392621038574L;
+
+    public final String requestTokenURL;
+    public final String userAuthorizationURL;
+    public final String accessTokenURL;
+
+    public OAuthServiceProvider(String requestTokenURL,
+            String userAuthorizationURL, String accessTokenURL) {
+        this.requestTokenURL = requestTokenURL;
+        this.userAuthorizationURL = userAuthorizationURL;
+        this.accessTokenURL = accessTokenURL;
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/ParameterStyle.java b/bbb-lti/src/java/net/oauth/ParameterStyle.java
index 0c896dfd0e..30e2862171 100644
--- a/bbb-lti/src/java/net/oauth/ParameterStyle.java
+++ b/bbb-lti/src/java/net/oauth/ParameterStyle.java
@@ -1,24 +1,40 @@
-package net.oauth;
-
-/**
- * Where to place OAuth parameters in an HTTP message. The alternatives are
- * summarized in OAuth Core section 5.2.
- */
-public enum ParameterStyle {
-    /**
-     * Send parameters whose names begin with "oauth_" in an HTTP header, and
-     * other parameters (whose names don't begin with "oauth_") in either the
-     * message body or URL query string. The header formats are specified by
-     * OAuth Core section 5.4.
-     */
-    AUTHORIZATION_HEADER,
-
-    /**
-     * Send all parameters in the message body, with a Content-Type of
-     * application/x-www-form-urlencoded.
-     */
-    BODY,
-
-    /** Send all parameters in the query string part of the URL. */
-    QUERY_STRING;
-}
+/*
+ * Copyright 2009 John Kristian
+ *
+ * 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 net.oauth;
+
+/**
+ * Where to place OAuth parameters in an HTTP message. The alternatives are
+ * summarized in OAuth Core section 5.2.
+ */
+public enum ParameterStyle {
+    /**
+     * Send parameters whose names begin with "oauth_" in an HTTP header, and
+     * other parameters (whose names don't begin with "oauth_") in either the
+     * message body or URL query string. The header formats are specified by
+     * OAuth Core section 5.4.
+     */
+    AUTHORIZATION_HEADER,
+
+    /**
+     * Send all parameters in the message body, with a Content-Type of
+     * application/x-www-form-urlencoded.
+     */
+    BODY,
+
+    /** Send all parameters in the query string part of the URL. */
+    QUERY_STRING;
+}
diff --git a/bbb-lti/src/java/net/oauth/SimpleOAuthValidator.java b/bbb-lti/src/java/net/oauth/SimpleOAuthValidator.java
index 33b96884c8..d23f53a6a8 100644
--- a/bbb-lti/src/java/net/oauth/SimpleOAuthValidator.java
+++ b/bbb-lti/src/java/net/oauth/SimpleOAuthValidator.java
@@ -100,7 +100,6 @@ public class SimpleOAuthValidator implements OAuthValidator {
         // Check for repeated oauth_ parameters:
         boolean repeated = false;
         Map<String, Collection<String>> nameToValues = new HashMap<String, Collection<String>>();
-        String repeatedParameter = "";
         for (Map.Entry<String, String> parameter : message.getParameters()) {
             String name = parameter.getKey();
             if (SINGLE_PARAMETERS.contains(name)) {
@@ -110,7 +109,6 @@ public class SimpleOAuthValidator implements OAuthValidator {
                     nameToValues.put(name, values);
                 } else {
                     repeated = true;
-                    repeatedParameter = name;
                 }
                 values.add(parameter.getValue());
             }
@@ -126,7 +124,7 @@ public class SimpleOAuthValidator implements OAuthValidator {
                     }
                 }
             }
-            OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_REJECTED + ":" + repeatedParameter);
+            OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_REJECTED);
             problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_REJECTED, OAuth.formEncode(rejected));
             throw problem;
         }
diff --git a/bbb-lti/src/java/net/oauth/client/ExcerptInputStream.java b/bbb-lti/src/java/net/oauth/client/ExcerptInputStream.java
index 277c970cae..480b0d2deb 100644
--- a/bbb-lti/src/java/net/oauth/client/ExcerptInputStream.java
+++ b/bbb-lti/src/java/net/oauth/client/ExcerptInputStream.java
@@ -1,42 +1,124 @@
-package net.oauth.client;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/** A decorator that retains a copy of the first few bytes of data. */
-public class ExcerptInputStream extends BufferedInputStream
-{
-    /**
-     * A marker that's appended to the excerpt if it's less than the complete
-     * stream.
-     */
-    public static final byte[] ELLIPSIS = " ...".getBytes();
-
-    public ExcerptInputStream(InputStream in) throws IOException {
-        super(in);
-        mark(LIMIT);
-        int total = 0;
-        int read;
-        while ((read = read(excerpt, total, LIMIT - total)) != -1 && ((total += read) < LIMIT));
-        if (total == LIMIT) {
-            // Only add the ellipsis if there are at least LIMIT bytes
-            System.arraycopy(ELLIPSIS, 0, excerpt, total, ELLIPSIS.length);
-        } else {
-            byte[] tmp = new byte[total];
-            System.arraycopy(excerpt, 0, tmp, 0, total);
-            excerpt = tmp;
-        }
-        reset();
-    }
-
-    private static final int LIMIT = 1024;
-    private byte[] excerpt = new byte[LIMIT + ELLIPSIS.length];
-
-    /** The first few bytes of data, plus ELLIPSIS if there are more bytes. */
-    public byte[] getExcerpt()
-    {
-        return excerpt;
-    }
-
-}
+package net.oauth.client;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/** A decorator that retains a copy of the first few bytes of data. */
+public class ExcerptInputStream extends FilterInputStream
+{
+    /**
+     * A marker that's appended to the excerpt if it's less than the complete
+     * stream.
+     */
+    public static final byte[] ELLIPSIS = " ...".getBytes();
+
+    public ExcerptInputStream(InputStream in)
+    {
+        super(in);
+    }
+
+    private static final int LIMIT = 1024;
+
+    private byte[] excerpt = new byte[LIMIT + ELLIPSIS.length];
+
+    private int taken = 0; // bytes received from in
+
+    private int given = Integer.MAX_VALUE; // bytes delivered to callers
+
+    @Override
+    public void close() throws IOException
+    {
+        super.close();
+        byte[] complete = new byte[taken];
+        System.arraycopy(excerpt, 0, complete, 0, taken);
+        excerpt = complete;
+    }
+
+    /** The first few bytes of data, plus ELLIPSIS if there are more bytes. */
+    public byte[] getExcerpt() throws IOException
+    {
+        if (taken < excerpt.length) {
+            final int mark = Math.min(given, taken);
+            given = Integer.MAX_VALUE;
+            while (taken < excerpt.length) {
+                read(excerpt, taken, LIMIT - taken);
+            }
+            given = mark;
+        }
+        return excerpt;
+    }
+
+    @Override
+    public int read(byte[] b, int offset, int length) throws IOException
+    {
+        int total = 0;
+        if (given < taken) {
+            final int e = Math.min(length, taken - given);
+            System.arraycopy(excerpt, given, b, offset, e);
+            total += e;
+            given += e;
+            if (given < taken) {
+                return total;
+            }
+            given = Integer.MAX_VALUE;
+            offset += e;
+            length -= e;
+        }
+        final int r = super.read(b, offset, length);
+        if (r > 0) {
+            total += r;
+            final int e = Math.min(r, LIMIT - taken);
+            if (e >= 0) {
+                System.arraycopy(b, offset, excerpt, taken, e);
+                taken += e;
+                if (taken >= LIMIT) {
+                    System.arraycopy(ELLIPSIS, 0, excerpt, LIMIT, ELLIPSIS.length);
+                    taken = excerpt.length;
+                }
+            }
+        } else if (taken < excerpt.length) {
+            byte[] complete = new byte[taken];
+            System.arraycopy(excerpt, 0, complete, 0, taken);
+            excerpt = complete;
+        }
+        return (total > 0) ? total : r;
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException
+    {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read() throws IOException
+    {
+        byte[] b = new byte[1];
+        return (read(b) <= 0) ? -1 : unsigned(b[0]);
+    }
+
+    /** @return an excerpt from the data copied. */
+    public static byte[] copyAll(InputStream from, OutputStream into) throws IOException
+    {
+        final ExcerptInputStream ex = new ExcerptInputStream(from);
+        ex.copyAll(into);
+        return ex.getExcerpt();
+    }
+
+    /** Copy all the data from this stream to the given output stream. */
+    private void copyAll(OutputStream into) throws IOException
+    {
+        byte[] b = new byte[1024];
+        for (int n; 0 < (n = read(b));) {
+            into.write(b, 0, n);
+        }
+    }
+
+    private static int unsigned(byte b)
+    {
+        return (b >= 0) ? b : ((int) b) + 256;
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/client/OAuthClient.java b/bbb-lti/src/java/net/oauth/client/OAuthClient.java
index 1087b0b61b..d470d766b4 100644
--- a/bbb-lti/src/java/net/oauth/client/OAuthClient.java
+++ b/bbb-lti/src/java/net/oauth/client/OAuthClient.java
@@ -1,300 +1,332 @@
-/*
- * Copyright 2007, 2008 Netflix, Inc.
- *
- * 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 net.oauth.client;
-
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import net.oauth.OAuth;
-import net.oauth.OAuthAccessor;
-import net.oauth.OAuthConsumer;
-import net.oauth.OAuthException;
-import net.oauth.OAuthMessage;
-import net.oauth.OAuthProblemException;
-import net.oauth.ParameterStyle;
-import net.oauth.http.HttpClient;
-import net.oauth.http.HttpMessage;
-import net.oauth.http.HttpMessageDecoder;
-import net.oauth.http.HttpResponseMessage;
-
-/**
- * Methods for an OAuth consumer to request tokens from a service provider.
- * <p>
- * This class can also be used to request access to protected resources, in some
- * cases. But not in all cases. For example, this class can't handle arbitrary
- * HTTP headers.
- * <p>
- * Methods of this class return a response as an OAuthMessage, from which you
- * can get a body or parameters but not both. Calling a getParameter method will
- * read and close the body (like readBodyAsString), so you can't read it later.
- * If you read or close the body first, then getParameter can't read it. The
- * response headers should tell you whether the response contains encoded
- * parameters, that is whether you should call getParameter or not.
- * <p>
- * Methods of this class don't follow redirects. When they receive a redirect
- * response, they throw an OAuthProblemException, with properties
- * HttpResponseMessage.STATUS_CODE = the redirect code
- * HttpResponseMessage.LOCATION = the redirect URL. Such a redirect can't be
- * handled at the HTTP level, if the second request must carry another OAuth
- * signature (with different parameters). For example, Google's Service Provider
- * routinely redirects requests for access to protected resources, and requires
- * the redirected request to be signed.
- * 
- * @author John Kristian
- */
-public class OAuthClient {
-
-    public OAuthClient(HttpClient http)
-    {
-        this.http = http;
-        httpParameters.put(HttpClient.FOLLOW_REDIRECTS, Boolean.FALSE);
-    }
-
-    private HttpClient http;
-    protected final Map<String, Object> httpParameters = new HashMap<String, Object>();
-
-    public void setHttpClient(HttpClient http) {
-        this.http = http;
-    }
-
-    public HttpClient getHttpClient() {
-        return http;
-    }
-
-    /**
-     * HTTP client parameters, as a map from parameter name to value.
-     * 
-     * @see HttpClient for parameter names.
-     */
-    public Map<String, Object> getHttpParameters() {
-        return httpParameters;
-    }
-
-    /**
-     * Get a fresh request token from the service provider.
-     * 
-     * @param accessor
-     *            should contain a consumer that contains a non-null consumerKey
-     *            and consumerSecret. Also,
-     *            accessor.consumer.serviceProvider.requestTokenURL should be
-     *            the URL (determined by the service provider) for getting a
-     *            request token.
-     * @throws OAuthProblemException
-     *             the HTTP response status code was not 200 (OK)
-     */
-    public void getRequestToken(OAuthAccessor accessor) throws IOException,
-            OAuthException, URISyntaxException {
-        getRequestToken(accessor, null);
-    }
-
-    /**
-     * Get a fresh request token from the service provider.
-     * 
-     * @param accessor
-     *            should contain a consumer that contains a non-null consumerKey
-     *            and consumerSecret. Also,
-     *            accessor.consumer.serviceProvider.requestTokenURL should be
-     *            the URL (determined by the service provider) for getting a
-     *            request token.
-     * @param httpMethod
-     *            typically OAuthMessage.POST or OAuthMessage.GET, or null to
-     *            use the default method.
-     * @throws OAuthProblemException
-     *             the HTTP response status code was not 200 (OK)
-     */
-    public void getRequestToken(OAuthAccessor accessor, String httpMethod)
-            throws IOException, OAuthException, URISyntaxException {
-        getRequestToken(accessor, httpMethod, null);
-    }
-
-    /** Get a fresh request token from the service provider.
-     * 
-     * @param accessor
-     *            should contain a consumer that contains a non-null consumerKey
-     *            and consumerSecret. Also,
-     *            accessor.consumer.serviceProvider.requestTokenURL should be
-     *            the URL (determined by the service provider) for getting a
-     *            request token.
-     * @param httpMethod
-     *            typically OAuthMessage.POST or OAuthMessage.GET, or null to
-     *            use the default method.
-     * @param parameters
-     *            additional parameters for this request, or null to indicate
-     *            that there are no additional parameters.
-     * @throws OAuthProblemException
-     *             the HTTP response status code was not 200 (OK)
-     */
-    public void getRequestToken(OAuthAccessor accessor, String httpMethod,
-            Collection<? extends Map.Entry> parameters) throws IOException,
-            OAuthException, URISyntaxException {
-        accessor.accessToken = null;
-        accessor.tokenSecret = null;
-        {
-            // This code supports the 'Variable Accessor Secret' extension
-            // described in http://oauth.pbwiki.com/AccessorSecret
-            Object accessorSecret = accessor
-                    .getProperty(OAuthConsumer.ACCESSOR_SECRET);
-            if (accessorSecret != null) {
-                List<Map.Entry> p = (parameters == null) ? new ArrayList<Map.Entry>(
-                        1)
-                        : new ArrayList<Map.Entry>(parameters);
-                p.add(new OAuth.Parameter("oauth_accessor_secret",
-                        accessorSecret.toString()));
-                parameters = p;
-                // But don't modify the caller's parameters.
-            }
-        }
-        OAuthMessage response = invoke(accessor, httpMethod,
-                accessor.consumer.serviceProvider.requestTokenURL, parameters);
-        accessor.requestToken = response.getParameter(OAuth.OAUTH_TOKEN);
-        accessor.tokenSecret = response.getParameter(OAuth.OAUTH_TOKEN_SECRET);
-        response.requireParameters(OAuth.OAUTH_TOKEN, OAuth.OAUTH_TOKEN_SECRET);
-    }
-
-    /**
-     * Get an access token from the service provider, in exchange for an
-     * authorized request token.
-     * 
-     * @param accessor
-     *            should contain a non-null requestToken and tokenSecret, and a
-     *            consumer that contains a consumerKey and consumerSecret. Also,
-     *            accessor.consumer.serviceProvider.accessTokenURL should be the
-     *            URL (determined by the service provider) for getting an access
-     *            token.
-     * @param httpMethod
-     *            typically OAuthMessage.POST or OAuthMessage.GET, or null to
-     *            use the default method.
-     * @param parameters
-     *            additional parameters for this request, or null to indicate
-     *            that there are no additional parameters.
-     * @throws OAuthProblemException
-     *             the HTTP response status code was not 200 (OK)
-     */
-    public OAuthMessage getAccessToken(OAuthAccessor accessor, String httpMethod,
-            Collection<? extends Map.Entry> parameters) throws IOException, OAuthException, URISyntaxException {
-        if (accessor.requestToken != null) {
-            if (parameters == null) {
-                parameters = OAuth.newList(OAuth.OAUTH_TOKEN, accessor.requestToken);
-            } else if (!OAuth.newMap(parameters).containsKey(OAuth.OAUTH_TOKEN)) {
-                List<Map.Entry> p = new ArrayList<Map.Entry>(parameters);
-                p.add(new OAuth.Parameter(OAuth.OAUTH_TOKEN, accessor.requestToken));
-                parameters = p;
-            }
-        }
-        OAuthMessage response = invoke(accessor, httpMethod,
-                accessor.consumer.serviceProvider.accessTokenURL, parameters);
-        response.requireParameters(OAuth.OAUTH_TOKEN, OAuth.OAUTH_TOKEN_SECRET);
-        accessor.accessToken = response.getParameter(OAuth.OAUTH_TOKEN);
-        accessor.tokenSecret = response.getParameter(OAuth.OAUTH_TOKEN_SECRET);
-        return response;
-    }
-
-    /**
-     * Construct a request message, send it to the service provider and get the
-     * response.
-     * 
-     * @param httpMethod
-     *            the HTTP request method, or null to use the default method
-     * @return the response
-     * @throws URISyntaxException
-     *             the given url isn't valid syntactically
-     * @throws OAuthProblemException
-     *             the HTTP response status code was not 200 (OK)
-     */
-    public OAuthMessage invoke(OAuthAccessor accessor, String httpMethod,
-            String url, Collection<? extends Map.Entry> parameters)
-    throws IOException, OAuthException, URISyntaxException {
-        OAuthMessage request = accessor.newRequestMessage(httpMethod, url, parameters);
-        Object accepted = accessor.consumer.getProperty(OAuthConsumer.ACCEPT_ENCODING);
-        if (accepted != null) {
-            request.getHeaders().add(new OAuth.Parameter(HttpMessage.ACCEPT_ENCODING, accepted.toString()));
-        }
-        Object ps = accessor.consumer.getProperty(PARAMETER_STYLE);
-        ParameterStyle style = (ps == null) ? ParameterStyle.BODY
-                : Enum.valueOf(ParameterStyle.class, ps.toString());
-        return invoke(request, style);
-    }
-
-    /**
-     * The name of the OAuthConsumer property whose value is the ParameterStyle
-     * to be used by invoke.
-     */
-    public static final String PARAMETER_STYLE = "parameterStyle";
-
-    /**
-     * The name of the OAuthConsumer property whose value is the Accept-Encoding
-     * header in HTTP requests.
-     * @deprecated use {@link OAuthConsumer#ACCEPT_ENCODING} instead
-     */
-    @Deprecated
-    public static final String ACCEPT_ENCODING = OAuthConsumer.ACCEPT_ENCODING;
-
-    /**
-     * Construct a request message, send it to the service provider and get the
-     * response.
-     * 
-     * @return the response
-     * @throws URISyntaxException
-     *                 the given url isn't valid syntactically
-     * @throws OAuthProblemException
-     *                 the HTTP response status code was not 200 (OK)
-     */
-    public OAuthMessage invoke(OAuthAccessor accessor, String url,
-            Collection<? extends Map.Entry> parameters) throws IOException,
-            OAuthException, URISyntaxException {
-        return invoke(accessor, null, url, parameters);
-    }
-
-    /**
-     * Send a request message to the service provider and get the response.
-     * 
-     * @return the response
-     * @throws IOException
-     *                 failed to communicate with the service provider
-     * @throws OAuthProblemException
-     *             the HTTP response status code was not 200 (OK)
-     */
-    public OAuthMessage invoke(OAuthMessage request, ParameterStyle style)
-            throws IOException, OAuthException {
-        OAuthResponseMessage response = access(request, style);
-        if ((response.getHttpResponse().getStatusCode() / 100) != 2) {
-            throw response.toOAuthProblemException();
-        }
-        return response;
-    }
-
-    /**
-     * Send a request and return the response. Don't try to decide whether the
-     * response indicates success; merely return it.
-     */
-    public OAuthResponseMessage access(OAuthMessage request, ParameterStyle style) throws IOException {
-        HttpMessage httpRequest = request.toHttpRequest(style);
-        HttpResponseMessage httpResponse = http.execute(httpRequest, httpParameters);
-        httpResponse = HttpMessageDecoder.decode(httpResponse);
-        return new OAuthResponseMessage(httpResponse);
-    }
-
-    protected static final String PUT = OAuthMessage.PUT;
-    protected static final String POST = OAuthMessage.POST;
-    protected static final String DELETE = OAuthMessage.DELETE;
-    protected static final String CONTENT_LENGTH = HttpMessage.CONTENT_LENGTH;
-
-}
+/*
+ * Copyright 2007, 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.client;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthException;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthProblemException;
+import net.oauth.http.HttpClient;
+import net.oauth.http.HttpMessage;
+import net.oauth.http.HttpMessageDecoder;
+import net.oauth.http.HttpResponseMessage;
+
+/**
+ * Methods for an OAuth consumer to request tokens from a service provider.
+ * <p>
+ * This class can also be used to request access to protected resources, in some
+ * cases. But not in all cases. For example, this class can't handle arbitrary
+ * HTTP headers.
+ * <p>
+ * Methods of this class return a response as an OAuthMessage, from which you
+ * can get a body or parameters but not both. Calling a getParameter method will
+ * read and close the body (like readBodyAsString), so you can't read it later.
+ * If you read or close the body first, then getParameter can't read it. The
+ * response headers should tell you whether the response contains encoded
+ * parameters, that is whether you should call getParameter or not.
+ * <p>
+ * Methods of this class don't follow redirects. When they receive a redirect
+ * response, they throw an OAuthProblemException, with properties
+ * HttpResponseMessage.STATUS_CODE = the redirect code
+ * HttpResponseMessage.LOCATION = the redirect URL. Such a redirect can't be
+ * handled at the HTTP level, if the second request must carry another OAuth
+ * signature (with different parameters). For example, Google's Service Provider
+ * routinely redirects requests for access to protected resources, and requires
+ * the redirected request to be signed.
+ * 
+ * @author John Kristian
+ */
+public class OAuthClient {
+
+    public OAuthClient(HttpClient http)
+    {
+        this.http = http;
+        httpParameters.put(HttpClient.FOLLOW_REDIRECTS, Boolean.FALSE);
+    }
+
+    private HttpClient http;
+    protected final Map<String, Object> httpParameters = new HashMap<String, Object>();
+
+    public void setHttpClient(HttpClient http) {
+        this.http = http;
+    }
+
+    public HttpClient getHttpClient() {
+        return http;
+    }
+
+    /**
+     * HTTP client parameters, as a map from parameter name to value.
+     * 
+     * @see HttpClient for parameter names.
+     */
+    public Map<String, Object> getHttpParameters() {
+        return httpParameters;
+    }
+
+    /**
+     * Get a fresh request token from the service provider.
+     * 
+     * @param accessor
+     *            should contain a consumer that contains a non-null consumerKey
+     *            and consumerSecret. Also,
+     *            accessor.consumer.serviceProvider.requestTokenURL should be
+     *            the URL (determined by the service provider) for getting a
+     *            request token.
+     * @throws OAuthProblemException
+     *             the HTTP response status code was not 200 (OK)
+     */
+    public void getRequestToken(OAuthAccessor accessor) throws IOException,
+            OAuthException, URISyntaxException {
+        getRequestToken(accessor, null);
+    }
+
+    /**
+     * Get a fresh request token from the service provider.
+     * 
+     * @param accessor
+     *            should contain a consumer that contains a non-null consumerKey
+     *            and consumerSecret. Also,
+     *            accessor.consumer.serviceProvider.requestTokenURL should be
+     *            the URL (determined by the service provider) for getting a
+     *            request token.
+     * @param httpMethod
+     *            typically OAuthMessage.POST or OAuthMessage.GET, or null to
+     *            use the default method.
+     * @throws OAuthProblemException
+     *             the HTTP response status code was not 200 (OK)
+     */
+    public void getRequestToken(OAuthAccessor accessor, String httpMethod)
+            throws IOException, OAuthException, URISyntaxException {
+        getRequestToken(accessor, httpMethod, null);
+    }
+
+    /** Get a fresh request token from the service provider.
+     * 
+     * @param accessor
+     *            should contain a consumer that contains a non-null consumerKey
+     *            and consumerSecret. Also,
+     *            accessor.consumer.serviceProvider.requestTokenURL should be
+     *            the URL (determined by the service provider) for getting a
+     *            request token.
+     * @param httpMethod
+     *            typically OAuthMessage.POST or OAuthMessage.GET, or null to
+     *            use the default method.
+     * @param parameters
+     *            additional parameters for this request, or null to indicate
+     *            that there are no additional parameters.
+     * @throws OAuthProblemException
+     *             the HTTP response status code was not 200 (OK)
+     */
+    public void getRequestToken(OAuthAccessor accessor, String httpMethod,
+            Collection<? extends Map.Entry> parameters) throws IOException,
+            OAuthException, URISyntaxException {
+        accessor.accessToken = null;
+        accessor.tokenSecret = null;
+        {
+            // This code supports the 'Variable Accessor Secret' extension
+            // described in http://oauth.pbwiki.com/AccessorSecret
+            Object accessorSecret = accessor
+                    .getProperty(OAuthConsumer.ACCESSOR_SECRET);
+            if (accessorSecret != null) {
+                List<Map.Entry> p = (parameters == null) ? new ArrayList<Map.Entry>(
+                        1)
+                        : new ArrayList<Map.Entry>(parameters);
+                p.add(new OAuth.Parameter("oauth_accessor_secret",
+                        accessorSecret.toString()));
+                parameters = p;
+                // But don't modify the caller's parameters.
+            }
+        }
+        OAuthMessage response = invoke(accessor, httpMethod,
+                accessor.consumer.serviceProvider.requestTokenURL, parameters);
+        accessor.requestToken = response.getParameter(OAuth.OAUTH_TOKEN);
+        accessor.tokenSecret = response.getParameter(OAuth.OAUTH_TOKEN_SECRET);
+        response.requireParameters(OAuth.OAUTH_TOKEN, OAuth.OAUTH_TOKEN_SECRET);
+    }
+
+    /**
+     * Get an access token from the service provider, in exchange for an
+     * authorized request token.
+     * 
+     * @param accessor
+     *            should contain a non-null requestToken and tokenSecret, and a
+     *            consumer that contains a consumerKey and consumerSecret. Also,
+     *            accessor.consumer.serviceProvider.accessTokenURL should be the
+     *            URL (determined by the service provider) for getting an access
+     *            token.
+     * @param httpMethod
+     *            typically OAuthMessage.POST or OAuthMessage.GET, or null to
+     *            use the default method.
+     * @param parameters
+     *            additional parameters for this request, or null to indicate
+     *            that there are no additional parameters.
+     * @throws OAuthProblemException
+     *             the HTTP response status code was not 200 (OK)
+     */
+    public OAuthMessage getAccessToken(OAuthAccessor accessor, String httpMethod,
+            Collection<? extends Map.Entry> parameters) throws IOException, OAuthException, URISyntaxException {
+        if (accessor.requestToken != null) {
+            if (parameters == null) {
+                parameters = OAuth.newList(OAuth.OAUTH_TOKEN, accessor.requestToken);
+            } else if (!OAuth.newMap(parameters).containsKey(OAuth.OAUTH_TOKEN)) {
+                List<Map.Entry> p = new ArrayList<Map.Entry>(parameters);
+                p.add(new OAuth.Parameter(OAuth.OAUTH_TOKEN, accessor.requestToken));
+                parameters = p;
+            }
+        }
+        OAuthMessage response = invoke(accessor, httpMethod,
+                accessor.consumer.serviceProvider.accessTokenURL, parameters);
+        response.requireParameters(OAuth.OAUTH_TOKEN, OAuth.OAUTH_TOKEN_SECRET);
+        accessor.accessToken = response.getParameter(OAuth.OAUTH_TOKEN);
+        accessor.tokenSecret = response.getParameter(OAuth.OAUTH_TOKEN_SECRET);
+        return response;
+    }
+
+    /**
+     * Construct a request message, send it to the service provider and get the
+     * response.
+     * 
+     * @param httpMethod
+     *            the HTTP request method, or null to use the default method
+     * @return the response
+     * @throws URISyntaxException
+     *             the given url isn't valid syntactically
+     * @throws OAuthProblemException
+     *             the HTTP response status code was not 200 (OK)
+     */
+    public OAuthMessage invoke(OAuthAccessor accessor, String httpMethod,
+            String url, Collection<? extends Map.Entry> parameters)
+    throws IOException, OAuthException, URISyntaxException {
+        OAuthMessage request = accessor.newRequestMessage(httpMethod, url, parameters);
+        Object accepted = accessor.consumer.getProperty(OAuthConsumer.ACCEPT_ENCODING);
+        if (accepted != null) {
+            request.getHeaders().add(new OAuth.Parameter(HttpMessage.ACCEPT_ENCODING, accepted.toString()));
+        }
+        Object ps = accessor.consumer.getProperty(PARAMETER_STYLE);
+        net.oauth.ParameterStyle style = (ps == null) ? net.oauth.ParameterStyle.BODY
+                : Enum.valueOf(net.oauth.ParameterStyle.class, ps.toString());
+        return invoke(request, style);
+    }
+
+    /**
+     * The name of the OAuthConsumer property whose value is the ParameterStyle
+     * to be used by invoke.
+     */
+    public static final String PARAMETER_STYLE = "parameterStyle";
+
+    /**
+     * The name of the OAuthConsumer property whose value is the Accept-Encoding
+     * header in HTTP requests.
+     * @deprecated use {@link OAuthConsumer#ACCEPT_ENCODING} instead
+     */
+    @Deprecated
+    public static final String ACCEPT_ENCODING = OAuthConsumer.ACCEPT_ENCODING;
+
+    /**
+     * Construct a request message, send it to the service provider and get the
+     * response.
+     * 
+     * @return the response
+     * @throws URISyntaxException
+     *                 the given url isn't valid syntactically
+     * @throws OAuthProblemException
+     *                 the HTTP response status code was not 200 (OK)
+     */
+    public OAuthMessage invoke(OAuthAccessor accessor, String url,
+            Collection<? extends Map.Entry> parameters) throws IOException,
+            OAuthException, URISyntaxException {
+        return invoke(accessor, null, url, parameters);
+    }
+
+    /**
+     * Send a request message to the service provider and get the response.
+     * 
+     * @return the response
+     * @throws IOException
+     *                 failed to communicate with the service provider
+     * @throws OAuthProblemException
+     *             the HTTP response status code was not 200 (OK)
+     */
+    public OAuthMessage invoke(OAuthMessage request,  net.oauth.ParameterStyle style)
+            throws IOException, OAuthException {
+        OAuthResponseMessage response = access(request, style);
+        if ((response.getHttpResponse().getStatusCode() / 100) != 2) {
+            throw response.toOAuthProblemException();
+        }
+        return response;
+    }
+
+    /**
+     * Send a request and return the response. Don't try to decide whether the
+     * response indicates success; merely return it.
+     */
+    public OAuthResponseMessage access(OAuthMessage request,  net.oauth.ParameterStyle style) throws IOException {
+        HttpMessage httpRequest = HttpMessage.newRequest(request, style);
+        HttpResponseMessage httpResponse = http.execute(httpRequest, httpParameters);
+        httpResponse = HttpMessageDecoder.decode(httpResponse);
+        return new OAuthResponseMessage(httpResponse);
+    }
+
+    /**
+     * Where to place parameters in an HTTP message.
+     * 
+     * @deprecated use net.oauth.ParameterStyle.
+     */
+    public static enum ParameterStyle {
+        AUTHORIZATION_HEADER(net.oauth.ParameterStyle.AUTHORIZATION_HEADER),
+        BODY                (net.oauth.ParameterStyle.BODY),
+        QUERY_STRING        (net.oauth.ParameterStyle.QUERY_STRING);
+
+        public net.oauth.ParameterStyle getReplacement() {
+            return replacement;
+        }
+
+        private ParameterStyle(net.oauth.ParameterStyle replacement) {
+            this.replacement = replacement;
+        }
+
+        private final net.oauth.ParameterStyle replacement;
+    }
+
+    /** @deprecated */
+    public OAuthMessage invoke(OAuthMessage request, ParameterStyle style)
+    throws IOException, OAuthException {
+        return invoke(request, style.getReplacement());
+    }
+
+    /** @deprecated */
+    public OAuthResponseMessage access(OAuthMessage request, ParameterStyle style)
+    throws IOException {
+        return access(request, style.getReplacement());
+    }
+
+    protected static final String PUT = OAuthMessage.PUT;
+    protected static final String POST = OAuthMessage.POST;
+    protected static final String DELETE = OAuthMessage.DELETE;
+    protected static final String CONTENT_LENGTH = HttpMessage.CONTENT_LENGTH;
+
+}
diff --git a/bbb-lti/src/java/net/oauth/client/OAuthResponseMessage.java b/bbb-lti/src/java/net/oauth/client/OAuthResponseMessage.java
index 71e7473ba6..4adc431f8a 100644
--- a/bbb-lti/src/java/net/oauth/client/OAuthResponseMessage.java
+++ b/bbb-lti/src/java/net/oauth/client/OAuthResponseMessage.java
@@ -1,116 +1,116 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.client;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-import net.oauth.OAuth;
-import net.oauth.OAuthMessage;
-import net.oauth.OAuthProblemException;
-import net.oauth.http.HttpResponseMessage;
-
-/**
- * An HTTP response, encapsulated as an OAuthMessage.
- * 
- * @author John Kristian
- */
-public class OAuthResponseMessage extends OAuthMessage
-{
-    OAuthResponseMessage(HttpResponseMessage http) throws IOException
-    {
-        super(http.method, http.url.toExternalForm(), null);
-        this.http = http;
-        getHeaders().addAll(http.headers);
-        for (Map.Entry<String, String> header : http.headers) {
-            if ("WWW-Authenticate".equalsIgnoreCase(header.getKey())) {
-                for (OAuth.Parameter parameter : decodeAuthorization(header.getValue())) {
-                    if (!"realm".equalsIgnoreCase(parameter.getKey())) {
-                        addParameter(parameter);
-                    }
-                }
-            }
-        }
-    }
-
-    private final HttpResponseMessage http;
-
-    public HttpResponseMessage getHttpResponse() {
-        return http;
-    }
-
-    @Override
-    public InputStream getBodyAsStream() throws IOException
-    {
-        return http.getBody();
-    }
-
-    @Override
-    public String getBodyEncoding()
-    {
-        return http.getContentCharset();
-    }
-
-    @Override
-    public void requireParameters(String... names) throws OAuthProblemException, IOException {
-        try {
-            super.requireParameters(names);
-        } catch (OAuthProblemException problem) {
-            problem.getParameters().putAll(getDump());
-            throw problem;
-        }
-    }
-
-    /**
-     * Encapsulate this message as an exception. Read and close the body of this
-     * message.
-     */
-    public OAuthProblemException toOAuthProblemException() throws IOException {
-        OAuthProblemException problem = new OAuthProblemException();
-        try {
-            getParameters(); // decode the response body
-        } catch (IOException ignored) {
-        }
-        problem.getParameters().putAll(getDump());
-        try {
-            InputStream b = getBodyAsStream();
-            if (b != null) {
-                b.close(); // release resources
-            }
-        } catch (IOException ignored) {
-        }
-        return problem;
-    }
-
-    @Override
-    protected void completeParameters() throws IOException
-    {
-        super.completeParameters();
-        String body = readBodyAsString();
-        if (body != null) {
-            addParameters(OAuth.decodeForm(body.trim()));
-        }
-    }
-
-    @Override
-    protected void dump(Map<String, Object> into) throws IOException
-    {
-        super.dump(into);
-        http.dump(into);
-    }
-
-}
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import net.oauth.OAuth;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthProblemException;
+import net.oauth.http.HttpResponseMessage;
+
+/**
+ * An HTTP response, encapsulated as an OAuthMessage.
+ * 
+ * @author John Kristian
+ */
+public class OAuthResponseMessage extends OAuthMessage
+{
+    OAuthResponseMessage(HttpResponseMessage http) throws IOException
+    {
+        super(http.method, http.url.toExternalForm(), null);
+        this.http = http;
+        getHeaders().addAll(http.headers);
+        for (Map.Entry<String, String> header : http.headers) {
+            if ("WWW-Authenticate".equalsIgnoreCase(header.getKey())) {
+                for (OAuth.Parameter parameter : decodeAuthorization(header.getValue())) {
+                    if (!"realm".equalsIgnoreCase(parameter.getKey())) {
+                        addParameter(parameter);
+                    }
+                }
+            }
+        }
+    }
+
+    private final HttpResponseMessage http;
+
+    public HttpResponseMessage getHttpResponse() {
+        return http;
+    }
+
+    @Override
+    public InputStream getBodyAsStream() throws IOException
+    {
+        return http.getBody();
+    }
+
+    @Override
+    public String getBodyEncoding()
+    {
+        return http.getContentCharset();
+    }
+
+    @Override
+    public void requireParameters(String... names) throws OAuthProblemException, IOException {
+        try {
+            super.requireParameters(names);
+        } catch (OAuthProblemException problem) {
+            problem.getParameters().putAll(getDump());
+            throw problem;
+        }
+    }
+
+    /**
+     * Encapsulate this message as an exception. Read and close the body of this
+     * message.
+     */
+    public OAuthProblemException toOAuthProblemException() throws IOException {
+        OAuthProblemException problem = new OAuthProblemException();
+        try {
+            getParameters(); // decode the response body
+        } catch (IOException ignored) {
+        }
+        problem.getParameters().putAll(getDump());
+        try {
+            InputStream b = getBodyAsStream();
+            if (b != null) {
+                b.close(); // release resources
+            }
+        } catch (IOException ignored) {
+        }
+        return problem;
+    }
+
+    @Override
+    protected void completeParameters() throws IOException
+    {
+        super.completeParameters();
+        String body = readBodyAsString();
+        if (body != null) {
+            addParameters(OAuth.decodeForm(body.trim()));
+        }
+    }
+
+    @Override
+    protected void dump(Map<String, Object> into) throws IOException
+    {
+        super.dump(into);
+        http.dump(into);
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/client/URLConnectionClient.java b/bbb-lti/src/java/net/oauth/client/URLConnectionClient.java
index 017ec8989f..e06c7544e9 100644
--- a/bbb-lti/src/java/net/oauth/client/URLConnectionClient.java
+++ b/bbb-lti/src/java/net/oauth/client/URLConnectionClient.java
@@ -1,122 +1,122 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.client;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import net.oauth.http.HttpClient;
-import net.oauth.http.HttpMessage;
-import net.oauth.http.HttpResponseMessage;
-
-/**
- * An HttpClient based on HttpURLConnection.
- * <p>
- * HttpClient3 or HttpClient4 perform better than this class, as a rule; since
- * they do things like connection pooling.  They also support reading the body
- * of an HTTP response whose status code isn't 200 (OK), which can enable your
- * application to handle problems better.
- * 
- * @author John Kristian
- */
-public class URLConnectionClient implements HttpClient {
-
-    /** Send a message to the service provider and get the response. */
-    public HttpResponseMessage execute(HttpMessage request, Map<String, Object> parameters) throws IOException {
-        final String httpMethod = request.method;
-        final Collection<Map.Entry<String, String>> addHeaders = request.headers;
-        final URL url = request.url;
-        final URLConnection connection = url.openConnection();
-        connection.setDoInput(true);
-        if (connection instanceof HttpURLConnection) {
-            HttpURLConnection http = (HttpURLConnection) connection;
-            http.setRequestMethod(httpMethod);
-            for (Map.Entry<String, Object> p : parameters.entrySet()) {
-                String name = p.getKey();
-                String value = p.getValue().toString();
-                if (FOLLOW_REDIRECTS.equals(name)) {
-                    http.setInstanceFollowRedirects(Boolean.parseBoolean(value));
-                } else if (CONNECT_TIMEOUT.equals(name)) {
-                    http.setConnectTimeout(Integer.parseInt(value));
-                } else if (READ_TIMEOUT.equals(name)) {
-                    http.setReadTimeout(Integer.parseInt(value));
-                }
-            }
-        }
-        StringBuilder headers = new StringBuilder(httpMethod);
-        {
-            headers.append(" ").append(url.getPath());
-            String query = url.getQuery();
-            if (query != null && query.length() > 0) {
-                headers.append("?").append(query);
-            }
-            headers.append(EOL);
-            for (Map.Entry<String, List<String>> header : connection
-                    .getRequestProperties().entrySet()) {
-                String key = header.getKey();
-                for (String value : header.getValue()) {
-                    headers.append(key).append(": ").append(value).append(EOL);
-                }
-            }
-        }
-        String contentLength = null;
-        for (Map.Entry<String, String> header : addHeaders) {
-            String key = header.getKey();
-            if (HttpMessage.CONTENT_LENGTH.equalsIgnoreCase(key)
-                    && connection instanceof HttpURLConnection) {
-                contentLength = header.getValue();
-            } else {
-                connection.setRequestProperty(key, header.getValue());
-            }
-            headers.append(key).append(": ").append(header.getValue()).append(EOL);
-        }
-        byte[] excerpt = null;
-        final InputStream body = request.getBody();
-        if (body != null) {
-            try {
-                if (contentLength != null) {
-                    ((HttpURLConnection) connection)
-                    .setFixedLengthStreamingMode(Integer.parseInt(contentLength));
-                }
-                connection.setDoOutput(true);
-                OutputStream output = connection.getOutputStream();
-                try {
-                    final ExcerptInputStream ex = new ExcerptInputStream(body);
-                    byte[] b = new byte[1024];
-                    for (int n; 0 < (n = ex.read(b));) {
-                        output.write(b, 0, n);
-                    }
-                    excerpt = ex.getExcerpt();
-                } finally {
-                    output.close();
-                }
-            } finally {
-                body.close();
-            }
-        }
-        return new URLConnectionResponse(request, headers.toString(), excerpt, connection);
-    }
-
-    private static final String EOL = HttpResponseMessage.EOL;
-
-}
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import net.oauth.http.HttpClient;
+import net.oauth.http.HttpMessage;
+import net.oauth.http.HttpResponseMessage;
+
+/**
+ * An HttpClient based on HttpURLConnection.
+ * <p>
+ * HttpClient3 or HttpClient4 perform better than this class, as a rule; since
+ * they do things like connection pooling.  They also support reading the body
+ * of an HTTP response whose status code isn't 200 (OK), which can enable your
+ * application to handle problems better.
+ * 
+ * @author John Kristian
+ */
+public class URLConnectionClient implements HttpClient {
+
+    /** Send a message to the service provider and get the response. */
+    public HttpResponseMessage execute(HttpMessage request, Map<String, Object> parameters) throws IOException {
+        final String httpMethod = request.method;
+        final Collection<Map.Entry<String, String>> addHeaders = request.headers;
+        final URL url = request.url;
+        final URLConnection connection = url.openConnection();
+        connection.setDoInput(true);
+        if (connection instanceof HttpURLConnection) {
+            HttpURLConnection http = (HttpURLConnection) connection;
+            http.setRequestMethod(httpMethod);
+            for (Map.Entry<String, Object> p : parameters.entrySet()) {
+                String name = p.getKey();
+                String value = p.getValue().toString();
+                if (FOLLOW_REDIRECTS.equals(name)) {
+                    http.setInstanceFollowRedirects(Boolean.parseBoolean(value));
+                } else if (CONNECT_TIMEOUT.equals(name)) {
+                    http.setConnectTimeout(Integer.parseInt(value));
+                } else if (READ_TIMEOUT.equals(name)) {
+                    http.setReadTimeout(Integer.parseInt(value));
+                }
+            }
+        }
+        StringBuilder headers = new StringBuilder(httpMethod);
+        {
+            headers.append(" ").append(url.getPath());
+            String query = url.getQuery();
+            if (query != null && query.length() > 0) {
+                headers.append("?").append(query);
+            }
+            headers.append(EOL);
+            for (Map.Entry<String, List<String>> header : connection
+                    .getRequestProperties().entrySet()) {
+                String key = header.getKey();
+                for (String value : header.getValue()) {
+                    headers.append(key).append(": ").append(value).append(EOL);
+                }
+            }
+        }
+        String contentLength = null;
+        for (Map.Entry<String, String> header : addHeaders) {
+            String key = header.getKey();
+            if (HttpMessage.CONTENT_LENGTH.equalsIgnoreCase(key)
+                    && connection instanceof HttpURLConnection) {
+                contentLength = header.getValue();
+            } else {
+                connection.setRequestProperty(key, header.getValue());
+            }
+            headers.append(key).append(": ").append(header.getValue()).append(EOL);
+        }
+        byte[] excerpt = null;
+        final InputStream body = request.getBody();
+        if (body != null) {
+            try {
+                if (contentLength != null) {
+                    ((HttpURLConnection) connection)
+                    .setFixedLengthStreamingMode(Integer.parseInt(contentLength));
+                }
+                connection.setDoOutput(true);
+                OutputStream output = connection.getOutputStream();
+                try {
+                    final ExcerptInputStream ex = new ExcerptInputStream(body);
+                    byte[] b = new byte[1024];
+                    for (int n; 0 < (n = ex.read(b));) {
+                        output.write(b, 0, n);
+                    }
+                    excerpt = ex.getExcerpt();
+                } finally {
+                    output.close();
+                }
+            } finally {
+                body.close();
+            }
+        }
+        return new URLConnectionResponse(request, headers.toString(), excerpt, connection);
+    }
+
+    private static final String EOL = HttpResponseMessage.EOL;
+
+}
diff --git a/bbb-lti/src/java/net/oauth/consumer.properties.sample b/bbb-lti/src/java/net/oauth/consumer.properties.sample
index 7235c08e53..ea26c9012a 100644
--- a/bbb-lti/src/java/net/oauth/consumer.properties.sample
+++ b/bbb-lti/src/java/net/oauth/consumer.properties.sample
@@ -1,16 +1,16 @@
-# NamedConsumerPool can gets consumer configuration parameters from a file like this.
-
-ma.gnolia.consumerKey: - Your key here -
-ma.gnolia.consumerSecret: - Your secret here -
-ma.gnolia.serviceProvider.requestTokenURL: http://ma.gnolia.com/oauth/get_request_token
-ma.gnolia.serviceProvider.userAuthorizationURL: http://ma.gnolia.com/oauth/authorize
-ma.gnolia.serviceProvider.accessTokenURL: http://ma.gnolia.com/oauth/get_access_token
-
-twitter.consumerKey: - Your key here -
-twitter.consumerSecret: - Your secret here -
-twitter.callbackURL: - Your URL here -
-twitter.consumer.oauth_signature_method: PLAINTEXT
-# There can be more consumer properties.
-twitter.serviceProvider.requestTokenURL: http://twitter.com/oauth/request_token
-twitter.serviceProvider.userAuthorizationURL: http://twitter.com/oauth/authorize
-twitter.serviceProvider.accessTokenURL: http://twitter.com/oauth/access_token
+# NamedConsumerPool can gets consumer configuration parameters from a file like this.
+
+ma.gnolia.consumerKey: - Your key here -
+ma.gnolia.consumerSecret: - Your secret here -
+ma.gnolia.serviceProvider.requestTokenURL: http://ma.gnolia.com/oauth/get_request_token
+ma.gnolia.serviceProvider.userAuthorizationURL: http://ma.gnolia.com/oauth/authorize
+ma.gnolia.serviceProvider.accessTokenURL: http://ma.gnolia.com/oauth/get_access_token
+
+twitter.consumerKey: - Your key here -
+twitter.consumerSecret: - Your secret here -
+twitter.callbackURL: - Your URL here -
+twitter.consumer.oauth_signature_method: PLAINTEXT
+# There can be more consumer properties.
+twitter.serviceProvider.requestTokenURL: http://twitter.com/oauth/request_token
+twitter.serviceProvider.userAuthorizationURL: http://twitter.com/oauth/authorize
+twitter.serviceProvider.accessTokenURL: http://twitter.com/oauth/access_token
diff --git a/bbb-lti/src/java/net/oauth/http/HttpClient.java b/bbb-lti/src/java/net/oauth/http/HttpClient.java
index 05f78e716a..afd393da0e 100644
--- a/bbb-lti/src/java/net/oauth/http/HttpClient.java
+++ b/bbb-lti/src/java/net/oauth/http/HttpClient.java
@@ -1,54 +1,56 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.http;
-
-import java.io.IOException;
-import java.util.Map;
-import net.oauth.OAuthMessage;
-
-public interface HttpClient {
-
-    /**
-     * Send an HTTP request and return the response.
-     * 
-     * @param httpParameters
-     *            HTTP client parameters, as a map from parameter name to value.
-     *            Parameter names are defined as constants below.
-     */
-    HttpResponseMessage execute(HttpMessage request, Map<String, Object> httpParameters) throws IOException;
-
-    /**
-     * The name of the parameter that is the maximum time to wait to connect to
-     * the server. (Integer msec)
-     */
-    static final String CONNECT_TIMEOUT = "connectTimeout";
-
-    /**
-     * The name of the parameter that is the maximum time to wait for response
-     * data. (Integer msec)
-     */
-    static final String READ_TIMEOUT = "readTimeout";
-
-    /** The name of the parameter to automatically follow redirects. (Boolean) */
-    static final String FOLLOW_REDIRECTS = "followRedirects";
-
-    static final String GET = OAuthMessage.GET;
-    static final String POST = OAuthMessage.POST;
-    static final String PUT = OAuthMessage.PUT;
-    static final String DELETE = OAuthMessage.DELETE;
-
-}
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.http;
+
+import java.io.IOException;
+import java.util.Map;
+import net.oauth.OAuthMessage;
+
+// TODO: move this class into oauth-core-consumer, together with HttpMessage.
+// The sticky part is deleting the method OAuthMessage.toHttpRequest.
+public interface HttpClient {
+
+    /**
+     * Send an HTTP request and return the response.
+     * 
+     * @param httpParameters
+     *            HTTP client parameters, as a map from parameter name to value.
+     *            Parameter names are defined as constants below.
+     */
+    HttpResponseMessage execute(HttpMessage request, Map<String, Object> httpParameters) throws IOException;
+
+    /**
+     * The name of the parameter that is the maximum time to wait to connect to
+     * the server. (Integer msec)
+     */
+    static final String CONNECT_TIMEOUT = "connectTimeout";
+
+    /**
+     * The name of the parameter that is the maximum time to wait for response
+     * data. (Integer msec)
+     */
+    static final String READ_TIMEOUT = "readTimeout";
+
+    /** The name of the parameter to automatically follow redirects. (Boolean) */
+    static final String FOLLOW_REDIRECTS = "followRedirects";
+
+    static final String GET = OAuthMessage.GET;
+    static final String POST = OAuthMessage.POST;
+    static final String PUT = OAuthMessage.PUT;
+    static final String DELETE = OAuthMessage.DELETE;
+
+}
diff --git a/bbb-lti/src/java/net/oauth/http/HttpMessage.java b/bbb-lti/src/java/net/oauth/http/HttpMessage.java
index 0cd8646bb1..749dbca18d 100644
--- a/bbb-lti/src/java/net/oauth/http/HttpMessage.java
+++ b/bbb-lti/src/java/net/oauth/http/HttpMessage.java
@@ -1,160 +1,222 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import net.oauth.client.ExcerptInputStream;
-
-/**
- * An HTTP request or response.
- * 
- * @author John Kristian
- */
-public class HttpMessage
-{
-
-    public HttpMessage()
-    {
-        this(null, null);
-    }
-
-    public HttpMessage(String method, URL url)
-    {
-        this(method, url, null);
-    }
-
-    public HttpMessage(String method, URL url, InputStream body)
-    {
-        this.method = method;
-        this.url = url;
-        this.body = body;
-    }
-
-    public String method;
-    public URL url;
-    public final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>();
-    protected InputStream body = null;
-
-    /**
-     * Get the value of the last header of the given name. The name is
-     * case-insensitive.
-     */
-    public final String getHeader(String name)
-    {
-        String value = null;
-        for (Map.Entry<String, String> header : headers) {
-            if (equalsIgnoreCase(name, header.getKey())) {
-                value = header.getValue();
-            }
-        }
-        return value;
-    }
-
-    /**
-     * Remove all headers of the given name. The name is case insensitive.
-     * 
-     * @return the value of the last header with that name, or null to indicate
-     *         there was no such header
-     */
-    public String removeHeaders(String name)
-    {
-        String value = null;
-        for (Iterator<Map.Entry<String, String>> i = headers.iterator(); i.hasNext();) {
-            Map.Entry<String, String> header = i.next();
-            if (equalsIgnoreCase(name, header.getKey())) {
-                value = header.getValue();
-                i.remove();
-            }
-        }
-        return value;
-    }
-
-    public final String getContentCharset()
-    {
-        return getCharset(getHeader(CONTENT_TYPE));
-    }
-
-    public final InputStream getBody() throws IOException
-    {
-        if (body == null) {
-            InputStream raw = openBody();
-            if (raw != null) {
-                body = new ExcerptInputStream(raw);
-            }
-        }
-        return body;
-    }
-
-    protected InputStream openBody() throws IOException
-    {
-        return null;
-    }
-
-    /** Put a description of this message and its origins into the given Map. */
-    public void dump(Map<String, Object> into) throws IOException
-    {
-    }
-
-    private static boolean equalsIgnoreCase(String x, String y)
-    {
-        if (x == null)
-            return y == null;
-        else
-            return x.equalsIgnoreCase(y);
-    }
-
-    private static final String getCharset(String mimeType)
-    {
-        if (mimeType != null) {
-            Matcher m = CHARSET.matcher(mimeType);
-            if (m.find()) {
-                String charset = m.group(1);
-                if (charset.length() >= 2 && charset.charAt(0) == '"'
-                        && charset.charAt(charset.length() - 1) == '"') {
-                    charset = charset.substring(1, charset.length() - 1);
-                    charset = charset.replace("\\\"", "\"");
-                }
-                return charset;
-            }
-        }
-        return DEFAULT_CHARSET;
-    }
-
-    /** The name of a dump entry whose value is the HTTP request. */
-    public static final String REQUEST = "HTTP request";
-
-    /** The name of a dump entry whose value is the HTTP response. */
-    public static final String RESPONSE = "HTTP response";
-
-    public static final String ACCEPT_ENCODING = "Accept-Encoding";
-    public static final String CONTENT_ENCODING = "Content-Encoding";
-    public static final String CONTENT_LENGTH = "Content-Length";
-    public static final String CONTENT_TYPE = "Content-Type";
-    public static final String DEFAULT_CHARSET = "ISO-8859-1";
-
-    private static final Pattern CHARSET = Pattern
-            .compile("; *charset *= *([^;\"]*|\"([^\"]|\\\\\")*\")(;|$)");
-
-}
\ No newline at end of file
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.oauth.client.ExcerptInputStream;
+import net.oauth.OAuth;
+import net.oauth.OAuthMessage;
+import net.oauth.ParameterStyle;
+
+// TODO: move this class into oauth-core-consumer, together with ExcerptInputStream.
+// The sticky part is deleting the method OAuthMessage.toHttpRequest.
+/**
+ * An HTTP request or response.
+ * 
+ * @author John Kristian
+ */
+public class HttpMessage
+{
+
+    public HttpMessage()
+    {
+        this(null, null);
+    }
+
+    public HttpMessage(String method, URL url)
+    {
+        this(method, url, null);
+    }
+
+    public HttpMessage(String method, URL url, InputStream body)
+    {
+        this.method = method;
+        this.url = url;
+        this.body = body;
+    }
+
+    public String method;
+    public URL url;
+    public final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>();
+    protected InputStream body = null;
+
+    /**
+     * Get the value of the last header of the given name. The name is
+     * case-insensitive.
+     */
+    public final String getHeader(String name)
+    {
+        String value = null;
+        for (Map.Entry<String, String> header : headers) {
+            if (equalsIgnoreCase(name, header.getKey())) {
+                value = header.getValue();
+            }
+        }
+        return value;
+    }
+
+    /**
+     * Remove all headers of the given name. The name is case insensitive.
+     * 
+     * @return the value of the last header with that name, or null to indicate
+     *         there was no such header
+     */
+    public String removeHeaders(String name)
+    {
+        String value = null;
+        for (Iterator<Map.Entry<String, String>> i = headers.iterator(); i.hasNext();) {
+            Map.Entry<String, String> header = i.next();
+            if (equalsIgnoreCase(name, header.getKey())) {
+                value = header.getValue();
+                i.remove();
+            }
+        }
+        return value;
+    }
+
+    public final String getContentCharset()
+    {
+        return getCharset(getHeader(CONTENT_TYPE));
+    }
+
+    public final InputStream getBody() throws IOException
+    {
+        if (body == null) {
+            InputStream raw = openBody();
+            if (raw != null) {
+                body = new ExcerptInputStream(raw);
+            }
+        }
+        return body;
+    }
+
+    protected InputStream openBody() throws IOException
+    {
+        return null;
+    }
+
+    /** Put a description of this message and its origins into the given Map. */
+    public void dump(Map<String, Object> into) throws IOException
+    {
+    }
+
+    /**
+     * Construct an HTTP request from this OAuth message.
+     * 
+     * @param style
+     *            where to put the OAuth parameters, within the HTTP request
+     */
+    public static HttpMessage newRequest(OAuthMessage from, ParameterStyle style) throws IOException {
+        final boolean isPost = OAuthMessage.POST.equalsIgnoreCase(from.method);
+        InputStream body = from.getBodyAsStream();
+        if (style == ParameterStyle.BODY && !(isPost && body == null)) {
+            style = ParameterStyle.QUERY_STRING;
+        }
+        String url = from.URL;
+        final List<Map.Entry<String, String>> headers = new ArrayList<Map.Entry<String, String>>(from.getHeaders());
+        switch (style) {
+        case QUERY_STRING:
+            url = OAuth.addParameters(url, from.getParameters());
+            break;
+        case BODY: {
+            byte[] form = OAuth.formEncode(from.getParameters()).getBytes(from.getBodyEncoding());
+            headers.add(new OAuth.Parameter(CONTENT_TYPE, OAuth.FORM_ENCODED));
+            headers.add(new OAuth.Parameter(CONTENT_LENGTH, form.length + ""));
+            body = new ByteArrayInputStream(form);
+            break;
+        }
+        case AUTHORIZATION_HEADER:
+            headers.add(new OAuth.Parameter("Authorization", from.getAuthorizationHeader(null)));
+            // Find the non-OAuth parameters:
+            List<Map.Entry<String, String>> others = from.getParameters();
+            if (others != null && !others.isEmpty()) {
+                others = new ArrayList<Map.Entry<String, String>>(others);
+                for (Iterator<Map.Entry<String, String>> p = others.iterator(); p.hasNext();) {
+                    if (p.next().getKey().startsWith("oauth_")) {
+                        p.remove();
+                    }
+                }
+                // Place the non-OAuth parameters elsewhere in the request:
+                if (isPost && body == null) {
+                    byte[] form = OAuth.formEncode(others).getBytes(from.getBodyEncoding());
+                    headers.add(new OAuth.Parameter(CONTENT_TYPE, OAuth.FORM_ENCODED));
+                    headers.add(new OAuth.Parameter(CONTENT_LENGTH, form.length + ""));
+                    body = new ByteArrayInputStream(form);
+                } else {
+                    url = OAuth.addParameters(url, others);
+                }
+            }
+            break;
+        }
+        HttpMessage httpRequest = new HttpMessage(from.method, new URL(url), body);
+        httpRequest.headers.addAll(headers);
+        return httpRequest;
+    }
+
+    private static boolean equalsIgnoreCase(String x, String y)
+    {
+        if (x == null)
+            return y == null;
+        else
+            return x.equalsIgnoreCase(y);
+    }
+
+    private static final String getCharset(String mimeType)
+    {
+        if (mimeType != null) {
+            Matcher m = CHARSET.matcher(mimeType);
+            if (m.find()) {
+                String charset = m.group(1);
+                if (charset.length() >= 2 && charset.charAt(0) == '"'
+                        && charset.charAt(charset.length() - 1) == '"') {
+                    charset = charset.substring(1, charset.length() - 1);
+                    charset = charset.replace("\\\"", "\"");
+                }
+                return charset;
+            }
+        }
+        return DEFAULT_CHARSET;
+    }
+
+    /** The name of a dump entry whose value is the HTTP request. */
+    public static final String REQUEST = "HTTP request";
+
+    /** The name of a dump entry whose value is the HTTP response. */
+    public static final String RESPONSE = "HTTP response";
+
+    /** The name of a dump entry whose value is the HTTP status code. */
+    public static final String STATUS_CODE = "HTTP status";
+
+    public static final String ACCEPT_ENCODING = "Accept-Encoding";
+    public static final String CONTENT_ENCODING = "Content-Encoding";
+    public static final String CONTENT_LENGTH = "Content-Length";
+    public static final String CONTENT_TYPE = "Content-Type";
+    public static final String DEFAULT_CHARSET = "ISO-8859-1";
+
+    private static final Pattern CHARSET = Pattern
+            .compile("; *charset *= *([^;\"]*|\"([^\"]|\\\\\")*\")(;|$)");
+
+}
diff --git a/bbb-lti/src/java/net/oauth/http/HttpMessageDecoder.java b/bbb-lti/src/java/net/oauth/http/HttpMessageDecoder.java
index 5c415a128e..c40d424419 100644
--- a/bbb-lti/src/java/net/oauth/http/HttpMessageDecoder.java
+++ b/bbb-lti/src/java/net/oauth/http/HttpMessageDecoder.java
@@ -1,94 +1,96 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.http;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.InflaterInputStream;
-
-/** A decorator that handles Content-Encoding. */
-public class HttpMessageDecoder extends HttpResponseMessage {
-
-    /**
-     * Decode the given message if necessary and possible.
-     * 
-     * @return a decorator that decodes the body of the given message; or the
-     *         given message if this class can't decode it.
-     */
-    public static HttpResponseMessage decode(HttpResponseMessage message)
-            throws IOException {
-        if (message != null) {
-            String encoding = getEncoding(message);
-            if (encoding != null) {
-                return new HttpMessageDecoder(message, encoding);
-            }
-        }
-        return message;
-    }
-
-    public static final String GZIP = "gzip";
-    public static final String DEFLATE = "deflate";
-    public static final String ACCEPTED = GZIP + "," + DEFLATE;
-
-    private static String getEncoding(HttpMessage message) {
-        String encoding = message.getHeader(CONTENT_ENCODING);
-        if (encoding == null) {
-            // That's easy.
-        } else if (GZIP.equalsIgnoreCase(encoding)
-                || ("x-" + GZIP).equalsIgnoreCase(encoding)) {
-            return GZIP;
-        } else if (DEFLATE.equalsIgnoreCase(encoding)) {
-            return DEFLATE;
-        }
-        return null;
-    }
-
-    private HttpMessageDecoder(HttpResponseMessage in, String encoding)
-            throws IOException {
-        super(in.method, in.url);
-        this.headers.addAll(in.headers);
-        removeHeaders(CONTENT_ENCODING); // handled here
-        removeHeaders(CONTENT_LENGTH); // unpredictable
-        InputStream body = in.getBody();
-        if (body != null) {
-            if (encoding == GZIP) {
-                body = new GZIPInputStream(body);
-            } else if (encoding == DEFLATE) {
-                body = new InflaterInputStream(body);
-            } else {
-                assert false;
-            }
-        }
-        this.body = body;
-        this.in = in;
-    }
-
-    private final HttpResponseMessage in;
-
-    @Override
-    public void dump(Map<String, Object> into) throws IOException {
-        in.dump(into);
-    }
-
-    @Override
-    public int getStatusCode() throws IOException {
-        return in.getStatusCode();
-    }
-
-}
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+
+// TODO: move this class into oauth-core-consumer, together with HttpMessage.
+// The sticky part is deleting the method OAuthMessage.toHttpRequest.
+/** A decorator that handles Content-Encoding. */
+public class HttpMessageDecoder extends HttpResponseMessage {
+
+    /**
+     * Decode the given message if necessary and possible.
+     * 
+     * @return a decorator that decodes the body of the given message; or the
+     *         given message if this class can't decode it.
+     */
+    public static HttpResponseMessage decode(HttpResponseMessage message)
+            throws IOException {
+        if (message != null) {
+            String encoding = getEncoding(message);
+            if (encoding != null) {
+                return new HttpMessageDecoder(message, encoding);
+            }
+        }
+        return message;
+    }
+
+    public static final String GZIP = "gzip";
+    public static final String DEFLATE = "deflate";
+    public static final String ACCEPTED = GZIP + "," + DEFLATE;
+
+    private static String getEncoding(HttpMessage message) {
+        String encoding = message.getHeader(CONTENT_ENCODING);
+        if (encoding == null) {
+            // That's easy.
+        } else if (GZIP.equalsIgnoreCase(encoding)
+                || ("x-" + GZIP).equalsIgnoreCase(encoding)) {
+            return GZIP;
+        } else if (DEFLATE.equalsIgnoreCase(encoding)) {
+            return DEFLATE;
+        }
+        return null;
+    }
+
+    private HttpMessageDecoder(HttpResponseMessage in, String encoding)
+            throws IOException {
+        super(in.method, in.url);
+        this.headers.addAll(in.headers);
+        removeHeaders(CONTENT_ENCODING); // handled here
+        removeHeaders(CONTENT_LENGTH); // unpredictable
+        InputStream body = in.getBody();
+        if (body != null) {
+            if (encoding == GZIP) {
+                body = new GZIPInputStream(body);
+            } else if (encoding == DEFLATE) {
+                body = new InflaterInputStream(body);
+            } else {
+                assert false;
+            }
+        }
+        this.body = body;
+        this.in = in;
+    }
+
+    private final HttpResponseMessage in;
+
+    @Override
+    public void dump(Map<String, Object> into) throws IOException {
+        in.dump(into);
+    }
+
+    @Override
+    public int getStatusCode() throws IOException {
+        return in.getStatusCode();
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/http/HttpResponseMessage.java b/bbb-lti/src/java/net/oauth/http/HttpResponseMessage.java
index 2366f122c3..491252fa97 100644
--- a/bbb-lti/src/java/net/oauth/http/HttpResponseMessage.java
+++ b/bbb-lti/src/java/net/oauth/http/HttpResponseMessage.java
@@ -1,58 +1,60 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.http;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.Map;
-
-/**
- * An HTTP response.
- * 
- * @author John Kristian
- */
-public abstract class HttpResponseMessage extends HttpMessage {
-
-    protected HttpResponseMessage(String method, URL url) {
-        super(method, url);
-    }
-
-    @Override
-    public void dump(Map<String, Object> into) throws IOException {
-        super.dump(into);
-        into.put(STATUS_CODE, Integer.valueOf(getStatusCode()));
-        String location = getHeader(LOCATION);
-        if (location != null) {
-            into.put(LOCATION, location);
-        }
-    }
-
-    public abstract int getStatusCode() throws IOException;
-
-    /** The name of a dump entry whose value is the response Location header. */
-    public static final String LOCATION = "Location";
-
-    /** The name of a dump entry whose value is the HTTP status code. */
-    public static final String STATUS_CODE = "HTTP status";
-
-    /** The statusCode that indicates a normal outcome. */
-    public static final int STATUS_OK = 200;
-
-    /** The standard end-of-line marker in an HTTP message. */
-    public static final String EOL = "\r\n";
-
-}
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.http;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+
+// TODO: move this class into oauth-core-consumer, together with HttpMessage.
+// The sticky part is deleting the method OAuthMessage.toHttpRequest.
+/**
+ * An HTTP response.
+ * 
+ * @author John Kristian
+ */
+public abstract class HttpResponseMessage extends HttpMessage {
+
+    protected HttpResponseMessage(String method, URL url) {
+        super(method, url);
+    }
+
+    @Override
+    public void dump(Map<String, Object> into) throws IOException {
+        super.dump(into);
+        into.put(STATUS_CODE, Integer.valueOf(getStatusCode()));
+        String location = getHeader(LOCATION);
+        if (location != null) {
+            into.put(LOCATION, location);
+        }
+    }
+
+    public abstract int getStatusCode() throws IOException;
+
+    /** The name of a dump entry whose value is the response Location header. */
+    public static final String LOCATION = "Location";
+
+    /** The name of a dump entry whose value is the HTTP status code. */
+    public static final String STATUS_CODE = "HTTP status";
+
+    /** The statusCode that indicates a normal outcome. */
+    public static final int STATUS_OK = 200;
+
+    /** The standard end-of-line marker in an HTTP message. */
+    public static final String EOL = "\r\n";
+
+}
diff --git a/bbb-lti/src/java/net/oauth/server/HttpRequestMessage.java b/bbb-lti/src/java/net/oauth/server/HttpRequestMessage.java
index bab3358712..2eb5195c2a 100644
--- a/bbb-lti/src/java/net/oauth/server/HttpRequestMessage.java
+++ b/bbb-lti/src/java/net/oauth/server/HttpRequestMessage.java
@@ -1,92 +1,92 @@
-/*
- * Copyright 2008 Netflix, Inc.
- *
- * 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 net.oauth.server;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import net.oauth.OAuth;
-import net.oauth.OAuthMessage;
-
-/**
- * An HttpServletRequest, encapsulated as an OAuthMessage.
- * 
- * @author John Kristian
- */
-public class HttpRequestMessage extends OAuthMessage {
-
-    public HttpRequestMessage(HttpServletRequest request, String URL) {
-        super(request.getMethod(), URL, getParameters(request));
-        this.request = request;
-        copyHeaders(request, getHeaders());
-    }
-
-    private final HttpServletRequest request;
-
-    @Override
-    public InputStream getBodyAsStream() throws IOException {
-        return request.getInputStream();
-    }
-
-    @Override
-    public String getBodyEncoding() {
-        return request.getCharacterEncoding();
-    }
-
-    private static void copyHeaders(HttpServletRequest request, Collection<Map.Entry<String, String>> into) {
-        Enumeration<String> names = request.getHeaderNames();
-        if (names != null) {
-            while (names.hasMoreElements()) {
-                String name = names.nextElement();
-                Enumeration<String> values = request.getHeaders(name);
-                if (values != null) {
-                    while (values.hasMoreElements()) {
-                        into.add(new OAuth.Parameter(name, values.nextElement()));
-                    }
-                }
-            }
-        }
-    }
-
-    public static List<OAuth.Parameter> getParameters(HttpServletRequest request) {
-        List<OAuth.Parameter> list = new ArrayList<OAuth.Parameter>();
-        for (Enumeration<String> headers = request.getHeaders("Authorization"); headers != null
-                && headers.hasMoreElements();) {
-            String header = headers.nextElement();
-            for (OAuth.Parameter parameter : OAuthMessage
-                    .decodeAuthorization(header)) {
-                if (!"realm".equalsIgnoreCase(parameter.getKey())) {
-                    list.add(parameter);
-                }
-            }
-        }
-        for (Object e : request.getParameterMap().entrySet()) {
-            Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) e;
-            String name = entry.getKey();
-            for (String value : entry.getValue()) {
-                list.add(new OAuth.Parameter(name, value));
-            }
-        }
-        return list;
-    }
-
-}
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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 net.oauth.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import net.oauth.OAuth;
+import net.oauth.OAuthMessage;
+
+/**
+ * An HttpServletRequest, encapsulated as an OAuthMessage.
+ * 
+ * @author John Kristian
+ */
+public class HttpRequestMessage extends OAuthMessage {
+
+    public HttpRequestMessage(HttpServletRequest request, String URL) {
+        super(request.getMethod(), URL, getParameters(request));
+        this.request = request;
+        copyHeaders(request, getHeaders());
+    }
+
+    private final HttpServletRequest request;
+
+    @Override
+    public InputStream getBodyAsStream() throws IOException {
+        return request.getInputStream();
+    }
+
+    @Override
+    public String getBodyEncoding() {
+        return request.getCharacterEncoding();
+    }
+
+    private static void copyHeaders(HttpServletRequest request, Collection<Map.Entry<String, String>> into) {
+        Enumeration<String> names = request.getHeaderNames();
+        if (names != null) {
+            while (names.hasMoreElements()) {
+                String name = names.nextElement();
+                Enumeration<String> values = request.getHeaders(name);
+                if (values != null) {
+                    while (values.hasMoreElements()) {
+                        into.add(new OAuth.Parameter(name, values.nextElement()));
+                    }
+                }
+            }
+        }
+    }
+
+    public static List<OAuth.Parameter> getParameters(HttpServletRequest request) {
+        List<OAuth.Parameter> list = new ArrayList<OAuth.Parameter>();
+        for (Enumeration<String> headers = request.getHeaders("Authorization"); headers != null
+                && headers.hasMoreElements();) {
+            String header = headers.nextElement();
+            for (OAuth.Parameter parameter : OAuthMessage
+                    .decodeAuthorization(header)) {
+                if (!"realm".equalsIgnoreCase(parameter.getKey())) {
+                    list.add(parameter);
+                }
+            }
+        }
+        for (Object e : request.getParameterMap().entrySet()) {
+            Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) e;
+            String name = entry.getKey();
+            for (String value : entry.getValue()) {
+                list.add(new OAuth.Parameter(name, value));
+            }
+        }
+        return list;
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/server/OAuthServlet.java b/bbb-lti/src/java/net/oauth/server/OAuthServlet.java
index 9054bea3eb..2ae266a7d5 100644
--- a/bbb-lti/src/java/net/oauth/server/OAuthServlet.java
+++ b/bbb-lti/src/java/net/oauth/server/OAuthServlet.java
@@ -1,158 +1,159 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth.server;
-
-import java.io.IOException;
-import java.util.Map;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import net.oauth.OAuth;
-import net.oauth.OAuthMessage;
-import net.oauth.OAuthProblemException;
-import net.oauth.http.HttpResponseMessage;
-
-/**
- * Utility methods for servlets that implement OAuth.
- * 
- * @author John Kristian
- */
-public class OAuthServlet {
-
-    /**
-     * Extract the parts of the given request that are relevant to OAuth.
-     * Parameters include OAuth Authorization headers and the usual request
-     * parameters in the query string and/or form encoded body. The header
-     * parameters come first, followed by the rest in the order they came from
-     * request.getParameterMap().
-     * 
-     * @param URL
-     *            the official URL of this service; that is the URL a legitimate
-     *            client would use to compute the digital signature. If this
-     *            parameter is null, this method will try to reconstruct the URL
-     *            from the HTTP request; which may be wrong in some cases.
-     */
-    public static OAuthMessage getMessage(HttpServletRequest request, String URL) {
-        if (URL == null) {
-            URL = request.getRequestURL().toString();
-        }
-        int q = URL.indexOf('?');
-        if (q >= 0) {
-            URL = URL.substring(0, q);
-            // The query string parameters will be included in
-            // the result from getParameters(request).
-        }
-        return new HttpRequestMessage(request, URL);
-    }
-
-    /** Reconstruct the requested URL, complete with query string (if any). */
-    public static String getRequestURL(HttpServletRequest request) {
-        StringBuffer url = request.getRequestURL();
-        String queryString = request.getQueryString();
-        if (queryString != null) {
-            url.append("?").append(queryString);
-        }
-        return url.toString();
-    }
-
-    public static void handleException(HttpServletResponse response,
-            Exception e, String realm) throws IOException, ServletException {
-        handleException(response, e, realm, true);
-    }
-
-    public static void handleException(HttpServletResponse response,
-            Exception e, String realm, boolean sendBody) throws IOException,
-            ServletException {
-        if (e instanceof OAuthProblemException) {
-            OAuthProblemException problem = (OAuthProblemException) e;
-            Object httpCode = problem.getParameters().get(HttpResponseMessage.STATUS_CODE);
-            if (httpCode == null) {
-                httpCode = PROBLEM_TO_HTTP_CODE.get(problem.getProblem());
-            }
-            if (httpCode == null) {
-                httpCode = SC_FORBIDDEN;
-            }
-            response.reset();
-            response.setStatus(Integer.parseInt(httpCode.toString()));
-            OAuthMessage message = new OAuthMessage(null, null, problem
-                    .getParameters().entrySet());
-            response.addHeader("WWW-Authenticate", message
-                    .getAuthorizationHeader(realm));
-            if (sendBody) {
-                sendForm(response, message.getParameters());
-            }
-        } else if (e instanceof IOException) {
-            throw (IOException) e;
-        } else if (e instanceof ServletException) {
-            throw (ServletException) e;
-        } else if (e instanceof RuntimeException) {
-            throw (RuntimeException) e;
-        } else {
-            throw new ServletException(e);
-        }
-    }
-
-    private static final Integer SC_FORBIDDEN = new Integer(
-            HttpServletResponse.SC_FORBIDDEN);
-
-    private static final Map<String, Integer> PROBLEM_TO_HTTP_CODE = OAuth.Problems.TO_HTTP_CODE;
-
-    /** Send the given parameters as a form-encoded response body. */
-    public static void sendForm(HttpServletResponse response,
-            Iterable<? extends Map.Entry> parameters) throws IOException {
-        response.resetBuffer();
-        response.setContentType(OAuth.FORM_ENCODED + ";charset="
-                + OAuth.ENCODING);
-        OAuth.formEncode(parameters, response.getOutputStream());
-    }
-
-    /**
-     * Return the HTML representation of the given plain text. Characters that
-     * would have special significance in HTML are replaced by <a
-     * href="http://www.w3.org/TR/html401/sgml/entities.html">character entity
-     * references</a>. Whitespace is not converted.
-     */
-    public static String htmlEncode(String s) {
-        if (s == null) {
-            return null;
-        }
-        StringBuilder html = new StringBuilder(s.length());
-        for (char c : s.toCharArray()) {
-            switch (c) {
-            case '<':
-                html.append("&lt;");
-                break;
-            case '>':
-                html.append("&gt;");
-                break;
-            case '&':
-                html.append("&amp;");
-                // This also takes care of numeric character references;
-                // for example &#169 becomes &amp;#169.
-                break;
-            case '"':
-                html.append("&quot;");
-                break;
-            default:
-                html.append(c);
-                break;
-            }
-        }
-        return html.toString();
-    }
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth.server;
+
+import net.oauth.http.HttpMessage;
+
+import java.io.IOException;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.oauth.OAuth;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthProblemException;
+
+/**
+ * Utility methods for servlets that implement OAuth.
+ * 
+ * @author John Kristian
+ */
+public class OAuthServlet {
+
+    /**
+     * Extract the parts of the given request that are relevant to OAuth.
+     * Parameters include OAuth Authorization headers and the usual request
+     * parameters in the query string and/or form encoded body. The header
+     * parameters come first, followed by the rest in the order they came from
+     * request.getParameterMap().
+     * 
+     * @param URL
+     *            the official URL of this service; that is the URL a legitimate
+     *            client would use to compute the digital signature. If this
+     *            parameter is null, this method will try to reconstruct the URL
+     *            from the HTTP request; which may be wrong in some cases.
+     */
+    public static OAuthMessage getMessage(HttpServletRequest request, String URL) {
+        if (URL == null) {
+            URL = request.getRequestURL().toString();
+        }
+        int q = URL.indexOf('?');
+        if (q >= 0) {
+            URL = URL.substring(0, q);
+            // The query string parameters will be included in
+            // the result from getParameters(request).
+        }
+        return new HttpRequestMessage(request, URL);
+    }
+
+    /** Reconstruct the requested URL, complete with query string (if any). */
+    public static String getRequestURL(HttpServletRequest request) {
+        StringBuffer url = request.getRequestURL();
+        String queryString = request.getQueryString();
+        if (queryString != null) {
+            url.append("?").append(queryString);
+        }
+        return url.toString();
+    }
+
+    public static void handleException(HttpServletResponse response,
+            Exception e, String realm) throws IOException, ServletException {
+        handleException(response, e, realm, true);
+    }
+
+    public static void handleException(HttpServletResponse response,
+            Exception e, String realm, boolean sendBody) throws IOException,
+            ServletException {
+        if (e instanceof OAuthProblemException) {
+            OAuthProblemException problem = (OAuthProblemException) e;
+            Object httpCode = problem.getParameters().get(HttpMessage.STATUS_CODE);
+            if (httpCode == null) {
+                httpCode = PROBLEM_TO_HTTP_CODE.get(problem.getProblem());
+            }
+            if (httpCode == null) {
+                httpCode = SC_FORBIDDEN;
+            }
+            response.reset();
+            response.setStatus(Integer.parseInt(httpCode.toString()));
+            OAuthMessage message = new OAuthMessage(null, null, problem
+                    .getParameters().entrySet());
+            response.addHeader("WWW-Authenticate", message
+                    .getAuthorizationHeader(realm));
+            if (sendBody) {
+                sendForm(response, message.getParameters());
+            }
+        } else if (e instanceof IOException) {
+            throw (IOException) e;
+        } else if (e instanceof ServletException) {
+            throw (ServletException) e;
+        } else if (e instanceof RuntimeException) {
+            throw (RuntimeException) e;
+        } else {
+            throw new ServletException(e);
+        }
+    }
+
+    private static final Integer SC_FORBIDDEN = new Integer(
+            HttpServletResponse.SC_FORBIDDEN);
+
+    private static final Map<String, Integer> PROBLEM_TO_HTTP_CODE = OAuth.Problems.TO_HTTP_CODE;
+
+    /** Send the given parameters as a form-encoded response body. */
+    public static void sendForm(HttpServletResponse response,
+            Iterable<? extends Map.Entry> parameters) throws IOException {
+        response.resetBuffer();
+        response.setContentType(OAuth.FORM_ENCODED + ";charset="
+                + OAuth.ENCODING);
+        OAuth.formEncode(parameters, response.getOutputStream());
+    }
+
+    /**
+     * Return the HTML representation of the given plain text. Characters that
+     * would have special significance in HTML are replaced by <a
+     * href="http://www.w3.org/TR/html401/sgml/entities.html">character entity
+     * references</a>. Whitespace is not converted.
+     */
+    public static String htmlEncode(String s) {
+        if (s == null) {
+            return null;
+        }
+        StringBuilder html = new StringBuilder(s.length());
+        for (char c : s.toCharArray()) {
+            switch (c) {
+            case '<':
+                html.append("&lt;");
+                break;
+            case '>':
+                html.append("&gt;");
+                break;
+            case '&':
+                html.append("&amp;");
+                // This also takes care of numeric character references;
+                // for example &#169 becomes &amp;#169.
+                break;
+            case '"':
+                html.append("&quot;");
+                break;
+            default:
+                html.append(c);
+                break;
+            }
+        }
+        return html.toString();
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/Base64.java b/bbb-lti/src/java/net/oauth/signature/Base64.java
index 6fc39023ac..fd5ea5fa95 100644
--- a/bbb-lti/src/java/net/oauth/signature/Base64.java
+++ b/bbb-lti/src/java/net/oauth/signature/Base64.java
@@ -1,714 +1,714 @@
-/*
- * Copyright 2001-2008 The Apache Software Foundation.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.oauth.signature;
-
-import java.io.UnsupportedEncodingException;
-import java.math.BigInteger;
-
-/**
- * Provides Base64 encoding and decoding as defined by RFC 2045.
- * 
- * <p>
- * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
- * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
- * </p>
- * 
- * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
- * @author Apache Software Foundation
- * @author John Kristian
- */
-class Base64 {
-    /**
-     * Chunk size per RFC 2045 section 6.8.
-     * 
-     * <p>
-     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
-     * equal signs.
-     * </p>
-     * 
-     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
-     */
-    static final int CHUNK_SIZE = 76;
-
-    /**
-     * Chunk separator per RFC 2045 section 2.1.
-     * 
-     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
-     */
-    static final byte[] CHUNK_SEPARATOR = {'\r','\n'};
-
-    /**
-     * This array is a lookup table that translates 6-bit positive integer
-     * index values into their "Base64 Alphabet" equivalents as specified
-     * in Table 1 of RFC 2045.
-     *
-     * Thanks to "commons" project in ws.apache.org for this code. 
-     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
-     */
-    private static final byte[] intToBase64 = {
-            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
-            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
-            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
-            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
-    };
-
-    /**
-     * Byte used to pad output.
-     */
-    private static final byte PAD = '=';
-
-    /**
-     * This array is a lookup table that translates unicode characters
-     * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
-     * into their 6-bit positive integer equivalents.  Characters that
-     * are not in the Base64 alphabet but fall within the bounds of the
-     * array are translated to -1.
-     *
-     * Thanks to "commons" project in ws.apache.org for this code.
-     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ 
-     */
-    private static final byte[] base64ToInt = {
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-            -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
-            55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
-            5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
-            24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
-            35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
-    };
-
-    /** Mask used to extract 6 bits, used when encoding */
-    private static final int MASK_6BITS = 0x3f;
-
-    /** Mask used to extract 8 bits, used in decoding base64 bytes */
-    private static final int MASK_8BITS = 0xff;
-
-    // The static final fields above are used for the original static byte[] methods on Base64.
-    // The private member fields below are used with the new streaming approach, which requires
-    // some state be preserved between calls of encode() and decode().
-
-
-    /**
-     * Line length for encoding.  Not used when decoding.  A value of zero or less implies
-     * no chunking of the base64 encoded data.
-     */
-    private final int lineLength;
-
-    /**
-     * Line separator for encoding.  Not used when decoding.  Only used if lineLength > 0.
-     */
-    private final byte[] lineSeparator;
-
-    /**
-     * Convenience variable to help us determine when our buffer is going to run out of
-     * room and needs resizing.  <code>decodeSize = 3 + lineSeparator.length;</code>
-     */
-    private final int decodeSize;
-
-    /**
-     * Convenience variable to help us determine when our buffer is going to run out of
-     * room and needs resizing.  <code>encodeSize = 4 + lineSeparator.length;</code>
-     */
-    private final int encodeSize;
-
-    /**
-     * Buffer for streaming. 
-     */
-    private byte[] buf;
-
-    /**
-     * Position where next character should be written in the buffer.
-     */
-    private int pos;
-
-    /**
-     * Position where next character should be read from the buffer.
-     */
-    private int readPos;
-
-    /**
-     * Variable tracks how many characters have been written to the current line.
-     * Only used when encoding.  We use it to make sure each encoded line never
-     * goes beyond lineLength (if lineLength > 0).
-     */
-    private int currentLinePos;
-
-    /**
-     * Writes to the buffer only occur after every 3 reads when encoding, an
-     * every 4 reads when decoding.  This variable helps track that.
-     */
-    private int modulus;
-
-    /**
-     * Boolean flag to indicate the EOF has been reached.  Once EOF has been
-     * reached, this Base64 object becomes useless, and must be thrown away.
-     */
-    private boolean eof;
-
-    /**
-     * Place holder for the 3 bytes we're dealing with for our base64 logic.
-     * Bitwise operations store and extract the base64 encoding or decoding from
-     * this variable.
-     */
-    private int x;
-
-    /**
-     * Default constructor:  lineLength is 76, and the lineSeparator is CRLF
-     * when encoding, and all forms can be decoded.
-     */
-    public Base64() {
-        this(CHUNK_SIZE, CHUNK_SEPARATOR);
-    }
-
-    /**
-     * <p>
-     * Consumer can use this constructor to choose a different lineLength
-     * when encoding (lineSeparator is still CRLF).  All forms of data can
-     * be decoded.
-     * </p><p>
-     * Note:  lineLengths that aren't multiples of 4 will still essentially
-     * end up being multiples of 4 in the encoded data.
-     * </p>
-     *
-     * @param lineLength each line of encoded data will be at most this long
-     * (rounded up to nearest multiple of 4). 
-     * If lineLength <= 0, then the output will not be divided into lines (chunks).  
-     * Ignored when decoding.
-     */
-    public Base64(int lineLength) {
-        this(lineLength, CHUNK_SEPARATOR);
-    }
-
-    /**
-     * <p>
-     * Consumer can use this constructor to choose a different lineLength
-     * and lineSeparator when encoding.  All forms of data can
-     * be decoded.
-     * </p><p>
-     * Note:  lineLengths that aren't multiples of 4 will still essentially
-     * end up being multiples of 4 in the encoded data.
-     * </p>
-     * @param lineLength    Each line of encoded data will be at most this long
-     *                      (rounded up to nearest multiple of 4).  Ignored when decoding.
-     *                      If <= 0, then output will not be divided into lines (chunks).
-     * @param lineSeparator Each line of encoded data will end with this
-     *                      sequence of bytes.
-     *                      If lineLength <= 0, then the lineSeparator is not used.
-     * @throws IllegalArgumentException The provided lineSeparator included
-     *                                  some base64 characters.  That's not going to work!
-     */
-    public Base64(int lineLength, byte[] lineSeparator) {
-        this.lineLength = lineLength;
-        this.lineSeparator = new byte[lineSeparator.length];
-        System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
-        if (lineLength > 0) {
-            this.encodeSize = 4 + lineSeparator.length;
-        } else {
-            this.encodeSize = 4;
-        }
-        this.decodeSize = encodeSize - 1;
-        if (containsBase64Byte(lineSeparator)) {
-            String sep;
-            try {
-                sep = new String(lineSeparator, "UTF-8");
-            } catch (UnsupportedEncodingException uee) {
-                sep = new String(lineSeparator);
-            }
-            throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
-        }
-    }
-
-    /**
-     * Returns true if this Base64 object has buffered data for reading.
-     *
-     * @return true if there is Base64 object still available for reading.
-     */
-    boolean hasData() { return buf != null; }
-
-    /**
-     * Returns the amount of buffered data available for reading.
-     *
-     * @return The amount of buffered data available for reading.
-     */
-    int avail() { return buf != null ? pos - readPos : 0; }
-
-    /** Doubles our buffer. */
-    private void resizeBuf() {
-        if (buf == null) {
-            buf = new byte[8192];
-            pos = 0;
-            readPos = 0;
-        } else {
-            byte[] b = new byte[buf.length * 2];
-            System.arraycopy(buf, 0, b, 0, buf.length);
-            buf = b;
-        }
-    }
-
-    /**
-     * Extracts buffered data into the provided byte[] array, starting
-     * at position bPos, up to a maximum of bAvail bytes.  Returns how
-     * many bytes were actually extracted.
-     *
-     * @param b      byte[] array to extract the buffered data into.
-     * @param bPos   position in byte[] array to start extraction at.
-     * @param bAvail amount of bytes we're allowed to extract.  We may extract
-     *               fewer (if fewer are available).
-     * @return The number of bytes successfully extracted into the provided
-     *         byte[] array.
-     */
-    int readResults(byte[] b, int bPos, int bAvail) {
-        if (buf != null) {
-            int len = Math.min(avail(), bAvail);
-            if (buf != b) {
-                System.arraycopy(buf, readPos, b, bPos, len);
-                readPos += len;
-                if (readPos >= pos) {
-                    buf = null;
-                }
-            } else {
-                // Re-using the original consumer's output array is only
-                // allowed for one round.
-                buf = null;
-            }
-            return len;
-        } else {
-            return eof ? -1 : 0;
-        }
-    }
-
-    /**
-     * Small optimization where we try to buffer directly to the consumer's
-     * output array for one round (if consumer calls this method first!) instead
-     * of starting our own buffer.
-     *
-     * @param out byte[] array to buffer directly to.
-     * @param outPos Position to start buffering into.
-     * @param outAvail Amount of bytes available for direct buffering.
-     */
-    void setInitialBuffer(byte[] out, int outPos, int outAvail) {
-        // We can re-use consumer's original output array under
-        // special circumstances, saving on some System.arraycopy().
-        if (out != null && out.length == outAvail) {
-            buf = out;
-            pos = outPos;
-            readPos = outPos;
-        }
-    }
-
-    /**
-     * <p>
-     * Encodes all of the provided data, starting at inPos, for inAvail bytes.
-     * Must be called at least twice:  once with the data to encode, and once
-     * with inAvail set to "-1" to alert encoder that EOF has been reached,
-     * so flush last remaining bytes (if not multiple of 3).
-     * </p><p>
-     * Thanks to "commons" project in ws.apache.org for the bitwise operations,
-     * and general approach.
-     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
-     * </p>
-     *
-     * @param in byte[] array of binary data to base64 encode.
-     * @param inPos Position to start reading data from.
-     * @param inAvail Amount of bytes available from input for encoding.
-     */
-    void encode(byte[] in, int inPos, int inAvail) {
-        if (eof) {
-            return;
-        }
-
-        // inAvail < 0 is how we're informed of EOF in the underlying data we're
-        // encoding.
-        if (inAvail < 0) {
-            eof = true;
-            if (buf == null || buf.length - pos < encodeSize) {
-                resizeBuf();
-            }
-            switch (modulus) {
-                case 1:
-                    buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS];
-                    buf[pos++] = intToBase64[(x << 4) & MASK_6BITS];
-                    buf[pos++] = PAD;
-                    buf[pos++] = PAD;
-                    break;
-
-                case 2:
-                    buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS];
-                    buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS];
-                    buf[pos++] = intToBase64[(x << 2) & MASK_6BITS];
-                    buf[pos++] = PAD;
-                    break;
-            }
-            if (lineLength > 0) {
-                System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
-                pos += lineSeparator.length;
-            }
-        } else {
-            for (int i = 0; i < inAvail; i++) {
-                if (buf == null || buf.length - pos < encodeSize) {
-                    resizeBuf();
-                }
-                modulus = (++modulus) % 3;
-                int b = in[inPos++];
-                if (b < 0) { b += 256; }
-                x = (x << 8) + b;
-                if (0 == modulus) {
-                    buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS];
-                    buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS];
-                    buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS];
-                    buf[pos++] = intToBase64[x & MASK_6BITS];
-                    currentLinePos += 4;
-                    if (lineLength > 0 && lineLength <= currentLinePos) {
-                        System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
-                        pos += lineSeparator.length;
-                        currentLinePos = 0;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * <p>
-     * Decodes all of the provided data, starting at inPos, for inAvail bytes.
-     * Should be called at least twice:  once with the data to decode, and once
-     * with inAvail set to "-1" to alert decoder that EOF has been reached.
-     * The "-1" call is not necessary when decoding, but it doesn't hurt, either.
-     * </p><p>
-     * Ignores all non-base64 characters.  This is how chunked (e.g. 76 character)
-     * data is handled, since CR and LF are silently ignored, but has implications
-     * for other bytes, too.  This method subscribes to the garbage-in, garbage-out
-     * philosophy:  it will not check the provided data for validity.
-     * </p><p>
-     * Thanks to "commons" project in ws.apache.org for the bitwise operations,
-     * and general approach.
-     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
-     * </p>
-
-     * @param in byte[] array of ascii data to base64 decode.
-     * @param inPos Position to start reading data from.
-     * @param inAvail Amount of bytes available from input for encoding.
-     */    
-    void decode(byte[] in, int inPos, int inAvail) {
-        if (eof) {
-            return;
-        }
-        if (inAvail < 0) {
-            eof = true;
-        }
-        for (int i = 0; i < inAvail; i++) {
-            if (buf == null || buf.length - pos < decodeSize) {
-                resizeBuf();
-            }
-            byte b = in[inPos++];
-            if (b == PAD) {
-                x = x << 6;
-                switch (modulus) {
-                    case 2:
-                        x = x << 6;
-                        buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                        break;
-                    case 3:
-                        buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                        buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                        break;
-                }
-                // WE'RE DONE!!!!
-                eof = true;
-                return;
-            } else {
-                if (b >= 0 && b < base64ToInt.length) {
-                    int result = base64ToInt[b];
-                    if (result >= 0) {
-                        modulus = (++modulus) % 4;
-                        x = (x << 6) + result;
-                        if (modulus == 0) {
-                            buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                            buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                            buf[pos++] = (byte) (x & MASK_8BITS);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns whether or not the <code>octet</code> is in the base 64 alphabet.
-     * 
-     * @param octet
-     *            The value to test
-     * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
-     */
-    public static boolean isBase64(byte octet) {
-        return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1);
-    }
-
-    /**
-     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
-     * Currently the method treats whitespace as valid.
-     * 
-     * @param arrayOctet
-     *            byte array to test
-     * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is
-     *         empty; false, otherwise
-     */
-    public static boolean isArrayByteBase64(byte[] arrayOctet) {
-        for (int i = 0; i < arrayOctet.length; i++) {
-            if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /*
-     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
-     * 
-     * @param arrayOctet
-     *            byte array to test
-     * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
-     */
-    private static boolean containsBase64Byte(byte[] arrayOctet) {
-        for (int i = 0; i < arrayOctet.length; i++) {
-            if (isBase64(arrayOctet[i])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Encodes binary data using the base64 algorithm but does not chunk the output.
-     * 
-     * @param binaryData
-     *            binary data to encode
-     * @return Base64 characters
-     */
-    public static byte[] encodeBase64(byte[] binaryData) {
-        return encodeBase64(binaryData, false);
-    }
-
-    /**
-     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
-     * 
-     * @param binaryData
-     *            binary data to encode
-     * @return Base64 characters chunked in 76 character blocks
-     */
-    public static byte[] encodeBase64Chunked(byte[] binaryData) {
-        return encodeBase64(binaryData, true);
-    }
-
-    /**
-     * Decodes a byte[] containing containing characters in the Base64 alphabet.
-     * 
-     * @param pArray
-     *            A byte array containing Base64 character data
-     * @return a byte array containing binary data
-     */
-    public byte[] decode(byte[] pArray) {
-        return decodeBase64(pArray);
-    }
-
-    /**
-     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
-     * 
-     * @param binaryData
-     *            Array containing binary data to encode.
-     * @param isChunked
-     *            if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
-     * @return Base64-encoded data.
-     * @throws IllegalArgumentException
-     *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
-     */
-    public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
-        if (binaryData == null || binaryData.length == 0) {
-            return binaryData;
-        }
-        Base64 b64 = isChunked ? new Base64() : new Base64(0);
-
-        long len = (binaryData.length * 4) / 3;
-        long mod = len % 4;
-        if (mod != 0) {
-            len += 4 - mod;
-        }
-        if (isChunked) {
-            len += (1 + (len / CHUNK_SIZE)) * CHUNK_SEPARATOR.length;
-        }
-
-        if (len > Integer.MAX_VALUE) {
-            throw new IllegalArgumentException(
-                    "Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE);
-        }
-        byte[] buf = new byte[(int) len];
-        b64.setInitialBuffer(buf, 0, buf.length);
-        b64.encode(binaryData, 0, binaryData.length);
-        b64.encode(binaryData, 0, -1); // Notify encoder of EOF.
-
-        // Encoder might have resized, even though it was unnecessary.
-        if (b64.buf != buf) {
-            b64.readResults(buf, 0, buf.length);
-        }
-        return buf;
-    }
-
-    /**
-     * Decodes Base64 data into octets
-     *
-     * @param base64Data Byte array containing Base64 data
-     * @return Array containing decoded data.
-     */
-    public static byte[] decodeBase64(byte[] base64Data) {
-        if (base64Data == null || base64Data.length == 0) {
-            return base64Data;
-        }
-        Base64 b64 = new Base64();
-
-        long len = (base64Data.length * 3) / 4;
-        byte[] buf = new byte[(int) len];
-        b64.setInitialBuffer(buf, 0, buf.length);
-        b64.decode(base64Data, 0, base64Data.length);
-        b64.decode(base64Data, 0, -1); // Notify decoder of EOF.
-
-        // We have no idea what the line-length was, so we
-        // cannot know how much of our array wasn't used.
-        byte[] result = new byte[b64.pos];
-        b64.readResults(result, 0, result.length);
-        return result;
-    }
-
-    /**
-     * Check if a byte value is whitespace or not.
-     * 
-     * @param byteToCheck the byte to check
-     * @return true if byte is whitespace, false otherwise
-     */
-    private static boolean isWhiteSpace(byte byteToCheck){
-        switch (byteToCheck) {
-        case ' ' :
-        case '\n' :
-        case '\r' :
-        case '\t' :
-            return true;
-        default :
-            return false;
-        }
-    }
-
-    /**
-     * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any
-     * characters outside of the base64 alphabet are to be ignored in base64 encoded data."
-     * 
-     * @param data
-     *            The base-64 encoded data to groom
-     * @return The data, less non-base64 characters (see RFC 2045).
-     */
-    static byte[] discardNonBase64(byte[] data) {
-        byte groomedData[] = new byte[data.length];
-        int bytesCopied = 0;
-
-        for (int i = 0; i < data.length; i++) {
-            if (isBase64(data[i])) {
-                groomedData[bytesCopied++] = data[i];
-            }
-        }
-
-        byte packedData[] = new byte[bytesCopied];
-
-        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
-
-        return packedData;
-    }
-
-    // Implementation of the Encoder Interface
-
-    /**
-     * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
-     * 
-     * @param pArray
-     *            a byte array containing binary data
-     * @return A byte array containing only Base64 character data
-     */
-    public byte[] encode(byte[] pArray) {
-        return encodeBase64(pArray, false);
-    }
-
-    // Implementation of integer encoding used for crypto
-    /**
-     * Decode a byte64-encoded integer according to crypto
-     * standards such as W3C's XML-Signature
-     * 
-     * @param pArray a byte array containing base64 character data
-     * @return A BigInteger
-     */
-    public static BigInteger decodeInteger(byte[] pArray) {
-        return new BigInteger(1, decodeBase64(pArray));
-    }
-
-    /**
-     * Encode to a byte64-encoded integer according to crypto
-     * standards such as W3C's XML-Signature
-     * 
-     * @param bigInt a BigInteger
-     * @return A byte array containing base64 character data
-     * @throws NullPointerException if null is passed in
-     */
-    public static byte[] encodeInteger(BigInteger bigInt) {
-        if(bigInt == null)  {
-            throw new NullPointerException("encodeInteger called with null parameter");
-        }
-
-        return encodeBase64(toIntegerBytes(bigInt), false);
-    }
-
-    /**
-     * Returns a byte-array representation of a <code>BigInteger</code>
-     * without sign bit.
-     *
-     * @param bigInt <code>BigInteger</code> to be converted
-     * @return a byte array representation of the BigInteger parameter
-     */
-     static byte[] toIntegerBytes(BigInteger bigInt) {
-        int bitlen = bigInt.bitLength();
-        // round bitlen
-        bitlen = ((bitlen + 7) >> 3) << 3;
-        byte[] bigBytes = bigInt.toByteArray();
-
-        if(((bigInt.bitLength() % 8) != 0) &&
-            (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
-            return bigBytes;
-        }
-
-        // set up params for copying everything but sign bit
-        int startSrc = 0;
-        int len = bigBytes.length;
-
-        // if bigInt is exactly byte-aligned, just skip signbit in copy
-        if((bigInt.bitLength() % 8) == 0) {
-            startSrc = 1;
-            len--;
-        }
-
-        int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
-        byte[] resizedBytes = new byte[bitlen / 8];
-
-        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
-
-        return resizedBytes;
-    }
-}
+/*
+ * Copyright 2001-2008 The Apache Software Foundation.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.oauth.signature;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+/**
+ * Provides Base64 encoding and decoding as defined by RFC 2045.
+ * 
+ * <p>
+ * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose
+ * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.
+ * </p>
+ * 
+ * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
+ * @author Apache Software Foundation
+ * @author John Kristian
+ */
+class Base64 {
+    /**
+     * Chunk size per RFC 2045 section 6.8.
+     * 
+     * <p>
+     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
+     * equal signs.
+     * </p>
+     * 
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
+     */
+    static final int CHUNK_SIZE = 76;
+
+    /**
+     * Chunk separator per RFC 2045 section 2.1.
+     * 
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+     */
+    static final byte[] CHUNK_SEPARATOR = {'\r','\n'};
+
+    /**
+     * This array is a lookup table that translates 6-bit positive integer
+     * index values into their "Base64 Alphabet" equivalents as specified
+     * in Table 1 of RFC 2045.
+     *
+     * Thanks to "commons" project in ws.apache.org for this code. 
+     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+     */
+    private static final byte[] intToBase64 = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+    };
+
+    /**
+     * Byte used to pad output.
+     */
+    private static final byte PAD = '=';
+
+    /**
+     * This array is a lookup table that translates unicode characters
+     * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
+     * into their 6-bit positive integer equivalents.  Characters that
+     * are not in the Base64 alphabet but fall within the bounds of the
+     * array are translated to -1.
+     *
+     * Thanks to "commons" project in ws.apache.org for this code.
+     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ 
+     */
+    private static final byte[] base64ToInt = {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
+            55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
+            5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+            24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+            35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+    };
+
+    /** Mask used to extract 6 bits, used when encoding */
+    private static final int MASK_6BITS = 0x3f;
+
+    /** Mask used to extract 8 bits, used in decoding base64 bytes */
+    private static final int MASK_8BITS = 0xff;
+
+    // The static final fields above are used for the original static byte[] methods on Base64.
+    // The private member fields below are used with the new streaming approach, which requires
+    // some state be preserved between calls of encode() and decode().
+
+
+    /**
+     * Line length for encoding.  Not used when decoding.  A value of zero or less implies
+     * no chunking of the base64 encoded data.
+     */
+    private final int lineLength;
+
+    /**
+     * Line separator for encoding.  Not used when decoding.  Only used if lineLength > 0.
+     */
+    private final byte[] lineSeparator;
+
+    /**
+     * Convenience variable to help us determine when our buffer is going to run out of
+     * room and needs resizing.  <code>decodeSize = 3 + lineSeparator.length;</code>
+     */
+    private final int decodeSize;
+
+    /**
+     * Convenience variable to help us determine when our buffer is going to run out of
+     * room and needs resizing.  <code>encodeSize = 4 + lineSeparator.length;</code>
+     */
+    private final int encodeSize;
+
+    /**
+     * Buffer for streaming. 
+     */
+    private byte[] buf;
+
+    /**
+     * Position where next character should be written in the buffer.
+     */
+    private int pos;
+
+    /**
+     * Position where next character should be read from the buffer.
+     */
+    private int readPos;
+
+    /**
+     * Variable tracks how many characters have been written to the current line.
+     * Only used when encoding.  We use it to make sure each encoded line never
+     * goes beyond lineLength (if lineLength > 0).
+     */
+    private int currentLinePos;
+
+    /**
+     * Writes to the buffer only occur after every 3 reads when encoding, an
+     * every 4 reads when decoding.  This variable helps track that.
+     */
+    private int modulus;
+
+    /**
+     * Boolean flag to indicate the EOF has been reached.  Once EOF has been
+     * reached, this Base64 object becomes useless, and must be thrown away.
+     */
+    private boolean eof;
+
+    /**
+     * Place holder for the 3 bytes we're dealing with for our base64 logic.
+     * Bitwise operations store and extract the base64 encoding or decoding from
+     * this variable.
+     */
+    private int x;
+
+    /**
+     * Default constructor:  lineLength is 76, and the lineSeparator is CRLF
+     * when encoding, and all forms can be decoded.
+     */
+    public Base64() {
+        this(CHUNK_SIZE, CHUNK_SEPARATOR);
+    }
+
+    /**
+     * <p>
+     * Consumer can use this constructor to choose a different lineLength
+     * when encoding (lineSeparator is still CRLF).  All forms of data can
+     * be decoded.
+     * </p><p>
+     * Note:  lineLengths that aren't multiples of 4 will still essentially
+     * end up being multiples of 4 in the encoded data.
+     * </p>
+     *
+     * @param lineLength each line of encoded data will be at most this long
+     * (rounded up to nearest multiple of 4). 
+     * If lineLength <= 0, then the output will not be divided into lines (chunks).  
+     * Ignored when decoding.
+     */
+    public Base64(int lineLength) {
+        this(lineLength, CHUNK_SEPARATOR);
+    }
+
+    /**
+     * <p>
+     * Consumer can use this constructor to choose a different lineLength
+     * and lineSeparator when encoding.  All forms of data can
+     * be decoded.
+     * </p><p>
+     * Note:  lineLengths that aren't multiples of 4 will still essentially
+     * end up being multiples of 4 in the encoded data.
+     * </p>
+     * @param lineLength    Each line of encoded data will be at most this long
+     *                      (rounded up to nearest multiple of 4).  Ignored when decoding.
+     *                      If <= 0, then output will not be divided into lines (chunks).
+     * @param lineSeparator Each line of encoded data will end with this
+     *                      sequence of bytes.
+     *                      If lineLength <= 0, then the lineSeparator is not used.
+     * @throws IllegalArgumentException The provided lineSeparator included
+     *                                  some base64 characters.  That's not going to work!
+     */
+    public Base64(int lineLength, byte[] lineSeparator) {
+        this.lineLength = lineLength;
+        this.lineSeparator = new byte[lineSeparator.length];
+        System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
+        if (lineLength > 0) {
+            this.encodeSize = 4 + lineSeparator.length;
+        } else {
+            this.encodeSize = 4;
+        }
+        this.decodeSize = encodeSize - 1;
+        if (containsBase64Byte(lineSeparator)) {
+            String sep;
+            try {
+                sep = new String(lineSeparator, "UTF-8");
+            } catch (UnsupportedEncodingException uee) {
+                sep = new String(lineSeparator);
+            }
+            throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
+        }
+    }
+
+    /**
+     * Returns true if this Base64 object has buffered data for reading.
+     *
+     * @return true if there is Base64 object still available for reading.
+     */
+    boolean hasData() { return buf != null; }
+
+    /**
+     * Returns the amount of buffered data available for reading.
+     *
+     * @return The amount of buffered data available for reading.
+     */
+    int avail() { return buf != null ? pos - readPos : 0; }
+
+    /** Doubles our buffer. */
+    private void resizeBuf() {
+        if (buf == null) {
+            buf = new byte[8192];
+            pos = 0;
+            readPos = 0;
+        } else {
+            byte[] b = new byte[buf.length * 2];
+            System.arraycopy(buf, 0, b, 0, buf.length);
+            buf = b;
+        }
+    }
+
+    /**
+     * Extracts buffered data into the provided byte[] array, starting
+     * at position bPos, up to a maximum of bAvail bytes.  Returns how
+     * many bytes were actually extracted.
+     *
+     * @param b      byte[] array to extract the buffered data into.
+     * @param bPos   position in byte[] array to start extraction at.
+     * @param bAvail amount of bytes we're allowed to extract.  We may extract
+     *               fewer (if fewer are available).
+     * @return The number of bytes successfully extracted into the provided
+     *         byte[] array.
+     */
+    int readResults(byte[] b, int bPos, int bAvail) {
+        if (buf != null) {
+            int len = Math.min(avail(), bAvail);
+            if (buf != b) {
+                System.arraycopy(buf, readPos, b, bPos, len);
+                readPos += len;
+                if (readPos >= pos) {
+                    buf = null;
+                }
+            } else {
+                // Re-using the original consumer's output array is only
+                // allowed for one round.
+                buf = null;
+            }
+            return len;
+        } else {
+            return eof ? -1 : 0;
+        }
+    }
+
+    /**
+     * Small optimization where we try to buffer directly to the consumer's
+     * output array for one round (if consumer calls this method first!) instead
+     * of starting our own buffer.
+     *
+     * @param out byte[] array to buffer directly to.
+     * @param outPos Position to start buffering into.
+     * @param outAvail Amount of bytes available for direct buffering.
+     */
+    void setInitialBuffer(byte[] out, int outPos, int outAvail) {
+        // We can re-use consumer's original output array under
+        // special circumstances, saving on some System.arraycopy().
+        if (out != null && out.length == outAvail) {
+            buf = out;
+            pos = outPos;
+            readPos = outPos;
+        }
+    }
+
+    /**
+     * <p>
+     * Encodes all of the provided data, starting at inPos, for inAvail bytes.
+     * Must be called at least twice:  once with the data to encode, and once
+     * with inAvail set to "-1" to alert encoder that EOF has been reached,
+     * so flush last remaining bytes (if not multiple of 3).
+     * </p><p>
+     * Thanks to "commons" project in ws.apache.org for the bitwise operations,
+     * and general approach.
+     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+     * </p>
+     *
+     * @param in byte[] array of binary data to base64 encode.
+     * @param inPos Position to start reading data from.
+     * @param inAvail Amount of bytes available from input for encoding.
+     */
+    void encode(byte[] in, int inPos, int inAvail) {
+        if (eof) {
+            return;
+        }
+
+        // inAvail < 0 is how we're informed of EOF in the underlying data we're
+        // encoding.
+        if (inAvail < 0) {
+            eof = true;
+            if (buf == null || buf.length - pos < encodeSize) {
+                resizeBuf();
+            }
+            switch (modulus) {
+                case 1:
+                    buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS];
+                    buf[pos++] = intToBase64[(x << 4) & MASK_6BITS];
+                    buf[pos++] = PAD;
+                    buf[pos++] = PAD;
+                    break;
+
+                case 2:
+                    buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS];
+                    buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS];
+                    buf[pos++] = intToBase64[(x << 2) & MASK_6BITS];
+                    buf[pos++] = PAD;
+                    break;
+            }
+            if (lineLength > 0) {
+                System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
+                pos += lineSeparator.length;
+            }
+        } else {
+            for (int i = 0; i < inAvail; i++) {
+                if (buf == null || buf.length - pos < encodeSize) {
+                    resizeBuf();
+                }
+                modulus = (++modulus) % 3;
+                int b = in[inPos++];
+                if (b < 0) { b += 256; }
+                x = (x << 8) + b;
+                if (0 == modulus) {
+                    buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS];
+                    buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS];
+                    buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS];
+                    buf[pos++] = intToBase64[x & MASK_6BITS];
+                    currentLinePos += 4;
+                    if (lineLength > 0 && lineLength <= currentLinePos) {
+                        System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
+                        pos += lineSeparator.length;
+                        currentLinePos = 0;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Decodes all of the provided data, starting at inPos, for inAvail bytes.
+     * Should be called at least twice:  once with the data to decode, and once
+     * with inAvail set to "-1" to alert decoder that EOF has been reached.
+     * The "-1" call is not necessary when decoding, but it doesn't hurt, either.
+     * </p><p>
+     * Ignores all non-base64 characters.  This is how chunked (e.g. 76 character)
+     * data is handled, since CR and LF are silently ignored, but has implications
+     * for other bytes, too.  This method subscribes to the garbage-in, garbage-out
+     * philosophy:  it will not check the provided data for validity.
+     * </p><p>
+     * Thanks to "commons" project in ws.apache.org for the bitwise operations,
+     * and general approach.
+     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+     * </p>
+
+     * @param in byte[] array of ascii data to base64 decode.
+     * @param inPos Position to start reading data from.
+     * @param inAvail Amount of bytes available from input for encoding.
+     */    
+    void decode(byte[] in, int inPos, int inAvail) {
+        if (eof) {
+            return;
+        }
+        if (inAvail < 0) {
+            eof = true;
+        }
+        for (int i = 0; i < inAvail; i++) {
+            if (buf == null || buf.length - pos < decodeSize) {
+                resizeBuf();
+            }
+            byte b = in[inPos++];
+            if (b == PAD) {
+                x = x << 6;
+                switch (modulus) {
+                    case 2:
+                        x = x << 6;
+                        buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                        break;
+                    case 3:
+                        buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                        buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                        break;
+                }
+                // WE'RE DONE!!!!
+                eof = true;
+                return;
+            } else {
+                if (b >= 0 && b < base64ToInt.length) {
+                    int result = base64ToInt[b];
+                    if (result >= 0) {
+                        modulus = (++modulus) % 4;
+                        x = (x << 6) + result;
+                        if (modulus == 0) {
+                            buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                            buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                            buf[pos++] = (byte) (x & MASK_8BITS);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether or not the <code>octet</code> is in the base 64 alphabet.
+     * 
+     * @param octet
+     *            The value to test
+     * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
+     */
+    public static boolean isBase64(byte octet) {
+        return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1);
+    }
+
+    /**
+     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
+     * Currently the method treats whitespace as valid.
+     * 
+     * @param arrayOctet
+     *            byte array to test
+     * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is
+     *         empty; false, otherwise
+     */
+    public static boolean isArrayByteBase64(byte[] arrayOctet) {
+        for (int i = 0; i < arrayOctet.length; i++) {
+            if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /*
+     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
+     * 
+     * @param arrayOctet
+     *            byte array to test
+     * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
+     */
+    private static boolean containsBase64Byte(byte[] arrayOctet) {
+        for (int i = 0; i < arrayOctet.length; i++) {
+            if (isBase64(arrayOctet[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm but does not chunk the output.
+     * 
+     * @param binaryData
+     *            binary data to encode
+     * @return Base64 characters
+     */
+    public static byte[] encodeBase64(byte[] binaryData) {
+        return encodeBase64(binaryData, false);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
+     * 
+     * @param binaryData
+     *            binary data to encode
+     * @return Base64 characters chunked in 76 character blocks
+     */
+    public static byte[] encodeBase64Chunked(byte[] binaryData) {
+        return encodeBase64(binaryData, true);
+    }
+
+    /**
+     * Decodes a byte[] containing containing characters in the Base64 alphabet.
+     * 
+     * @param pArray
+     *            A byte array containing Base64 character data
+     * @return a byte array containing binary data
+     */
+    public byte[] decode(byte[] pArray) {
+        return decodeBase64(pArray);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
+     * 
+     * @param binaryData
+     *            Array containing binary data to encode.
+     * @param isChunked
+     *            if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
+     * @return Base64-encoded data.
+     * @throws IllegalArgumentException
+     *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
+     */
+    public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
+        if (binaryData == null || binaryData.length == 0) {
+            return binaryData;
+        }
+        Base64 b64 = isChunked ? new Base64() : new Base64(0);
+
+        long len = (binaryData.length * 4) / 3;
+        long mod = len % 4;
+        if (mod != 0) {
+            len += 4 - mod;
+        }
+        if (isChunked) {
+            len += (1 + (len / CHUNK_SIZE)) * CHUNK_SEPARATOR.length;
+        }
+
+        if (len > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException(
+                    "Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE);
+        }
+        byte[] buf = new byte[(int) len];
+        b64.setInitialBuffer(buf, 0, buf.length);
+        b64.encode(binaryData, 0, binaryData.length);
+        b64.encode(binaryData, 0, -1); // Notify encoder of EOF.
+
+        // Encoder might have resized, even though it was unnecessary.
+        if (b64.buf != buf) {
+            b64.readResults(buf, 0, buf.length);
+        }
+        return buf;
+    }
+
+    /**
+     * Decodes Base64 data into octets
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containing decoded data.
+     */
+    public static byte[] decodeBase64(byte[] base64Data) {
+        if (base64Data == null || base64Data.length == 0) {
+            return base64Data;
+        }
+        Base64 b64 = new Base64();
+
+        long len = (base64Data.length * 3) / 4;
+        byte[] buf = new byte[(int) len];
+        b64.setInitialBuffer(buf, 0, buf.length);
+        b64.decode(base64Data, 0, base64Data.length);
+        b64.decode(base64Data, 0, -1); // Notify decoder of EOF.
+
+        // We have no idea what the line-length was, so we
+        // cannot know how much of our array wasn't used.
+        byte[] result = new byte[b64.pos];
+        b64.readResults(result, 0, result.length);
+        return result;
+    }
+
+    /**
+     * Check if a byte value is whitespace or not.
+     * 
+     * @param byteToCheck the byte to check
+     * @return true if byte is whitespace, false otherwise
+     */
+    private static boolean isWhiteSpace(byte byteToCheck){
+        switch (byteToCheck) {
+        case ' ' :
+        case '\n' :
+        case '\r' :
+        case '\t' :
+            return true;
+        default :
+            return false;
+        }
+    }
+
+    /**
+     * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any
+     * characters outside of the base64 alphabet are to be ignored in base64 encoded data."
+     * 
+     * @param data
+     *            The base-64 encoded data to groom
+     * @return The data, less non-base64 characters (see RFC 2045).
+     */
+    static byte[] discardNonBase64(byte[] data) {
+        byte groomedData[] = new byte[data.length];
+        int bytesCopied = 0;
+
+        for (int i = 0; i < data.length; i++) {
+            if (isBase64(data[i])) {
+                groomedData[bytesCopied++] = data[i];
+            }
+        }
+
+        byte packedData[] = new byte[bytesCopied];
+
+        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+        return packedData;
+    }
+
+    // Implementation of the Encoder Interface
+
+    /**
+     * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
+     * 
+     * @param pArray
+     *            a byte array containing binary data
+     * @return A byte array containing only Base64 character data
+     */
+    public byte[] encode(byte[] pArray) {
+        return encodeBase64(pArray, false);
+    }
+
+    // Implementation of integer encoding used for crypto
+    /**
+     * Decode a byte64-encoded integer according to crypto
+     * standards such as W3C's XML-Signature
+     * 
+     * @param pArray a byte array containing base64 character data
+     * @return A BigInteger
+     */
+    public static BigInteger decodeInteger(byte[] pArray) {
+        return new BigInteger(1, decodeBase64(pArray));
+    }
+
+    /**
+     * Encode to a byte64-encoded integer according to crypto
+     * standards such as W3C's XML-Signature
+     * 
+     * @param bigInt a BigInteger
+     * @return A byte array containing base64 character data
+     * @throws NullPointerException if null is passed in
+     */
+    public static byte[] encodeInteger(BigInteger bigInt) {
+        if(bigInt == null)  {
+            throw new NullPointerException("encodeInteger called with null parameter");
+        }
+
+        return encodeBase64(toIntegerBytes(bigInt), false);
+    }
+
+    /**
+     * Returns a byte-array representation of a <code>BigInteger</code>
+     * without sign bit.
+     *
+     * @param bigInt <code>BigInteger</code> to be converted
+     * @return a byte array representation of the BigInteger parameter
+     */
+     static byte[] toIntegerBytes(BigInteger bigInt) {
+        int bitlen = bigInt.bitLength();
+        // round bitlen
+        bitlen = ((bitlen + 7) >> 3) << 3;
+        byte[] bigBytes = bigInt.toByteArray();
+
+        if(((bigInt.bitLength() % 8) != 0) &&
+            (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
+            return bigBytes;
+        }
+
+        // set up params for copying everything but sign bit
+        int startSrc = 0;
+        int len = bigBytes.length;
+
+        // if bigInt is exactly byte-aligned, just skip signbit in copy
+        if((bigInt.bitLength() % 8) == 0) {
+            startSrc = 1;
+            len--;
+        }
+
+        int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
+        byte[] resizedBytes = new byte[bitlen / 8];
+
+        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
+
+        return resizedBytes;
+    }
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/HMAC_SHA1.java b/bbb-lti/src/java/net/oauth/signature/HMAC_SHA1.java
index 244ea07cc9..ed9dc5c694 100644
--- a/bbb-lti/src/java/net/oauth/signature/HMAC_SHA1.java
+++ b/bbb-lti/src/java/net/oauth/signature/HMAC_SHA1.java
@@ -1,102 +1,102 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth.signature;
-
-import java.io.UnsupportedEncodingException;
-import java.security.GeneralSecurityException;
-import java.util.Arrays;
-
-import javax.crypto.Mac;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import net.oauth.OAuth;
-import net.oauth.OAuthException;
-
-/**
- * @author John Kristian
- */
-class HMAC_SHA1 extends OAuthSignatureMethod {
-
-    @Override
-    protected String getSignature(String baseString) throws OAuthException {
-        try {
-            String signature = base64Encode(computeSignature(baseString));
-            return signature;
-        } catch (GeneralSecurityException e) {
-            throw new OAuthException(e);
-        } catch (UnsupportedEncodingException e) {
-            throw new OAuthException(e);
-        }
-    }
-
-    @Override
-    protected boolean isValid(String signature, String baseString)
-    throws OAuthException {
-        try {
-            byte[] expected = computeSignature(baseString);
-            byte[] actual = decodeBase64(signature);
-            return Arrays.equals(expected, actual);
-        } catch (GeneralSecurityException e) {
-            throw new OAuthException(e);
-        } catch (UnsupportedEncodingException e) {
-            throw new OAuthException(e);
-        }
-    }
-
-    private byte[] computeSignature(String baseString)
-            throws GeneralSecurityException, UnsupportedEncodingException {
-        SecretKey key = null;
-        synchronized (this) {
-            if (this.key == null) {
-                String keyString = OAuth.percentEncode(getConsumerSecret())
-                        + '&' + OAuth.percentEncode(getTokenSecret());
-                byte[] keyBytes = keyString.getBytes(ENCODING);
-                this.key = new SecretKeySpec(keyBytes, MAC_NAME);
-            }
-            key = this.key;
-        }
-        Mac mac = Mac.getInstance(MAC_NAME);
-        mac.init(key);
-        byte[] text = baseString.getBytes(ENCODING);
-        return mac.doFinal(text);
-    }
-
-    /** ISO-8859-1 or US-ASCII would work, too. */
-    private static final String ENCODING = OAuth.ENCODING;
-
-    private static final String MAC_NAME = "HmacSHA1";
-
-    private SecretKey key = null;
-
-    @Override
-    public void setConsumerSecret(String consumerSecret) {
-        synchronized (this) {
-            key = null;
-        }
-        super.setConsumerSecret(consumerSecret);
-    }
-
-    @Override
-    public void setTokenSecret(String tokenSecret) {
-        synchronized (this) {
-            key = null;
-        }
-        super.setTokenSecret(tokenSecret);
-    }
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth.signature;
+
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthException;
+
+/**
+ * @author John Kristian
+ */
+public class HMAC_SHA1 extends OAuthSignatureMethod {
+
+    @Override
+    public String getSignature(String baseString) throws OAuthException {
+        try {
+            String signature = base64Encode(computeSignature(baseString));
+            return signature;
+        } catch (GeneralSecurityException e) {
+            throw new OAuthException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new OAuthException(e);
+        }
+    }
+
+    @Override
+    public boolean isValid(String signature, String baseString)
+    throws OAuthException {
+        try {
+            byte[] expected = computeSignature(baseString);
+            byte[] actual = decodeBase64(signature);
+            return Arrays.equals(expected, actual);
+        } catch (GeneralSecurityException e) {
+            throw new OAuthException(e);
+        } catch (UnsupportedEncodingException e) {
+            throw new OAuthException(e);
+        }
+    }
+
+    private byte[] computeSignature(String baseString)
+            throws GeneralSecurityException, UnsupportedEncodingException {
+        SecretKey key = null;
+        synchronized (this) {
+            if (this.key == null) {
+                String keyString = OAuth.percentEncode(getConsumerSecret())
+                        + '&' + OAuth.percentEncode(getTokenSecret());
+                byte[] keyBytes = keyString.getBytes(ENCODING);
+                this.key = new SecretKeySpec(keyBytes, MAC_NAME);
+            }
+            key = this.key;
+        }
+        Mac mac = Mac.getInstance(MAC_NAME);
+        mac.init(key);
+        byte[] text = baseString.getBytes(ENCODING);
+        return mac.doFinal(text);
+    }
+
+    /** ISO-8859-1 or US-ASCII would work, too. */
+    private static final String ENCODING = OAuth.ENCODING;
+
+    private static final String MAC_NAME = "HmacSHA1";
+
+    private SecretKey key = null;
+
+    @Override
+    public void setConsumerSecret(String consumerSecret) {
+        synchronized (this) {
+            key = null;
+        }
+        super.setConsumerSecret(consumerSecret);
+    }
+
+    @Override
+    public void setTokenSecret(String tokenSecret) {
+        synchronized (this) {
+            key = null;
+        }
+        super.setTokenSecret(tokenSecret);
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/OAuthSignatureMethod.java b/bbb-lti/src/java/net/oauth/signature/OAuthSignatureMethod.java
index ec1d5595a7..5b53b07de3 100644
--- a/bbb-lti/src/java/net/oauth/signature/OAuthSignatureMethod.java
+++ b/bbb-lti/src/java/net/oauth/signature/OAuthSignatureMethod.java
@@ -31,7 +31,6 @@ import net.oauth.OAuthConsumer;
 import net.oauth.OAuthException;
 import net.oauth.OAuthMessage;
 import net.oauth.OAuthProblemException;
-import org.apache.commons.codec.binary.Base64;
 
 /**
  * A pair of algorithms for computing and verifying an OAuth digital signature.
diff --git a/bbb-lti/src/java/net/oauth/signature/PLAINTEXT.java b/bbb-lti/src/java/net/oauth/signature/PLAINTEXT.java
index 1efd94f596..59c94a900e 100644
--- a/bbb-lti/src/java/net/oauth/signature/PLAINTEXT.java
+++ b/bbb-lti/src/java/net/oauth/signature/PLAINTEXT.java
@@ -1,64 +1,64 @@
-/*
- * Copyright 2007 Netflix, Inc.
- *
- * 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 net.oauth.signature;
-
-import net.oauth.OAuth;
-import net.oauth.OAuthException;
-
-/**
- * @author John Kristian
- */
-class PLAINTEXT extends OAuthSignatureMethod {
-
-    @Override
-    public String getSignature(String baseString) {
-        return getSignature();
-    }
-
-    @Override
-    protected boolean isValid(String signature, String baseString)
-            throws OAuthException {
-        return signature.equals(getSignature());
-    }
-
-    private synchronized String getSignature() {
-        if (signature == null) {
-            signature = OAuth.percentEncode(getConsumerSecret()) + '&'
-                    + OAuth.percentEncode(getTokenSecret());
-        }
-        return signature;
-    }
-
-    private String signature = null;
-
-    @Override
-    public void setConsumerSecret(String consumerSecret) {
-        synchronized (this) {
-            signature = null;
-        }
-        super.setConsumerSecret(consumerSecret);
-    }
-
-    @Override
-    public void setTokenSecret(String tokenSecret) {
-        synchronized (this) {
-            signature = null;
-        }
-        super.setTokenSecret(tokenSecret);
-    }
-
-}
+/*
+ * Copyright 2007 Netflix, Inc.
+ *
+ * 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 net.oauth.signature;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthException;
+
+/**
+ * @author John Kristian
+ */
+class PLAINTEXT extends OAuthSignatureMethod {
+
+    @Override
+    public String getSignature(String baseString) {
+        return getSignature();
+    }
+
+    @Override
+    protected boolean isValid(String signature, String baseString)
+            throws OAuthException {
+        return signature.equals(getSignature());
+    }
+
+    private synchronized String getSignature() {
+        if (signature == null) {
+            signature = OAuth.percentEncode(getConsumerSecret()) + '&'
+                    + OAuth.percentEncode(getTokenSecret());
+        }
+        return signature;
+    }
+
+    private String signature = null;
+
+    @Override
+    public void setConsumerSecret(String consumerSecret) {
+        synchronized (this) {
+            signature = null;
+        }
+        super.setConsumerSecret(consumerSecret);
+    }
+
+    @Override
+    public void setTokenSecret(String tokenSecret) {
+        synchronized (this) {
+            signature = null;
+        }
+        super.setTokenSecret(tokenSecret);
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/RSA_SHA1.java b/bbb-lti/src/java/net/oauth/signature/RSA_SHA1.java
index f723ecb75d..a0f92a7923 100644
--- a/bbb-lti/src/java/net/oauth/signature/RSA_SHA1.java
+++ b/bbb-lti/src/java/net/oauth/signature/RSA_SHA1.java
@@ -17,6 +17,8 @@
 package net.oauth.signature;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
@@ -26,12 +28,15 @@ import java.security.Signature;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.spec.EncodedKeySpec;
+import java.security.spec.KeySpec;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 
 import net.oauth.OAuth;
 import net.oauth.OAuthAccessor;
 import net.oauth.OAuthException;
+import net.oauth.signature.pem.PEMReader;
+import net.oauth.signature.pem.PKCS1EncodedKeySpec;
 
 /**
  * Class to handle RSA-SHA1 signatures on OAuth requests. A consumer
@@ -44,10 +49,9 @@ import net.oauth.OAuthException;
  * c.setProperty(RSA_SHA1.PRIVATE_KEY, consumer_privateRSAKey);
  *
  * consumer_privateRSAKey must be an RSA signing key and
- * of type java.security.PrivateKey, String, or byte[]. In the latter two
- * cases, the key must be PKCS#8-encoded (byte[]) or PKCS#8-encoded and
- * then Base64-encoded (String).
- *
+ * of type java.security.PrivateKey, String, byte[] or InputStream. 
+ * The key must either PKCS#1 or PKCS#8 encoded.
+ * 
  * A service provider that wishes to verify signatures made by such a
  * consumer does not need a shared secret with the consumer, but it needs
  * to know the consumer's public key. You create the necessary
@@ -87,73 +91,36 @@ public class RSA_SHA1 extends OAuthSignatureMethod {
 
     private PrivateKey privateKey = null;
     private PublicKey publicKey = null;
-
+    
     @Override
     protected void initialize(String name, OAuthAccessor accessor)
     throws OAuthException {
         super.initialize(name, accessor);
 
-        Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY);
+        // Due to the support of PEM input stream, the keys must be cached. 
+        // The stream may not be markable so it can't be read again.
         try {
+            Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY);
             if (privateKeyObject != null) {
-                if (privateKeyObject instanceof PrivateKey) {
-                    privateKey = (PrivateKey)privateKeyObject;
-                } else if (privateKeyObject instanceof String) {
-                    privateKey = getPrivateKeyFromPem((String)privateKeyObject);
-                } else if (privateKeyObject instanceof byte[]) {
-                    privateKey = getPrivateKeyFromDer((byte[])privateKeyObject);
-                } else {
-                    throw new IllegalArgumentException(
-                            "Private key set through RSA_SHA1.PRIVATE_KEY must be of " +
-                            "type PrivateKey, String, or byte[], and not " +
-                            privateKeyObject.getClass().getName());
-                }
+                privateKey = loadPrivateKey(privateKeyObject);
             }
 
             Object publicKeyObject = accessor.consumer.getProperty(PUBLIC_KEY);
             if (publicKeyObject != null) {
-                if (publicKeyObject instanceof PublicKey) {
-                    publicKey = (PublicKey)publicKeyObject;
-                } else if (publicKeyObject instanceof String) {
-                    publicKey = getPublicKeyFromPem((String)publicKeyObject);
-                } else if (publicKeyObject instanceof byte[]) {
-                    publicKey = getPublicKeyFromDer((byte[])publicKeyObject);
-                } else {
-                    throw new IllegalArgumentException(
-                            "Public key set through RSA_SHA1.PRIVATE_KEY must be of " +
-                            "type PublicKey, String, or byte[], and not " +
-                            publicKeyObject.getClass().getName());
-                }
+                publicKey = loadPublicKey(publicKeyObject, false);
             } else {  // public key was null. perhaps they gave us a X509 cert.
                 Object certObject = accessor.consumer.getProperty(X509_CERTIFICATE);
                 if (certObject != null) {
-                    if (certObject instanceof X509Certificate) {
-                        publicKey = ((X509Certificate) certObject).getPublicKey();
-                    } else if (certObject instanceof String) {
-                        publicKey = getPublicKeyFromPemCert((String)certObject);
-                    } else if (certObject instanceof byte[]) {
-                        publicKey = getPublicKeyFromDerCert((byte[])certObject);
-                    } else {
-                        throw new IllegalArgumentException(
-                                "X509Certificate set through RSA_SHA1.X509_CERTIFICATE" +
-                                " must be of type X509Certificate, String, or byte[]," +
-                                " and not " + certObject.getClass().getName());
-                    }
+                    publicKey = loadPublicKey(certObject, true);
                 }
             }
         } catch (GeneralSecurityException e) {
             throw new OAuthException(e);
+        } catch (IOException e) {
+            throw new OAuthException(e);
         }
     }
 
-    private PublicKey getPublicKeyFromPemCert(String certObject)
-            throws GeneralSecurityException {
-        CertificateFactory fac = CertificateFactory.getInstance("X509");
-        ByteArrayInputStream in = new ByteArrayInputStream(certObject.getBytes());
-        X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
-        return cert.getPublicKey();
-    }
-
     private PublicKey getPublicKeyFromDerCert(byte[] certObject)
             throws GeneralSecurityException {
         CertificateFactory fac = CertificateFactory.getInstance("X509");
@@ -169,9 +136,28 @@ public class RSA_SHA1 extends OAuthSignatureMethod {
         return fac.generatePublic(pubKeySpec);
     }
 
-    private PublicKey getPublicKeyFromPem(String publicKeyObject)
-            throws GeneralSecurityException {
-        return getPublicKeyFromDer(decodeBase64(publicKeyObject));
+    private PublicKey getPublicKeyFromPem(String pem) 
+    throws GeneralSecurityException, IOException {
+
+        InputStream stream = new ByteArrayInputStream(
+                pem.getBytes("UTF-8"));
+
+        PEMReader reader = new PEMReader(stream);
+        byte[] bytes = reader.getDerBytes(); 	
+        PublicKey pubKey;
+
+        if (PEMReader.PUBLIC_X509_MARKER.equals(reader.getBeginMarker())) {
+            KeySpec keySpec = new X509EncodedKeySpec(bytes);
+            KeyFactory fac = KeyFactory.getInstance("RSA");
+            pubKey = fac.generatePublic(keySpec);
+        } else if (PEMReader.CERTIFICATE_X509_MARKER.equals(reader.getBeginMarker())) {
+            pubKey = getPublicKeyFromDerCert(bytes);
+        } else {
+            throw new IOException("Invalid PEM fileL: Unknown marker for " + 
+                    " public key or cert " + reader.getBeginMarker());
+        }
+
+        return pubKey;
     }
 
     private PrivateKey getPrivateKeyFromDer(byte[] privateKeyObject)
@@ -181,9 +167,27 @@ public class RSA_SHA1 extends OAuthSignatureMethod {
         return fac.generatePrivate(privKeySpec);
     }
 
-    private PrivateKey getPrivateKeyFromPem(String privateKeyObject)
-            throws GeneralSecurityException {
-        return getPrivateKeyFromDer(decodeBase64(privateKeyObject));
+    private PrivateKey getPrivateKeyFromPem(String pem)
+    throws GeneralSecurityException, IOException {
+
+        InputStream stream = new ByteArrayInputStream(
+                pem.getBytes("UTF-8"));
+
+        PEMReader reader = new PEMReader(stream);
+        byte[] bytes = reader.getDerBytes();
+        KeySpec keySpec;
+
+        if (PEMReader.PRIVATE_PKCS1_MARKER.equals(reader.getBeginMarker())) {
+            keySpec = (new PKCS1EncodedKeySpec(bytes)).getKeySpec();
+        } else if (PEMReader.PRIVATE_PKCS8_MARKER.equals(reader.getBeginMarker())) {
+            keySpec = new PKCS8EncodedKeySpec(bytes);
+        } else {
+            throw new IOException("Invalid PEM file: Unknown marker " +
+                    "for private key " + reader.getBeginMarker());
+        }
+
+        KeyFactory fac = KeyFactory.getInstance("RSA");
+        return fac.generatePrivate(keySpec);
     }
 
     @Override
@@ -235,4 +239,99 @@ public class RSA_SHA1 extends OAuthSignatureMethod {
         verifier.update(message);
         return verifier.verify(signature);
     }
+    
+    /**
+     * Load private key from various sources, including
+     * <ul>
+     * <li>A PrivateKey object
+     * <li>A string buffer for PEM
+     * <li>A byte array with PKCS#8 encoded key
+     * </ul>
+     * @param privateKeyObject
+     * @return The private key
+     * @throws IOException
+     * @throws GeneralSecurityException
+     */
+    private PrivateKey loadPrivateKey(Object privateKeyObject) 
+    throws IOException, GeneralSecurityException {
+
+        PrivateKey privateKey;
+
+        if (privateKeyObject instanceof PrivateKey) {
+            privateKey = (PrivateKey)privateKeyObject;
+        } else if (privateKeyObject instanceof String) {
+            try {
+                // PEM Reader's native string constructor is for filename.
+                privateKey = getPrivateKeyFromPem((String)privateKeyObject);
+            } catch (IOException e) {
+                // Check if it's PEM with markers stripped
+                privateKey = getPrivateKeyFromDer(
+                        decodeBase64((String)privateKeyObject));
+            }
+        } else if (privateKeyObject instanceof byte[]) {
+            privateKey = getPrivateKeyFromDer((byte[])privateKeyObject);
+        } else {
+            throw new IllegalArgumentException(
+                    "Private key set through RSA_SHA1.PRIVATE_KEY must be of " +
+                    "type PrivateKey, String or byte[] and not " +
+                    privateKeyObject.getClass().getName());
+        }
+
+        return privateKey;
+    }
+
+    /**
+     * Load a public key from key file or certificate. It can load from
+     * different sources depending on the type of the input,
+     * <ul>
+     * <li>A PublicKey object
+     * <li>A X509Certificate object
+     * <li>A string buffer for PEM
+     * <li>A byte array with X509 encoded key or certificate
+     * </ul>
+     * 
+     * @param publicKeyObject The object for public key or certificate
+     * @param isCert True if this object is provided as Certificate
+     * @return The public key
+     * @throws IOException
+     * @throws GeneralSecurityException
+     */
+    private PublicKey loadPublicKey(Object publicKeyObject, boolean isCert) 
+    throws IOException, GeneralSecurityException {
+
+        PublicKey publicKey;
+
+        if (publicKeyObject instanceof PublicKey) {
+            publicKey = (PublicKey)publicKeyObject;
+        } else if (publicKeyObject instanceof X509Certificate) {
+            publicKey = ((X509Certificate) publicKeyObject).getPublicKey();
+        } else if (publicKeyObject instanceof String) {
+            try {
+                publicKey = getPublicKeyFromPem((String)publicKeyObject);
+            } catch (IOException e) {
+                // Check if it's marker-stripped PEM for public key
+                if (isCert) 
+                    throw e;
+                publicKey = getPublicKeyFromDer(
+                        decodeBase64((String)publicKeyObject));
+            }
+        } else if (publicKeyObject instanceof byte[]) { 
+            if (isCert)
+                publicKey = getPublicKeyFromDerCert((byte[])publicKeyObject);
+            else
+                publicKey = getPublicKeyFromDer((byte[])publicKeyObject);
+        } else {
+            String source;
+            if (isCert)
+                source = "RSA_SHA1.X509_CERTIFICATE";
+            else
+                source = "RSA_SHA1.PUBLIC_KEY";
+            throw new IllegalArgumentException(
+                    "Public key or certificate set through " + source + " must be of " +
+                    "type PublicKey, String or byte[], and not " +
+                    publicKeyObject.getClass().getName());
+        }
+
+        return publicKey;
+    }
 }
diff --git a/bbb-lti/src/java/net/oauth/signature/pem/Asn1Object.java b/bbb-lti/src/java/net/oauth/signature/pem/Asn1Object.java
index 8a96707583..4994f1f1b3 100644
--- a/bbb-lti/src/java/net/oauth/signature/pem/Asn1Object.java
+++ b/bbb-lti/src/java/net/oauth/signature/pem/Asn1Object.java
@@ -1,150 +1,150 @@
-/****************************************************************************
- * Copyright (c) 1998-2009 AOL LLC. 
- * 
- * 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 net.oauth.signature.pem;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-/**
- * An ASN.1 TLV. The object is not parsed. It can
- * only handle integers and strings.
- * 
- * @author zhang
- *
- */
-class Asn1Object {
-
-    protected final int type;
-    protected final int length;
-    protected final byte[] value;
-    protected final int tag;
-
-    /**
-     * Construct a ASN.1 TLV. The TLV could be either a
-     * constructed or primitive entity.
-     * 
-     * <p/>The first byte in DER encoding is made of following fields,
-     * <pre>
-     *-------------------------------------------------
-     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
-     *-------------------------------------------------
-     *|  Class    | CF  |     +      Type             |
-     *-------------------------------------------------
-     * </pre>
-     * <ul>
-     * <li>Class: Universal, Application, Context or Private
-     * <li>CF: Constructed flag. If 1, the field is constructed.
-     * <li>Type: This is actually called tag in ASN.1. It
-     * indicates data type (Integer, String) or a construct
-     * (sequence, choice, set).
-     * </ul>
-     * 
-     * @param tag Tag or Identifier
-     * @param length Length of the field
-     * @param value Encoded octet string for the field.
-     */
-    public Asn1Object(int tag, int length, byte[] value) {
-        this.tag = tag;
-        this.type = tag & 0x1F;
-        this.length = length;
-        this.value = value;
-    }
-
-    public int getType() {
-        return type;
-    }
-
-    public int getLength() {
-        return length;
-    }
-
-    public byte[] getValue() {
-        return value;
-    }
-
-    public boolean isConstructed() {
-        return  (tag & DerParser.CONSTRUCTED) == DerParser.CONSTRUCTED;
-    }
-
-    /**
-     * For constructed field, return a parser for its content.
-     * 
-     * @return A parser for the construct.
-     * @throws IOException
-     */
-    public DerParser getParser() throws IOException {
-        if (!isConstructed()) 
-            throw new IOException("Invalid DER: can't parse primitive entity"); //$NON-NLS-1$
-
-        return new DerParser(value);
-    }
-
-    /**
-     * Get the value as integer
-     * 
-     * @return BigInteger
-     * @throws IOException
-     */
-    public BigInteger getInteger() throws IOException {
-        if (type != DerParser.INTEGER)
-            throw new IOException("Invalid DER: object is not integer"); //$NON-NLS-1$
-
-        return new BigInteger(value);
-    }
-
-    /**
-     * Get value as string. Most strings are treated
-     * as Latin-1.
-     * 
-     * @return Java string
-     * @throws IOException
-     */
-    public String getString() throws IOException {
-
-        String encoding;
-
-        switch (type) {
-
-        // Not all are Latin-1 but it's the closest thing
-        case DerParser.NUMERIC_STRING:
-        case DerParser.PRINTABLE_STRING:
-        case DerParser.VIDEOTEX_STRING:
-        case DerParser.IA5_STRING:
-        case DerParser.GRAPHIC_STRING:
-        case DerParser.ISO646_STRING:
-        case DerParser.GENERAL_STRING:
-            encoding = "ISO-8859-1"; //$NON-NLS-1$
-            break;
-
-        case DerParser.BMP_STRING:
-            encoding = "UTF-16BE"; //$NON-NLS-1$
-            break;
-
-        case DerParser.UTF8_STRING:
-            encoding = "UTF-8"; //$NON-NLS-1$
-            break;
-
-        case DerParser.UNIVERSAL_STRING:
-            throw new IOException("Invalid DER: can't handle UCS-4 string"); //$NON-NLS-1$
-
-        default:
-            throw new IOException("Invalid DER: object is not a string"); //$NON-NLS-1$
-        }
-
-        return new String(value, encoding);
-    }
-}
+/****************************************************************************
+ * Copyright (c) 1998-2009 AOL LLC. 
+ * 
+ * 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 net.oauth.signature.pem;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+/**
+ * An ASN.1 TLV. The object is not parsed. It can
+ * only handle integers and strings.
+ * 
+ * @author zhang
+ *
+ */
+class Asn1Object {
+
+    protected final int type;
+    protected final int length;
+    protected final byte[] value;
+    protected final int tag;
+
+    /**
+     * Construct a ASN.1 TLV. The TLV could be either a
+     * constructed or primitive entity.
+     * 
+     * <p/>The first byte in DER encoding is made of following fields,
+     * <pre>
+     *-------------------------------------------------
+     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+     *-------------------------------------------------
+     *|  Class    | CF  |     +      Type             |
+     *-------------------------------------------------
+     * </pre>
+     * <ul>
+     * <li>Class: Universal, Application, Context or Private
+     * <li>CF: Constructed flag. If 1, the field is constructed.
+     * <li>Type: This is actually called tag in ASN.1. It
+     * indicates data type (Integer, String) or a construct
+     * (sequence, choice, set).
+     * </ul>
+     * 
+     * @param tag Tag or Identifier
+     * @param length Length of the field
+     * @param value Encoded octet string for the field.
+     */
+    public Asn1Object(int tag, int length, byte[] value) {
+        this.tag = tag;
+        this.type = tag & 0x1F;
+        this.length = length;
+        this.value = value;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    public byte[] getValue() {
+        return value;
+    }
+
+    public boolean isConstructed() {
+        return  (tag & DerParser.CONSTRUCTED) == DerParser.CONSTRUCTED;
+    }
+
+    /**
+     * For constructed field, return a parser for its content.
+     * 
+     * @return A parser for the construct.
+     * @throws IOException
+     */
+    public DerParser getParser() throws IOException {
+        if (!isConstructed()) 
+            throw new IOException("Invalid DER: can't parse primitive entity"); //$NON-NLS-1$
+
+        return new DerParser(value);
+    }
+
+    /**
+     * Get the value as integer
+     * 
+     * @return BigInteger
+     * @throws IOException
+     */
+    public BigInteger getInteger() throws IOException {
+        if (type != DerParser.INTEGER)
+            throw new IOException("Invalid DER: object is not integer"); //$NON-NLS-1$
+
+        return new BigInteger(value);
+    }
+
+    /**
+     * Get value as string. Most strings are treated
+     * as Latin-1.
+     * 
+     * @return Java string
+     * @throws IOException
+     */
+    public String getString() throws IOException {
+
+        String encoding;
+
+        switch (type) {
+
+        // Not all are Latin-1 but it's the closest thing
+        case DerParser.NUMERIC_STRING:
+        case DerParser.PRINTABLE_STRING:
+        case DerParser.VIDEOTEX_STRING:
+        case DerParser.IA5_STRING:
+        case DerParser.GRAPHIC_STRING:
+        case DerParser.ISO646_STRING:
+        case DerParser.GENERAL_STRING:
+            encoding = "ISO-8859-1"; //$NON-NLS-1$
+            break;
+
+        case DerParser.BMP_STRING:
+            encoding = "UTF-16BE"; //$NON-NLS-1$
+            break;
+
+        case DerParser.UTF8_STRING:
+            encoding = "UTF-8"; //$NON-NLS-1$
+            break;
+
+        case DerParser.UNIVERSAL_STRING:
+            throw new IOException("Invalid DER: can't handle UCS-4 string"); //$NON-NLS-1$
+
+        default:
+            throw new IOException("Invalid DER: object is not a string"); //$NON-NLS-1$
+        }
+
+        return new String(value, encoding);
+    }
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/pem/DerParser.java b/bbb-lti/src/java/net/oauth/signature/pem/DerParser.java
index b82526b028..fff242ea84 100644
--- a/bbb-lti/src/java/net/oauth/signature/pem/DerParser.java
+++ b/bbb-lti/src/java/net/oauth/signature/pem/DerParser.java
@@ -1,170 +1,170 @@
-/****************************************************************************
- * Copyright (c) 1998-2009 AOL LLC. 
- * 
- * 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 net.oauth.signature.pem;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-
-/**
- * A bare-minimum ASN.1 DER decoder, just having enough functions to 
- * decode PKCS#1 private keys. Especially, it doesn't handle explicitly
- * tagged types with an outer tag.
- * 
- * <p/>This parser can only handle one layer. To parse nested constructs,
- * get a new parser for each layer using <code>Asn1Object.getParser()</code>.
- * 
- * <p/>There are many DER decoders in JRE but using them will tie this
- * program to a specific JCE/JVM.
- * 
- * @author zhang
- *
- */
-class DerParser {
-
-    // Classes
-    public final static int UNIVERSAL = 0x00;
-    public final static int APPLICATION = 0x40;
-    public final static int CONTEXT = 0x80;
-    public final static int PRIVATE = 0xC0;
-
-    // Constructed Flag
-    public final static int CONSTRUCTED = 0x20;
-
-    // Tag and data types
-    public final static int ANY = 0x00;
-    public final static int BOOLEAN = 0x01;
-    public final static int INTEGER = 0x02;
-    public final static int BIT_STRING = 0x03;
-    public final static int OCTET_STRING = 0x04;
-    public final static int NULL = 0x05;
-    public final static int OBJECT_IDENTIFIER = 0x06;
-    public final static int REAL = 0x09;
-    public final static int ENUMERATED = 0x0a;
-    public final static int RELATIVE_OID = 0x0d;
-
-    public final static int SEQUENCE = 0x10;
-    public final static int SET = 0x11;
-
-    public final static int NUMERIC_STRING = 0x12;
-    public final static int PRINTABLE_STRING = 0x13;
-    public final static int T61_STRING = 0x14;
-    public final static int VIDEOTEX_STRING = 0x15;
-    public final static int IA5_STRING = 0x16;
-    public final static int GRAPHIC_STRING = 0x19;
-    public final static int ISO646_STRING = 0x1A;
-    public final static int GENERAL_STRING = 0x1B;
-
-    public final static int UTF8_STRING = 0x0C;
-    public final static int UNIVERSAL_STRING = 0x1C;
-    public final static int BMP_STRING = 0x1E;
-
-    public final static int UTC_TIME = 0x17;
-    public final static int GENERALIZED_TIME = 0x18;
-
-    protected InputStream in;
-
-    /**
-     * Create a new DER decoder from an input stream.
-     * 
-     * @param in
-     *            The DER encoded stream
-     */
-    public DerParser(InputStream in) throws IOException {
-        this.in = in;
-    }
-
-    /**
-     * Create a new DER decoder from a byte array.
-     * 
-     * @param The
-     *            encoded bytes
-     * @throws IOException 
-     */
-    public DerParser(byte[] bytes) throws IOException {
-        this(new ByteArrayInputStream(bytes));
-    }
-
-    /**
-     * Read next object. If it's constructed, the value holds
-     * encoded content and it should be parsed by a new
-     * parser from <code>Asn1Object.getParser</code>.
-     * 
-     * @return A object
-     * @throws IOException
-     */
-    public Asn1Object read() throws IOException {
-        int tag = in.read();
-
-        if (tag == -1)
-            throw new IOException("Invalid DER: stream too short, missing tag"); //$NON-NLS-1$
-
-        int length = getLength();
-
-        byte[] value = new byte[length];
-        int n = in.read(value);
-        if (n < length)
-            throw new IOException("Invalid DER: stream too short, missing value"); //$NON-NLS-1$
-
-        Asn1Object o = new Asn1Object(tag, length, value);
-
-        return o;
-    }
-
-    /**
-     * Decode the length of the field. Can only support length
-     * encoding up to 4 octets.
-     * 
-     * <p/>In BER/DER encoding, length can be encoded in 2 forms,
-     * <ul>
-     * <li>Short form. One octet. Bit 8 has value "0" and bits 7-1
-     * give the length.
-     * <li>Long form. Two to 127 octets (only 4 is supported here). 
-     * Bit 8 of first octet has value "1" and bits 7-1 give the 
-     * number of additional length octets. Second and following 
-     * octets give the length, base 256, most significant digit first.
-     * </ul>
-     * @return The length as integer
-     * @throws IOException
-     */
-    private int getLength() throws IOException {
-
-        int i = in.read();
-        if (i == -1)
-            throw new IOException("Invalid DER: length missing"); //$NON-NLS-1$
-
-        // A single byte short length
-        if ((i & ~0x7F) == 0)
-            return i;
-
-        int num = i & 0x7F;
-
-        // We can't handle length longer than 4 bytes
-        if ( i >= 0xFF || num > 4) 
-            throw new IOException("Invalid DER: length field too big (" //$NON-NLS-1$
-                    + i + ")"); //$NON-NLS-1$
-
-        byte[] bytes = new byte[num];                   
-        int n = in.read(bytes);
-        if (n < num)
-            throw new IOException("Invalid DER: length too short"); //$NON-NLS-1$
-
-        return new BigInteger(1, bytes).intValue();
-    }
-
-}
+/****************************************************************************
+ * Copyright (c) 1998-2009 AOL LLC. 
+ * 
+ * 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 net.oauth.signature.pem;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+
+/**
+ * A bare-minimum ASN.1 DER decoder, just having enough functions to 
+ * decode PKCS#1 private keys. Especially, it doesn't handle explicitly
+ * tagged types with an outer tag.
+ * 
+ * <p/>This parser can only handle one layer. To parse nested constructs,
+ * get a new parser for each layer using <code>Asn1Object.getParser()</code>.
+ * 
+ * <p/>There are many DER decoders in JRE but using them will tie this
+ * program to a specific JCE/JVM.
+ * 
+ * @author zhang
+ *
+ */
+class DerParser {
+
+    // Classes
+    public final static int UNIVERSAL = 0x00;
+    public final static int APPLICATION = 0x40;
+    public final static int CONTEXT = 0x80;
+    public final static int PRIVATE = 0xC0;
+
+    // Constructed Flag
+    public final static int CONSTRUCTED = 0x20;
+
+    // Tag and data types
+    public final static int ANY = 0x00;
+    public final static int BOOLEAN = 0x01;
+    public final static int INTEGER = 0x02;
+    public final static int BIT_STRING = 0x03;
+    public final static int OCTET_STRING = 0x04;
+    public final static int NULL = 0x05;
+    public final static int OBJECT_IDENTIFIER = 0x06;
+    public final static int REAL = 0x09;
+    public final static int ENUMERATED = 0x0a;
+    public final static int RELATIVE_OID = 0x0d;
+
+    public final static int SEQUENCE = 0x10;
+    public final static int SET = 0x11;
+
+    public final static int NUMERIC_STRING = 0x12;
+    public final static int PRINTABLE_STRING = 0x13;
+    public final static int T61_STRING = 0x14;
+    public final static int VIDEOTEX_STRING = 0x15;
+    public final static int IA5_STRING = 0x16;
+    public final static int GRAPHIC_STRING = 0x19;
+    public final static int ISO646_STRING = 0x1A;
+    public final static int GENERAL_STRING = 0x1B;
+
+    public final static int UTF8_STRING = 0x0C;
+    public final static int UNIVERSAL_STRING = 0x1C;
+    public final static int BMP_STRING = 0x1E;
+
+    public final static int UTC_TIME = 0x17;
+    public final static int GENERALIZED_TIME = 0x18;
+
+    protected InputStream in;
+
+    /**
+     * Create a new DER decoder from an input stream.
+     * 
+     * @param in
+     *            The DER encoded stream
+     */
+    public DerParser(InputStream in) throws IOException {
+        this.in = in;
+    }
+
+    /**
+     * Create a new DER decoder from a byte array.
+     * 
+     * @param The
+     *            encoded bytes
+     * @throws IOException 
+     */
+    public DerParser(byte[] bytes) throws IOException {
+        this(new ByteArrayInputStream(bytes));
+    }
+
+    /**
+     * Read next object. If it's constructed, the value holds
+     * encoded content and it should be parsed by a new
+     * parser from <code>Asn1Object.getParser</code>.
+     * 
+     * @return A object
+     * @throws IOException
+     */
+    public Asn1Object read() throws IOException {
+        int tag = in.read();
+
+        if (tag == -1)
+            throw new IOException("Invalid DER: stream too short, missing tag"); //$NON-NLS-1$
+
+        int length = getLength();
+
+        byte[] value = new byte[length];
+        int n = in.read(value);
+        if (n < length)
+            throw new IOException("Invalid DER: stream too short, missing value"); //$NON-NLS-1$
+
+        Asn1Object o = new Asn1Object(tag, length, value);
+
+        return o;
+    }
+
+    /**
+     * Decode the length of the field. Can only support length
+     * encoding up to 4 octets.
+     * 
+     * <p/>In BER/DER encoding, length can be encoded in 2 forms,
+     * <ul>
+     * <li>Short form. One octet. Bit 8 has value "0" and bits 7-1
+     * give the length.
+     * <li>Long form. Two to 127 octets (only 4 is supported here). 
+     * Bit 8 of first octet has value "1" and bits 7-1 give the 
+     * number of additional length octets. Second and following 
+     * octets give the length, base 256, most significant digit first.
+     * </ul>
+     * @return The length as integer
+     * @throws IOException
+     */
+    private int getLength() throws IOException {
+
+        int i = in.read();
+        if (i == -1)
+            throw new IOException("Invalid DER: length missing"); //$NON-NLS-1$
+
+        // A single byte short length
+        if ((i & ~0x7F) == 0)
+            return i;
+
+        int num = i & 0x7F;
+
+        // We can't handle length longer than 4 bytes
+        if ( i >= 0xFF || num > 4) 
+            throw new IOException("Invalid DER: length field too big (" //$NON-NLS-1$
+                    + i + ")"); //$NON-NLS-1$
+
+        byte[] bytes = new byte[num];                   
+        int n = in.read(bytes);
+        if (n < num)
+            throw new IOException("Invalid DER: length too short"); //$NON-NLS-1$
+
+        return new BigInteger(1, bytes).intValue();
+    }
+
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/pem/PEMReader.java b/bbb-lti/src/java/net/oauth/signature/pem/PEMReader.java
index 54fb2b555b..71e826e15a 100644
--- a/bbb-lti/src/java/net/oauth/signature/pem/PEMReader.java
+++ b/bbb-lti/src/java/net/oauth/signature/pem/PEMReader.java
@@ -1,134 +1,134 @@
-/****************************************************************************
- * Copyright (c) 1998-2009 AOL LLC. 
- * 
- * 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.
- *
- ****************************************************************************
- * 
- * @author:     zhang
- * @version:    $Revision: 2 $
- * @created:    Apr 24, 2009
- *
- * Description: A class to decode PEM files
- * 
- ****************************************************************************/
-package net.oauth.signature.pem;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import net.oauth.signature.OAuthSignatureMethod;
-
-/**
- * This class convert PEM into byte array. The begin marker
- * is saved and it can be used to determine the type of the
- * PEM file.
- * 
- * @author zhang
- */
-public class PEMReader {
-
-    // Begin markers for all supported PEM files 
-    public static final String PRIVATE_PKCS1_MARKER =
-        "-----BEGIN RSA PRIVATE KEY-----";
-    public static final String PRIVATE_PKCS8_MARKER = 
-        "-----BEGIN PRIVATE KEY-----";
-    public static final String CERTIFICATE_X509_MARKER =
-        "-----BEGIN CERTIFICATE-----";
-    public static final String PUBLIC_X509_MARKER = 
-        "-----BEGIN PUBLIC KEY-----";
-
-    private static final String BEGIN_MARKER = "-----BEGIN ";
-
-    private InputStream stream;
-    private byte[] derBytes;
-    private String beginMarker;
-
-    public PEMReader(InputStream inStream) throws IOException {
-        stream = inStream;
-        readFile();
-    }
-
-    public PEMReader(byte[] buffer) throws IOException {
-        this(new ByteArrayInputStream(buffer));
-    }
-
-    public PEMReader(String fileName) throws IOException {
-        this(new FileInputStream(fileName));
-    }
-
-    public byte[] getDerBytes() {
-        return derBytes;
-    }
-
-    public String getBeginMarker() {
-        return beginMarker;
-    }
-
-    /**
-     * Read the PEM file and save the DER encoded octet
-     * stream and begin marker.
-     * 
-     * @throws IOException
-     */
-    protected void readFile() throws IOException {
-
-        String  line;
-        BufferedReader reader = new BufferedReader(
-                new InputStreamReader(stream));
-        try {
-            while ((line = reader.readLine()) != null)
-            {
-                if (line.indexOf(BEGIN_MARKER) != -1)
-                {
-                    beginMarker = line.trim();
-                    String endMarker = beginMarker.replace("BEGIN", "END");
-                    derBytes = readBytes(reader, endMarker);
-                    return;
-                }
-            }	        
-            throw new IOException("Invalid PEM file: no begin marker");
-        } finally {
-            reader.close();
-        }
-    }
-
-
-    /**
-     * Read the lines between BEGIN and END marker and convert
-     * the Base64 encoded content into binary byte array.
-     * 
-     * @return DER encoded octet stream
-     * @throws IOException
-     */
-    private byte[] readBytes(BufferedReader reader, String endMarker) throws IOException
-    {
-        String          line = null;
-        StringBuffer    buf = new StringBuffer();
-
-        while ((line = reader.readLine()) != null)
-        {
-            if (line.indexOf(endMarker) != -1) {
-
-                return OAuthSignatureMethod.decodeBase64(buf.toString());
-            }
-
-            buf.append(line.trim());        
-        }
-
-        throw new IOException("Invalid PEM file: No end marker");
-    }    
-}
+/****************************************************************************
+ * Copyright (c) 1998-2009 AOL LLC. 
+ * 
+ * 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.
+ *
+ ****************************************************************************
+ * 
+ * @author:     zhang
+ * @version:    $Revision: 73016 $
+ * @created:    Apr 24, 2009
+ *
+ * Description: A class to decode PEM files
+ * 
+ ****************************************************************************/
+package net.oauth.signature.pem;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import net.oauth.signature.OAuthSignatureMethod;
+
+/**
+ * This class convert PEM into byte array. The begin marker
+ * is saved and it can be used to determine the type of the
+ * PEM file.
+ * 
+ * @author zhang
+ */
+public class PEMReader {
+
+    // Begin markers for all supported PEM files 
+    public static final String PRIVATE_PKCS1_MARKER =
+        "-----BEGIN RSA PRIVATE KEY-----";
+    public static final String PRIVATE_PKCS8_MARKER = 
+        "-----BEGIN PRIVATE KEY-----";
+    public static final String CERTIFICATE_X509_MARKER =
+        "-----BEGIN CERTIFICATE-----";
+    public static final String PUBLIC_X509_MARKER = 
+        "-----BEGIN PUBLIC KEY-----";
+
+    private static final String BEGIN_MARKER = "-----BEGIN ";
+
+    private InputStream stream;
+    private byte[] derBytes;
+    private String beginMarker;
+
+    public PEMReader(InputStream inStream) throws IOException {
+        stream = inStream;
+        readFile();
+    }
+
+    public PEMReader(byte[] buffer) throws IOException {
+        this(new ByteArrayInputStream(buffer));
+    }
+
+    public PEMReader(String fileName) throws IOException {
+        this(new FileInputStream(fileName));
+    }
+
+    public byte[] getDerBytes() {
+        return derBytes;
+    }
+
+    public String getBeginMarker() {
+        return beginMarker;
+    }
+
+    /**
+     * Read the PEM file and save the DER encoded octet
+     * stream and begin marker.
+     * 
+     * @throws IOException
+     */
+    protected void readFile() throws IOException {
+
+        String  line;
+        BufferedReader reader = new BufferedReader(
+                new InputStreamReader(stream));
+        try {
+            while ((line = reader.readLine()) != null)
+            {
+                if (line.indexOf(BEGIN_MARKER) != -1)
+                {
+                    beginMarker = line.trim();
+                    String endMarker = beginMarker.replace("BEGIN", "END");
+                    derBytes = readBytes(reader, endMarker);
+                    return;
+                }
+            }	        
+            throw new IOException("Invalid PEM file: no begin marker");
+        } finally {
+            reader.close();
+        }
+    }
+
+
+    /**
+     * Read the lines between BEGIN and END marker and convert
+     * the Base64 encoded content into binary byte array.
+     * 
+     * @return DER encoded octet stream
+     * @throws IOException
+     */
+    private byte[] readBytes(BufferedReader reader, String endMarker) throws IOException
+    {
+        String          line = null;
+        StringBuffer    buf = new StringBuffer();
+
+        while ((line = reader.readLine()) != null)
+        {
+            if (line.indexOf(endMarker) != -1) {
+
+                return OAuthSignatureMethod.decodeBase64(buf.toString());
+            }
+
+            buf.append(line.trim());        
+        }
+
+        throw new IOException("Invalid PEM file: No end marker");
+    }    
+}
diff --git a/bbb-lti/src/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java b/bbb-lti/src/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java
index 29f698a2c9..5cb893b102 100644
--- a/bbb-lti/src/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java
+++ b/bbb-lti/src/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java
@@ -1,116 +1,116 @@
-/****************************************************************************
- * Copyright (c) 1998-2009 AOL LLC. 
- * 
- * 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.
- *
- ****************************************************************************
- * 
- * @author:     zhang
- * @version:    $Revision: 2 $
- * @created:    Apr 24, 2009
- *
- * Description: A KeySpec for PKCS#1 encoded RSA private key
- * 
- ****************************************************************************/
-package net.oauth.signature.pem;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.spec.RSAPrivateCrtKeySpec;
-
-/**
- * PKCS#1 encoded private key is commonly used with OpenSSL. It provides CRT parameters
- * so the private key operation can be much faster than using exponent/modulus alone, 
- * which is the case for PKCS#8 encoded key.
- * 
- * <p/>Unfortunately, JCE doesn't have an API to decode the DER. This class takes DER
- * buffer and decoded into CRT key.
- * 
- * @author zhang
- */
-public class PKCS1EncodedKeySpec {
-
-    private RSAPrivateCrtKeySpec keySpec;
-
-    /**
-     * Create a PKCS#1 keyspec from DER encoded buffer
-     * 
-     * @param keyBytes DER encoded octet stream
-     * @throws IOException
-     */
-    public PKCS1EncodedKeySpec(byte[] keyBytes) throws IOException {
-        decode(keyBytes);
-    }
-
-    /**
-     * Get the key spec that JCE understands.
-     * 
-     * @return CRT keyspec defined by JCE
-     */
-    public RSAPrivateCrtKeySpec getKeySpec() {
-        return keySpec;
-    }
-
-    /**
-     * Decode PKCS#1 encoded private key into RSAPrivateCrtKeySpec.
-     * 
-     * <p/>The ASN.1 syntax for the private key with CRT is
-     * 
-     * <pre>
-     * -- 
-     * -- Representation of RSA private key with information for the CRT algorithm.
-     * --
-     * RSAPrivateKey ::= SEQUENCE {
-     *   version           Version, 
-     *   modulus           INTEGER,  -- n
-     *   publicExponent    INTEGER,  -- e
-     *   privateExponent   INTEGER,  -- d
-     *   prime1            INTEGER,  -- p
-     *   prime2            INTEGER,  -- q
-     *   exponent1         INTEGER,  -- d mod (p-1)
-     *   exponent2         INTEGER,  -- d mod (q-1) 
-     *   coefficient       INTEGER,  -- (inverse of q) mod p
-     *   otherPrimeInfos   OtherPrimeInfos OPTIONAL 
-     * }
-     * </pre>
-     * 
-     * @param keyBytes PKCS#1 encoded key
-     * @throws IOException
-     */
-
-    private void decode(byte[] keyBytes) throws IOException  {
-
-        DerParser parser = new DerParser(keyBytes);
-
-        Asn1Object sequence = parser.read();
-        if (sequence.getType() != DerParser.SEQUENCE)
-            throw new IOException("Invalid DER: not a sequence"); //$NON-NLS-1$
-
-        // Parse inside the sequence
-        parser = sequence.getParser();
-
-        parser.read(); // Skip version
-        BigInteger modulus = parser.read().getInteger();
-        BigInteger publicExp = parser.read().getInteger();
-        BigInteger privateExp = parser.read().getInteger();
-        BigInteger prime1 = parser.read().getInteger();
-        BigInteger prime2 = parser.read().getInteger();
-        BigInteger exp1 = parser.read().getInteger();
-        BigInteger exp2 = parser.read().getInteger();
-        BigInteger crtCoef = parser.read().getInteger();
-
-        keySpec = new RSAPrivateCrtKeySpec(
-                modulus, publicExp, privateExp, prime1, prime2,
-                exp1, exp2, crtCoef);
-    }    
-}
+/****************************************************************************
+ * Copyright (c) 1998-2009 AOL LLC. 
+ * 
+ * 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.
+ *
+ ****************************************************************************
+ * 
+ * @author:     zhang
+ * @version:    $Revision: 73016 $
+ * @created:    Apr 24, 2009
+ *
+ * Description: A KeySpec for PKCS#1 encoded RSA private key
+ * 
+ ****************************************************************************/
+package net.oauth.signature.pem;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.spec.RSAPrivateCrtKeySpec;
+
+/**
+ * PKCS#1 encoded private key is commonly used with OpenSSL. It provides CRT parameters
+ * so the private key operation can be much faster than using exponent/modulus alone, 
+ * which is the case for PKCS#8 encoded key.
+ * 
+ * <p/>Unfortunately, JCE doesn't have an API to decode the DER. This class takes DER
+ * buffer and decoded into CRT key.
+ * 
+ * @author zhang
+ */
+public class PKCS1EncodedKeySpec {
+
+    private RSAPrivateCrtKeySpec keySpec;
+
+    /**
+     * Create a PKCS#1 keyspec from DER encoded buffer
+     * 
+     * @param keyBytes DER encoded octet stream
+     * @throws IOException
+     */
+    public PKCS1EncodedKeySpec(byte[] keyBytes) throws IOException {
+        decode(keyBytes);
+    }
+
+    /**
+     * Get the key spec that JCE understands.
+     * 
+     * @return CRT keyspec defined by JCE
+     */
+    public RSAPrivateCrtKeySpec getKeySpec() {
+        return keySpec;
+    }
+
+    /**
+     * Decode PKCS#1 encoded private key into RSAPrivateCrtKeySpec.
+     * 
+     * <p/>The ASN.1 syntax for the private key with CRT is
+     * 
+     * <pre>
+     * -- 
+     * -- Representation of RSA private key with information for the CRT algorithm.
+     * --
+     * RSAPrivateKey ::= SEQUENCE {
+     *   version           Version, 
+     *   modulus           INTEGER,  -- n
+     *   publicExponent    INTEGER,  -- e
+     *   privateExponent   INTEGER,  -- d
+     *   prime1            INTEGER,  -- p
+     *   prime2            INTEGER,  -- q
+     *   exponent1         INTEGER,  -- d mod (p-1)
+     *   exponent2         INTEGER,  -- d mod (q-1) 
+     *   coefficient       INTEGER,  -- (inverse of q) mod p
+     *   otherPrimeInfos   OtherPrimeInfos OPTIONAL 
+     * }
+     * </pre>
+     * 
+     * @param keyBytes PKCS#1 encoded key
+     * @throws IOException
+     */
+
+    private void decode(byte[] keyBytes) throws IOException  {
+
+        DerParser parser = new DerParser(keyBytes);
+
+        Asn1Object sequence = parser.read();
+        if (sequence.getType() != DerParser.SEQUENCE)
+            throw new IOException("Invalid DER: not a sequence"); //$NON-NLS-1$
+
+        // Parse inside the sequence
+        parser = sequence.getParser();
+
+        parser.read(); // Skip version
+        BigInteger modulus = parser.read().getInteger();
+        BigInteger publicExp = parser.read().getInteger();
+        BigInteger privateExp = parser.read().getInteger();
+        BigInteger prime1 = parser.read().getInteger();
+        BigInteger prime2 = parser.read().getInteger();
+        BigInteger exp1 = parser.read().getInteger();
+        BigInteger exp2 = parser.read().getInteger();
+        BigInteger crtCoef = parser.read().getInteger();
+
+        keySpec = new RSAPrivateCrtKeySpec(
+                modulus, publicExp, privateExp, prime1, prime2,
+                exp1, exp2, crtCoef);
+    }    
+}
-- 
GitLab