30

I'm working on a data transfer for a gateway which requires me to send data in UrlEncoded form. However, .net's UrlEncode creates lowercase tags, and it breaks the transfer (Java creates uppercase).

Any thoughts how can I force .net to do uppercase UrlEncoding?

update1:

.net out:

dltz7UK2pzzdCWJ6QOvWXyvnIJwihPdmAioZ%2fENVuAlDQGRNCp1F

vs Java's:

dltz7UK2pzzdCWJ6QOvWXyvnIJwihPdmAioZ%2FENVuAlDQGRNCp1F

(it is a base64d 3DES string, i need to maintain it's case).

balint
  • 3,391
  • 7
  • 41
  • 50
  • What is the input string for the output that you show? – Fredrik Mörk May 27 '09 at 21:41
  • @Fredrik: URL Encoding returns the original string with invalid characters replaced by %xx, where xx is the hexadecimal value of the invalid character in ISO-8859-1. Hence, the original string is what is shown, but with %2F changed to a '/' character. – Kevin May 27 '09 at 22:07
  • +1 Thanks for asking this question. The answer helped me. This issue was causing a lot of headaches for me. – jessegavin May 26 '11 at 16:28
  • This is a great question! I know this is an old one, but @GreysonTyrus's answer is by far the easiest these days--just use WebUtility.UrlEncode. – Dr. Hilarius Jul 22 '16 at 23:27

7 Answers7

48

I think you're stuck with what C# gives you, and getting errors suggests a poorly implemented UrlDecode function on the other end.

With that said, you should just need to loop through the string and uppercase only the two characters following a % sign. That'll keep your base64 data intact while massaging the encoded characters into the right format:

public static string UpperCaseUrlEncode(string s)
{
  char[] temp = HttpUtility.UrlEncode(s).ToCharArray();
  for (int i = 0; i < temp.Length - 2; i++)
  {
    if (temp[i] == '%')
    {
      temp[i + 1] = char.ToUpper(temp[i + 1]);
      temp[i + 2] = char.ToUpper(temp[i + 2]);
    }
  }
  return new string(temp);
}
Kevin
  • 8,353
  • 3
  • 37
  • 33
  • thanks, but the question was rather about RFC compliance and implementation. – balint May 27 '09 at 22:10
  • 3
    @balint, the relevant RFCs say that you can use upper or lower case for escaped characters in the URL, which is why errors with lower case suggest a bad implementation on the decoding end. However, the above C# code will produce the same results as Java's UrlEncode, so unless I misunderstand the question it should be exactly what you need. – Kevin May 27 '09 at 22:20
  • 16
    Even though the spec says either is ok, It still makes a huge difference when the encoded string is part of an input into a Hashing routine :-( – Tim Jarvis Jul 24 '09 at 01:00
  • Thanks, it was getting me mad. I was trying to do a simple signature verification (SAML2), and I was not noticed the lowercase characters. – Gaucho Jun 06 '12 at 08:44
  • 4
    This is of relevance to me since I am using HMAC. Took me a full two hours to discover the subtle difference was causing this to fail for me. – frostymarvelous Mar 13 '14 at 18:27
  • 3
    I had a similar issue as @frostymarvelous, I needed to generate an HMAC signature of the rest of a query string, and while URIs aren't case-sensitive, HMACs are. According to RFC 3986, both uppercase and lowercase hexadecimal codes are legal in "percent-encoding" (meaning servers and browsers should accept either), but uppercase is recommended for consistency. – KeithS Jan 19 '16 at 22:45
  • JW Player api has same problem – dvdmn Jun 03 '17 at 03:40
27

I know this is very old and maybe this solution didn't exist, but this was one of the top pages I found on google when trying to solve this.

System.Net.WebUtility.UrlEncode(posResult);

This encodes to uppercase.

GreysonTyrus
  • 687
  • 1
  • 6
  • 15
15

Replace the lowercase percent encoding from HttpUtility.UrlEncode with a Regex:

static string UrlEncodeUpperCase(string value) {
    value = HttpUtility.UrlEncode(value);
    return Regex.Replace(value, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
}

var value = "SomeWords 123 #=/ äöü";

var encodedValue = HttpUtility.UrlEncode(value);
// SomeWords+123+%23%3d%2f+%c3%a4%c3%b6%c3%bc

var encodedValueUpperCase = UrlEncodeUpperCase(value);
// now the hex chars after % are uppercase:
// SomeWords+123+%23%3D%2F+%C3%A4%C3%B6%C3%BC
Satinder singh
  • 10,100
  • 16
  • 60
  • 102
Martin Meixger
  • 2,547
  • 24
  • 26
3

This is very easy

Regex.Replace( encodedString, @"%[a-f\d]{2}", m => m.Value.ToUpper() )

I.e. replace all hex letter-digit combinations to upper case

SeregaKa
  • 61
  • 3
2

if anyone else gets here in search for perl code or a PCRE (perl-compatible regular expression) to solve the issue, a (candidate for the shortest possible) perl-expression to convert url-encoding to lowercase hex is:

s/%(\X{2})/%\L$1\E/go

and the other way around (lowercase to uppercase)

s/%(\x{2})/%\U$1\E/go
Nils Goroll
  • 148
  • 4
0

This is the code that I'm using in a Twitter application for OAuth...

    Public Function OAuthUrlEncode(ByVal value As String) As String
    Dim result As New StringBuilder()
    Dim unreservedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"
    For Each symbol As Char In value
        If unreservedChars.IndexOf(symbol) <> -1 Then
            result.Append(symbol)
        Else
            result.Append("%"c + [String].Format("{0:X2}", AscW(symbol)))
        End If
    Next

    Return result.ToString()
End Function

Hope this helps!

DWRoelands
  • 4,878
  • 4
  • 29
  • 42
  • 2
    Be careful with this. It will cause problems with international characters. For example, try encoding this: õ. System.Web.HttpUtility.UrlEncode("õ") will return the correct "%c3%b5", but your method will return "%F5". And when posting this as a Twitter status update, Twitter will return this error: {"errors":[{"code":91,"message":"One or more parameters contains an invalid UTF-8 sequence"}]} – Johnny Oshika Jul 08 '13 at 22:09
  • 1
    be aware that the code (OAuthUrlEncode) in DWRoelands' answer will cause You problems with international characters! – Dov Oct 13 '09 at 07:01
0

Here's my VB.NET conversion of the public OAuth.OAuthBase version of UrlEncode (for .NET 2.0/3.5):

' MEH: Made this ReadOnly because the range of unreserved characters is specified in the OAuth spec. Inheritors ought not change it.
Protected Shared ReadOnly unreservedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"
' MEH: Added. These are the other characters HttpUtility.UrlEncode does not convert to percent encoding itself.
Protected Shared ReadOnly reservedChars As String = " !'()*" ' If this moves to .NET 4+ ' can be removed.


''' <summary>
''' This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case.
''' While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth.
''' Also the OAuth spec explicitly requires some characters to be unencoded.
''' </summary>
''' <param name="Value">The value to Url encode</param>
''' <returns>Returns a Url encoded string</returns>
''' <revisionhistory>
'''   140313 MEH Fixed to correctly cater for any characters between %80 and %ff, and the O(n^2) IndexOf call.
''' </revisionhistory>
Public Shared Function UrlEncode(ByVal Value As String) As String
    Dim result, buffer As New StringBuilder()

    For Each symbol As Char In Value
        If unreservedChars.IndexOf(symbol) <> -1 Then
            UrlEncodeAppendClear(result, buffer).Append(symbol)
        ElseIf reservedChars.IndexOf(symbol) = -1 Then
            'some symbols produce 2 or more octets in UTF8 so the system urlencoder must be used to get the correct data
            ' but this is best done over a full sequence of characters, so just store this one in a buffer.
            buffer.Append(symbol)
        Else ' These characters are not converted to percent encoding by UrlEncode.
            UrlEncodeAppendClear(result, buffer).AppendFormat(Globalization.CultureInfo.InvariantCulture, "%{0:X2}", AscW(symbol))
        End If
    Next
    UrlEncodeAppendClear(result, buffer)
    Return result.ToString()
End Function


Private Shared percentHex As New RegularExpressions.Regex("(%[0-9a-f][0-9a-f])", RegularExpressions.RegexOptions.Compiled)

''' <summary>
'''  Actually performs the UrlEncode on any buffered characters, ensuring the resulting percents are uppercased and clears the buffer.
''' </summary>
''' 
''' <param name="Result">The result of the UrlEncode is appended here.</param>
''' <param name="Buffer">The current buffer of characters to be encoded. Cleared on return.</param>
''' 
''' <returns>The <paramref name="Result">Result</paramref>, as passed in, with the UrlEncoding of the <paramref name="Buffer">buffer of characters</paramref> appended.</returns>
''' 
''' <remarks>Would like to be an extension method.</remarks>
''' 
''' <revisionhistory>
'''   140313 MEH Created.
''' </revisionhistory>
Private Shared Function UrlEncodeAppendClear(ByVal Result As StringBuilder, ByVal Buffer As StringBuilder) As StringBuilder
    If Buffer.Length > 0 Then
        Result.Append(percentHex.Replace(HttpUtility.UrlEncode(Buffer.ToString), Function(c) c.Value.ToUpperInvariant()))
        Buffer.Length = 0
    End If
    Return Result
End Function
Mark Hurd
  • 10,665
  • 10
  • 68
  • 101