LocustHorde
LocustHorde

Reputation: 6399

c# equivalent of base64 + UrlTokenEncode in clojure

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

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?

Upvotes: 4

Views: 970

Answers (2)

Mike Fikes
Mike Fikes

Reputation: 3527

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"

Upvotes: 2

lnmx
lnmx

Reputation: 11154

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.

Upvotes: 3

Related Questions