base大家族详解

*/-->

pre.src {background-color: #292b2e; color: #b2b2b2;}

pre.src {background-color: #292b2e; color: #b2b2b2;}

base大家族详解

1 简介

对于base编码的使用,参考Base What? A Practical Introduction to Base Encoding

  • base2: 2进制
  • base10: 10进制
  • base16: 计算机字节表示
  • base32: 字母加数字
  • base64: 更大的表示范围
  • base58: bitcoin为了比base64更易读采用的表示

这些base编码主要是在计算机的二进制表示,和人可读的字符表示之间转换。

具体实现可以分为2种,能利用bit位直接转换的base(例如base2, base16,base32, base64),和不能利用bit位直接转换的base(例如base10, base58)。

2 实现代码

全部采用clojure实现,主要是为了理解base编码,只求代码容易理解,性能不是很好:

  1. (ns base)
  2.  
  3. (defn indices-of
  4. [f coll]
  5. (keep-indexed #(if (f %2) %1 nil) coll))
  6.  
  7. (defn first-index-of
  8. [f coll]
  9. (first (indices-of f coll)))
  10.  
  11. (defn find-thing
  12. "查找`coll`中第一个等于value的索引"
  13. [value coll]
  14. (first-index-of #(= % value) coll))
  15.  
  16. (def divmod "返回[商 余数]" (juxt quot mod))
  17.  
  18. ;; 1. 不能利用bit位直接转换的base
  19. ;; 基本编码方法就是把输入作为一个数字,不断的对baseNN进行除法运算,
  20. ;; 每次的余数就是base-table的索引,商作为下一次的除数进行计算。直到除尽
  21.  
  22. (defn base-encode-num
  23. "base编码一个数字`n`
  24. `leading-zeros`为padding个数
  25. `base-table`为编码转换表"
  26. [n leading-zeros base-table]
  27. (let [base-count (count base-table)]
  28. (loop [n n
  29. result ""]
  30. (if (>= n base-count)
  31. (let [[d m] (divmod n base-count)]
  32. (recur d
  33. ;; 编码是从后往前进行的,每次除以baseNN,
  34. ;; 以余数作为索引
  35. (-> (nth base-table m)
  36. (str result))))
  37. (->> (if (pos? n)
  38. (-> (nth base-table n)
  39. (str result))
  40. result)
  41. ;; 注意,因为是从后往前进行生成的,padding在前面添加
  42. (concat (repeat leading-zeros (first base-table)))
  43. (apply str))))))
  44.  
  45. (defn base-enc
  46. "编码base10,base36,base58等"
  47. [base-table s]
  48. (let [s-bytes (.getBytes s)
  49. leading-zeros (->> s-bytes
  50. (take-while zero?)
  51. count)]
  52. (-> (java.math.BigInteger. s-bytes)
  53. (base-encode-num leading-zeros base-table))))
  54.  
  55. (defn invert-table
  56. "生成`table`序列的反向索引表 "
  57. [table]
  58. (into {}
  59. (map #(vector %1 %2)
  60. table
  61. (iterate inc ))))
  62.  
  63. ;; baseN的解码方法就是对输入字符串的每个字符查找索引 * (N ** 输入串反向顺序的索引),
  64. ;; 结果累加就是目标数字,再把数字还原为字符串
  65.  
  66. (defn base-dec
  67. "解码base10,base36,base58等"
  68. [base-table s]
  69. (let [padding (->> s
  70. (take-while #(= % (first base-table)))
  71. (map (constantly (byte ))))
  72. inverted-table (invert-table base-table)
  73. base-count (count base-table)]
  74. (->> (reverse s)
  75. (reduce (fn [[r m] c]
  76. [(+ r (*' m (inverted-table c)))
  77. (*' m base-count)]) [ ])
  78. first
  79. str
  80. java.math.BigInteger.
  81. .toByteArray
  82. (drop-while zero?)
  83. (concat padding)
  84. byte-array
  85. String.)))
  86.  
  87. (def base58-table "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
  88. (def enc-base58 (partial base-enc base58-table))
  89. (def dec-base58 (partial base-dec base58-table))
  90.  
  91. (def base36-table "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  92. (def enc-base36 (partial base-enc base36-table))
  93. (def dec-base36 (partial base-dec base36-table))
  94.  
  95. (def base10-table "0123456789")
  96. (def enc-base10 (partial base-enc base10-table))
  97. (def dec-base10 (partial base-dec base10-table))
  98.  
  99. (comment
  100. (def target "D9cS9N9iHjMLTdA8YSMRMp")
  101.  
  102. (def r (dec-base58 target))
  103.  
  104. (= target (enc-base58 r))
  105.  
  106. (= "64021609848" (enc-base36 "testabc"))
  107.  
  108. (enc-base10 "this is a test")
  109. ;; => "2361031878030638688519054699098996"
  110.  
  111. (dec-base10 "2361031878030638688519054699098996")
  112. ;; => "this is a test"
  113.  
  114. )
  115.  
  116. ;;; ============= 2为基的base算法,base2, base4, base16, base32, base64 ...
  117. ;;; 可以利用bit位直接变换的base
  118. ;;; 算法把输入的8bit串转换为base(2^n)的nbit串,不需要做除法,
  119. ;;; 编码过程 直接把输入字符串转换为bit序列,再根据baseN的位宽(如base64 6位,base32 5位)重新划分,
  120. ;;; 查表获取目标串,不需要除法,因此要比上面的base算法速度快
  121. ;;; 下面统一称为base2
  122.  
  123. (defn bits
  124. "转换数字为bit序列,如果不指定`width`,默认为8位"
  125. ([n] (bits n ))
  126. ([n width]
  127. (reverse (map #(bit-and (bit-shift-right n %) ) (range width)))))
  128.  
  129. (defn numb
  130. "bit序列转换为数字"
  131. [bits]
  132. (BigInteger. (apply str bits) ))
  133.  
  134. (defn string-to-bits
  135. "字符串转换为bit序列"
  136. [msg]
  137. (->> (.getBytes msg)
  138. (map #(bits %))
  139. flatten))
  140.  
  141. (defn padding-count
  142. "获取字节序列`v`的padding个数"
  143. [v]
  144. (->> (vec v)
  145. rseq
  146. (take-while zero?)
  147. count))
  148.  
  149. (defn drop-padding
  150. "去掉字节序列`v`的padding"
  151. [v]
  152. (-> (padding-count v)
  153. (drop-last v)))
  154.  
  155. (defn log2
  156. [n]
  157. (/ (Math/log n)
  158. (Math/log )))
  159.  
  160. (defn get-bit-width
  161. "获取`base-table`的位宽,即用多少字节表示一个字符"
  162. [base-table]
  163. (let [width (-> (count base-table)
  164. log2)]
  165. (when (= width (Math/floor width))
  166. (int width))))
  167.  
  168. (defn valid-base2-table?
  169. "检测是否为有效的base2表"
  170. [base-table]
  171. (-> (get-bit-width base-table)
  172. nil?
  173. not))
  174.  
  175. (defn byte-align
  176. "根据位宽bit-width,获取需要对齐的base字节数
  177. 比如一个字节是8位,转换base64,每个base64字节为6位
  178. byte-align就是确定最低需要多少个base64的6位字节才能对齐到8位字节"
  179. [bit-width]
  180. (let [max-padding
  181. fn-len-range (fn [width]
  182. (map (partial * width) (range max-padding)))
  183. hex-range (fn-len-range )
  184. bit-range (fn-len-range bit-width)
  185. align-value (->> (clojure.set/intersection (set hex-range) (set bit-range))
  186. (apply min))]
  187. (/ align-value bit-width)))
  188.  
  189. (defn hex-byte-align
  190. "获取最低需要多少个hex字节才能对齐"
  191. [bit-width]
  192. (-> (byte-align bit-width)
  193. (* bit-width)
  194. (/ )))
  195.  
  196. (comment
  197.  
  198. (byte-align )
  199. ;; => 4
  200.  
  201. (hex-byte-align )
  202. ;; => 3
  203. ;; base64需要46位字节才能对齐到38hex字节,即 4 * 6 = 3 * 8
  204.  
  205. (byte-align )
  206. ;; => 8
  207.  
  208. (hex-byte-align )
  209. ;; => 5
  210. ;; base32需要85位字节才能对齐到58hex字节, 8 * 5 = 5 * 8
  211.  
  212. (hex-byte-align )
  213. ;; => 1
  214. (hex-byte-align )
  215. ;; => 1
  216. (hex-byte-align )
  217. ;; => 1
  218. ;; 可以看到base2, base4,base16 可以对齐到1个字节,不需要padding
  219.  
  220. )
  221.  
  222. (defn gen-padding
  223. "生成`data`的padding串"
  224. [bit-width data]
  225. (let [len (count data)
  226. align (byte-align bit-width)
  227. aligned-byte (mod len align)]
  228. (if (zero? aligned-byte)
  229. ""
  230. (apply str
  231. (-> (- align aligned-byte)
  232. (repeat "="))))))
  233.  
  234. (defn base2-enc
  235. "编码base2类型的字符串`s`
  236. `base-table`必须是以2为基的base表,base(2^n),例如base16,base32,base64"
  237. [base-table s]
  238. {:pre [(valid-base2-table? base-table)]}
  239. (let [bits (string-to-bits s)
  240. base2-char #(->> (numb %)
  241. (nth base-table))
  242. bit-width (get-bit-width base-table)
  243. data (partition bit-width bit-width (repeat ) bits)]
  244. (str (->> (map base2-char data)
  245. (apply str))
  246. (gen-padding bit-width data))))
  247.  
  248. (defn base2-dec
  249. "base2解码,`s`为要解码的字符串
  250. `base-table` 解码表"
  251. [base-table s]
  252. {:pre [(valid-base2-table? base-table)]}
  253. (let [bit-width (get-bit-width base-table)
  254. inverted-table (merge (invert-table base-table)
  255. {\= } ;; 添加padding字符
  256. )]
  257. (->> (mapcat #(some-> (inverted-table %1)
  258. (bits bit-width))
  259. s)
  260. ;; mapcat自动过滤掉base2-bitsnil值,即忽略s串中不属于base-table的字符
  261. (partition (repeat ))
  262. (map numb)
  263. drop-padding
  264. (map char)
  265. (apply str))))
  266.  
  267. ;; base64,base32 需要使用base2-enc dec
  268. (def base64-table "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
  269. (def enc-base64 (partial base2-enc base64-table))
  270. (def dec-base64 (partial base2-dec base64-table))
  271.  
  272. (def base32-table "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
  273. (def enc-base32 (partial base2-enc base32-table))
  274. (def dec-base32 (partial base2-dec base32-table))
  275.  
  276. ;; base16等同于hex转换
  277. (def base16-table "0123456789ABCDEF")
  278. (def enc-base16 (partial base2-enc base16-table))
  279. (def dec-base16 (partial base2-dec base16-table))
  280.  
  281. ;; base22进制表示
  282. (def base2-table "01")
  283. (def enc-base2 (partial base2-enc base2-table))
  284. (def dec-base2 (partial base2-dec base2-table))
  285.  
  286. ;; 自动定义base的宏
  287. (defmacro defbase
  288. "根据base-table生成base编码和解码函数的定义"
  289. [base-name base-table]
  290. (let [table-name (symbol (str base-name "-table"))
  291. enc-fname (symbol (str "enc-" base-name))
  292. dec-fname (symbol (str "dec-" base-name))
  293. def-table `(def ~table-name ~base-table)]
  294. (if-let [bit-width (get-bit-width base-table)]
  295. `(do ~def-table
  296. (def ~enc-fname (partial base2-enc ~base-table))
  297. (def ~dec-fname (partial base2-dec ~base-table)))
  298. `(do ~def-table
  299. (def ~enc-fname (partial base-enc ~base-table))
  300. (def ~dec-fname (partial base-dec ~base-table))))))
  301.  
  302. ;; (defbase base64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
  303. ;; (defbase base32 "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
  304. ;; (defbase base16 "0123456789ABCDEF")
  305. ;; (defbase base2 "01")
  306. ;; (defbase base58 "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
  307. ;; (defbase base36 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  308. ;; (defbase base10 "0123456789")
  309.  
  310. (defbase base8 "01234567")
  311.  
  312. (comment
  313.  
  314. base8-table
  315. ;; => "01234567"
  316.  
  317. (enc-base8 "test 1")
  318. ;; => "3506256335020061"
  319.  
  320. (dec-base8 *1)
  321. ;; => "test 1"
  322.  
  323. (= (padding-count [ ]))
  324.  
  325. (= (padding-count [ ]))
  326.  
  327. (= (padding-count []))
  328.  
  329. (= [ ] (drop-padding [ ]))
  330.  
  331. (= "I" (base2-dec base64-table "SQ=="))
  332.  
  333. (= "AM" (base2-dec base64-table "QU0="))
  334.  
  335. (base2-enc base64-table "I")
  336. ;; => "SQ=="
  337.  
  338. (base2-enc base64-table "AM")
  339. ;; => "QU0="
  340.  
  341. (String. (base64/encode "I"))
  342. ;; => "SQ=="
  343.  
  344. (String. (base64/encode "AM"))
  345. ;; => "QU0="
  346.  
  347. (= "this is a test" (base2-dec base32-table "ORUGS4ZANFZSAYJAORSXG5A="))
  348.  
  349. (base32/encode "this is a test")
  350. ;; => "ORUGS4ZANFZSAYJAORSXG5A="
  351.  
  352. (base2-enc base32-table "this is a test")
  353. ;; => "ORUGS4ZANFZSAYJAORSXG5A="
  354.  
  355. ;; base-table不符,产生异常
  356. (= "base58_is_boring" (base2-dec base58-table "D9cS9N9iHjMLTdA8YSMRMp"))
  357.  
  358. (= (get-bit-width base64-table))
  359.  
  360. (= (get-bit-width base32-table))
  361.  
  362. (= nil (get-bit-width base58-table))
  363.  
  364. (enc-base32 "I") ;; base32的padding比较多
  365. ;; => "JE======"
  366.  
  367. (dec-base32 *1)
  368. ;; => "I"
  369.  
  370. ;; base16相当于内存hex表示
  371. (enc-base16 "this is a test")
  372. ;; => "7468697320697320612074657374"
  373.  
  374. (dec-base16 "7468697320697320612074657374")
  375. ;; => "this is a test"
  376.  
  377. (dec-base16 "7468IIOO697320697320612074657374") ;; 不合法字符直接忽略
  378. ;; => "this is a test"
  379.  
  380. (codecs/bytes->hex (codecs/str->bytes "this is a test"))
  381. ;; => "7468697320697320612074657374"
  382.  
  383. ;; base2相当于内存的二进制表示
  384. (enc-base2 "t")
  385. ;; => "01110100"
  386.  
  387. ;; \t的二进制
  388. (Integer/toString (int \t) )
  389. ;; => "1110100"
  390.  
  391. (dec-base2 "01110100")
  392. ;; => "t"
  393. ;; 注意解码时前面的0是必要的
  394.  
  395. (def s1 "this is a test")
  396. (= (enc-base64 s1) (String. (base64/encode s1)))
  397.  
  398. (= (enc-base32 s1) (base32/encode s1))
  399.  
  400. )
  401.  
  402. ;;; ============== 自动base解码
  403.  
  404. (defn valid-base-str?
  405. "检查字符串s是否符合base-table"
  406. [base-table s]
  407. (clojure.set/subset? (set s)
  408. (-> (set base-table)
  409. (conj \=))))
  410.  
  411. (defn guess-base
  412. [text]
  413. (cond
  414. ;; 这里必须按从小到大的顺序测试
  415. (valid-base-str? base16-table text) :base16
  416. (valid-base-str? base32-table text) :base32
  417. (valid-base-str? base64-table text) :base64
  418. :else :unknown))
  419.  
  420. (defn decode
  421. "自动base解码"
  422. [text & {:keys [step debug]
  423. :or {step
  424. debug false}}]
  425. (loop [text text
  426. step step]
  427. (when debug
  428. (println "step" step "decode:" text))
  429. (case (guess-base text)
  430. :base16 (do (println "step" step "--> base16")
  431. (-> (dec-base16 text)
  432. (recur (inc step))))
  433. :base32 (do (println "step" step "--> base32")
  434. (-> (dec-base32 text)
  435. (recur (inc step))))
  436. :base64 (do (println "step" step "--> base64")
  437. (-> (dec-base64 text)
  438. (recur (inc step))))
  439. :unknown (do (println "result:" text)
  440. text))))
  441.  
  442. (comment
  443. (def text "3441353234343435353735323442353634423539354134353336353333323536343735323444343535333537344234433441343634353535353535323533343734413335344134363439353534423530344135413437353634463444353334463441344534443435333235363533353534423531354134363431353334413335")
  444.  
  445. ;; 经测试,直接转换bits对于长字符串速度比较慢
  446. (decode text)
  447. ;; step 1 --> base16
  448. ;; step 2 --> base16
  449. ;; step 3 --> base32
  450. ;; step 4 --> base32
  451. ;; step 5 --> base64
  452. ;; step 6 --> base64
  453. ;; result: key:hi!venus
  454. ;; => "key:hi!venus"
  455.  
  456. )

3 结论

base编码基本是码表转换,产生人可读的字符表示,不适合加密。

作者: ntestoc

Created: 2019-04-11 四 10:28

base大家族详解的更多相关文章

  1. 基于CentOS6.5下snort+barnyard2+base的入侵检测系统的搭建(图文详解)(博主推荐)

    为什么,要写这篇论文? 是因为,目前科研的我,正值研三,致力于网络安全.大数据.机器学习研究领域! 论文方向的需要,同时不局限于真实物理环境机器实验室的攻防环境.也不局限于真实物理机器环境实验室的大数 ...

  2. 基于Windows7下snort+apache+php 7 + acid(或者base) + adodb + jpgraph的入侵检测系统的搭建(图文详解)(博主推荐)

    为什么,要写这篇论文? 是因为,目前科研的我,正值研三,致力于网络安全.大数据.机器学习.人工智能.区域链研究领域! 论文方向的需要,同时不局限于真实物理环境机器实验室的攻防环境.也不局限于真实物理机 ...

  3. Xamarin.Android通知详解

    一.发送通知的机制 在日常的app应用中经常需要使用通知,因为服务.广播后台活动如果有事件需要通知用户,则需要通过通知栏显示,而在Xamarin.Android下的通知需要获取Notification ...

  4. Android 网络框架之Retrofit2使用详解及从源码中解析原理

    就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...

  5. 基础拾遗------redis详解

    基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾遗----- ...

  6. 好用的wget命令从下载添加环境变量到各参数详解

    本文是因为(笔者使用的windows系统)使用过好几次wget后,始终存在各种细节问题,于是下定决定细致的研究一下,并记录下其中细节. 下载与安装 第一步:下载wget,网络地址:http://dow ...

  7. PHP 进程详解

    .note-content { font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", STHeit ...

  8. linux查看端口及端口详解

    今天现场查看了TCP端口的占用情况,如下图   红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说E ...

  9. 【转】Inode详解

    Inode详解 转自: Inode详解   一.inode是什么 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存 ...

随机推荐

  1. 为什么java需要序列化对象

    序列化是一种用来处理对象流的机制 所谓对象流:就是将对象的内容进行流化,可以对流化后的对象进行读写操作,也可将流化后的对象传输与网络之间 序列化是为了解决在对象流进行读写操作时所引发的问题 序列化的实 ...

  2. 使用crontab调度任务

    复杂的.分布式的.工作流式的调度可以通过azkaban来进行调度,除了执行调度任务之外,它还能进行定时调度.而对于简单的服务器任务,如执行一个小脚本,发送邮件等,可以使用crontab命令直接进行,在 ...

  3. 面向连接的传输TCP(一)

    这篇博客主要是对计算机网络自顶向上做的阅读笔记,深入地了解TCP 一.TCP连接 1.特点: a.TCP是面向连接的,因为一个进程在向另一个进程进行数据传输之前必须先要握手,即要互相发送报文,以确认信 ...

  4. JVM之---Java源码编译机制

    Sun JDK中采用javac将Java源码编译为class文件,这个过程包含三个步骤:     1.分析和输入到符号表(Parse and Enter)    Parse过程所做的工作有词法和语法分 ...

  5. 项目Debug版本与Release版本的区别

    Debug版本:通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序. Release版本:称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很 ...

  6. python数据类型之字典

    字典定义 字典是一种 key-value 的数据类型,这点很重要,是区别使用列表和字典的依据. 语法格式: info = { 'stu1101': "Aaron", 'stu110 ...

  7. FineReport中如何实现自动滚屏效果

    对于一些特殊的模板,可能为了展示的更加丰富.全面会在一个页面放置很多图表.表格等内容.由于内容过多,超出了浏览器窗口的大小导致内容展示不全的情况.这样我们就需要用到JS滚屏效果来解决,这里主要介绍在F ...

  8. Surface电池阈值

    Surface电池阈值 笔记本电脑一般都会提供一个电池保养的软件,其主要最用是让电池在插电情况下保持在50%-80%之间,以延长电池寿命,减少电池损耗.而微软自家的Surface却一直没有这个设置. ...

  9. Ubunt 安装mysql

    apt-get install mysql-client-core-5.6apt-get install mysql-client-5.6apt-get install mysql-server-5. ...

  10. 0 Linux下Java使用ProcessBuilder执行命令与直接Bash执行命令之间的不同(环境变量方面)

    0 问题发生 xiaojietest.java package tasks; import java.io.BufferedReader; import java.io.BufferedWriter; ...