4

I have a piece of code in C# that encodes string and returns URL safe string (that is decoded later on)

string stringToEncrypt = "Winter is coming";

byte[] bytes = new byte[stringToEncrypt.Length * sizeof(char)];
System.Buffer.BlockCopy(stringToEncrypt.ToCharArray(), 0, bytes, 0, bytes.Length);

System.Web.HttpServerUtility.UrlTokenEncode(bytes).Dump();

Dump is from LinqPad. I use it to test bits and pieces of C# quickly

This Returns VwBpAG4AdABlAHIAIABpAHMAIABjAG8AbQBpAG4AZwA1 when Executed.

I am trying to do the same thing from a clojure service now. Using the encode library and going by this answer When I have

(String. (b64/encode (.getBytes email)) "UTF-8")

I get V2ludGVyIGlzIGNvbWluZw==, which is

  • not url encoded
  • doesn't match the C# version.

Tried looking at the MSDN documentation for UrlTokenEncode() but it doesn't have much detail on the implementation of it to see what's going on under the hood.

Can I generate the equivalent string in clojure?

Community
  • 1
  • 1
LocustHorde
  • 6,361
  • 16
  • 65
  • 94
  • You can find the source code for UrlTokenEncode [here](http://www.dotnetframework.org/default.aspx/Dotnetfx_Win7_3@5@1/Dotnetfx_Win7_3@5@1/3@5@1/DEVDIV/depot/DevDiv/releases/whidbey/NetFXspW7/ndp/fx/src/xsp/System/Web/httpserverutility@cs/1/httpserverutility@cs) – Robert Harvey Dec 01 '14 at 18:49
  • Ack. Have no idea why the link is not working. Do a google search for urltokenencode source, and click on the dotnetframework.org link. – Robert Harvey Dec 01 '14 at 18:55
  • @RobertHarvey ah cool, of course, I was looking at the wrong place! Thanks. – LocustHorde Dec 02 '14 at 09:57
  • @Rolled back because the question is specifically about duplicating a particular c# functionality in clojure. **NOT B64 ENCODING IN CLOJURE** Please read the question before you edit. – LocustHorde Dec 02 '14 at 10:02

2 Answers2

3

Thanks to Robert for pointing out the source for UrlTokenEncode. It does the following:

  1. Base64-encode the input bytes
  2. Replace any trailing padding = with the number of padding characters (1 or 2)
  3. Replace + with - and / with _

The other essential detail is that the C# example encodes the UTF-16 representation of the string (2 bytes per character). To illustrate, here is an example from the Go playground: http://play.golang.org/p/UlKMa7_OwV

This code produces the expected output for your test input:

(ns blah.core
  (:require [clojure.data.codec.base64 :as b64])
  (:require [clojure.string :as string])
  (:gen-class))

(defn encode [original]
  (let [bytes_in (.getBytes original "UTF-16LE")
        bytes_enc (b64/encode bytes_in)
        bytes_len (alength bytes_enc)
        pad_count (b64/pad-length bytes_enc 0 bytes_len)
        enc_string (String. bytes_enc 0 (- bytes_len pad_count) "UTF-8")
        enc_string (string/replace enc_string \+ \-)
        enc_string (string/replace enc_string \/ \_)]
      (str enc_string pad_count)))

(defn -main
  [& args]
  (let [message "Winter is coming"]
    (println message)
    (println (encode message))))

The decode function is left as an exercise for the reader.

lnmx
  • 10,846
  • 3
  • 40
  • 36
2

If you are using Java 8, the new Base64 class, along with a little modular arithmetic for the pad count does it:

(defn url-token-encode [bytes]
  (let [byte-count (count bytes)]
    (if (pos? byte-count)
      (str (.. (java.util.Base64/getUrlEncoder) (withoutPadding) (encodeToString bytes))
           (mod (- byte-count) 3))
      "")))

(defn encode [s]
  (url-token-encode (.getBytes s "UTF-16LE")))

(encode "Winter is coming")
;=>"VwBpAG4AdABlAHIAIABpAHMAIABjAG8AbQBpAG4AZwA1"
Mike Fikes
  • 3,507
  • 14
  • 28