This is a repost from the IMS Developer’s forum – I forgot to put this in my own blog.
Possible issues with the OAuth C# library. I was working with some folks on C# Basic LTI and came across a seeming interoperability. Here is the discussion:
I looked at the Java Implementations and the PHP implementation and both seem to go to some length *to* double URL-encode the signature string. The PHP code interoperates with the Java code as distributed.
This results in the base signature string having %2520 where there are spaces. Which looks like a mistaken double-encode – but I think that it is an intentional double encode.
If you look at this page (broken into multiple lines to enhance readiblilty)
http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-signing-requests/
After you pick “Non-URL Safe Parameter” expand the little + signs and follow it through – it ends up with a base string that includes a %2520:
GET&http%3A%2F%2Fphotos.example.net%3A8001%2FPhotos&oauth_consumer_key
%3Ddpf43f3%252B%252Bp%252B%25232l4k3l03%26oauth_nonce
%3Dkllo~9940~pd9333jh%26oauth_signature_method%3DHMAC-SHA1
%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d
%25280%25290sl2jdk%26oauth_version%3D1.0%26photo%2520size%3D300%2525
%26title%3DBack%2520of%2520%2524100%2520Dollars%2520Bill
As I look at the sample Java code below – it is effectively double encoding very much on purpose.
So far, this suggests that it is *correct* when spaces turn into %2520 in the pre-signature string instead of %20. So I am thinking that the the sample VB.NET code from oauth.net *might* be wrong.
Sample Java code
public static String getBaseString(OAuthMessage message) throws IOException, URISyntaxException { List<Map.Entry<String, String>> parameters; String url = message.URL; int q = url.indexOf('?'); if (q < 0) { parameters = message.getParameters(); } else { // Combine the URL query string with the other parameters: parameters = new ArrayList<Map.Entry<String, String>>(); parameters.addAll(OAuth.decodeForm(message.URL.substring(q + 1))); parameters.addAll(message.getParameters()); url = url.substring(0, q); } return OAuth.percentEncode(message.method.toUpperCase()) + '&' + OAuth.percentEncode(normalizeUrl(url)) + '&' + OAuth.percentEncode(normalizeParameters(parameters)); } protected static String normalizeParameters(Collection<? extends Map.Entry> parameters) throws IOException { if (parameters == null) { return ""; } List<ComparableParameter> p = new ArrayList<ComparableParameter>(parameters.size()); for (Map.Entry parameter : parameters) { if (!"oauth_signature".equals(parameter.getKey())) { p.add(new ComparableParameter(parameter)); } } Collections.sort(p); return OAuth.formEncode(getParameters(p)); } 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()); } } }