【Java小工匠聊密码学】--base58编码
1、Base58编码概述
1.1 什么是Base58编码?
Base58是比特币的一种特殊编码方式,主要用于产生比特币钱包地址。相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+"和"/"符号。
1.2 Base58的设计目的?
Base58编码,为比特币比特币钱包地址设计的。
(1)避免混淆,在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似。
(2)Base64编码中包含"+"和"/",非字母或数字的字符串作为帐号较难被接受。
(3) 在邮件系统中,使用字符和数字的组合,不容易换行。
(4)双击可以选中整个字符串。
1.3 Base58编码表
1.4 Base58编码原理
Base58编码实际上是58进制,和2进制、8进制、16进制是一样的道理,只是用58作为进制的单位了,正好和58个不容易混淆的字符对应。
1.5 Base58Check
二进制数据的传输过程中,为了防止数据传输的错误,保护数据安全,通常会加一个校验码。通过校验码的配合可以发现数据是否被破坏或者是否在发送时输入错误了。Base58Check就是Base58加上校验码,或者可以说是Base58的一种编码形式,在比特币系统中生成钱包地址的时候就使用到了这种编码形式。
2、Base58编码实现
2.1 比特币Base58
比特币中的Base58编码,不是纯正的Base58,包含一部分比特币地址规范,例如前导0的处理。
package lzf.cipher.jdk;
import java.util.Arrays;
/**
* 复制比特币源码,去掉与Base58编码无关功能
*
* @author java小工匠
*/
public class Base58 {
// Bsae58 编码表
public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
private static final char ENCODED_ZERO = ALPHABET[0];
private static final int[] INDEXES = new int[128];
static {
Arrays.fill(INDEXES, -1);
for (int i = 0; i < ALPHABET.length; i++) {
INDEXES[ALPHABET[i]] = i;
}
}
// Base58 编码
public static String encode(byte[] input) {
if (input.length == 0) {
return "";
}
// 统计前导0
int zeros = 0;
while (zeros < input.length && input[zeros] == 0) {
++zeros;
}
// 复制一份进行修改
input = Arrays.copyOf(input, input.length);
// 最大编码数据长度
char[] encoded = new char[input.length * 2];
int outputStart = encoded.length;
// Base58编码正式开始
for (int inputStart = zeros; inputStart < input.length;) {
encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
if (input[inputStart] == 0) {
++inputStart;
}
}
// 输出结果中有0,去掉输出结果的前端0
while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
++outputStart;
}
// 处理前导0
while (--zeros >= 0) {
encoded[--outputStart] = ENCODED_ZERO;
}
// 返回Base58
return new String(encoded, outputStart, encoded.length - outputStart);
}
public static byte[] decode(String input) {
if (input.length() == 0) {
return new byte[0];
}
// 将BASE58编码的ASCII字符转换为BASE58字节序列
byte[] input58 = new byte[input.length()];
for (int i = 0; i < input.length(); ++i) {
char c = input.charAt(i);
int digit = c < 128 ? INDEXES[c] : -1;
if (digit < 0) {
String msg = "Invalid characters,c=" + c;
throw new RuntimeException(msg);
}
input58[i] = (byte) digit;
}
// 统计前导0
int zeros = 0;
while (zeros < input58.length && input58[zeros] == 0) {
++zeros;
}
// Base58 编码转 字节序(256进制)编码
byte[] decoded = new byte[input.length()];
int outputStart = decoded.length;
for (int inputStart = zeros; inputStart < input58.length;) {
decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
if (input58[inputStart] == 0) {
++inputStart;
}
}
// 忽略在计算过程中添加的额外超前零点。
while (outputStart < decoded.length && decoded[outputStart] == 0) {
++outputStart;
}
// 返回原始的字节数据
return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
}
// 进制转换代码
private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
int remainder = 0;
for (int i = firstDigit; i < number.length; i++) {
int digit = (int) number[i] & 0xFF;
int temp = remainder * base + digit;
number[i] = (byte) (temp / divisor);
remainder = temp % divisor;
}
return (byte) remainder;
}
public static void main(String[] args) {
byte[] data = new byte[] {0,0,58,0,0,59};
String dataStr = Base58.encode(data);
System.out.println("Base58编码后:"+dataStr);
byte[] ndata = Base58.decode(dataStr);
for (byte b : ndata) {
System.out.println(b);
}
}
}
如果读完觉得有收获的话,欢迎点赞、关注、加公众号【小工匠技术圈】
个人公众号,欢迎关注,查阅更多精彩历史!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
Java值类型的当前状态
甲骨文一直在努力向Java中加入值类型,这项工作包含在Valhalla项目中,Valhalla是“一个探索和孵化候选高级Java虚拟机和语言特性的地方”。InfoQ之前已经报道了这个项目以及向Java中引入值类型的工作进展。 值类型旨在成为未来Java版本中的第三种数据类型,当前已有的两种类型分别是原始类型和对象引用。经常有人说,Java值类型应该“写起来像类,用起来像int”。这意味着它们应该是一个复合数据类型(代码与类相似),只是少了标识,并且如果有可能的话不提供对象头部(像int一样)。 以Java平台目前的情况来看,运行环境不会提供这种对内存布局的底层控制形式——它可能类似于C语言当中的struct,但JVM并不支持。所以,在当前版本中,所有组合数据类型都必须通过引用来访问。 如果要将Java平台扩展为包含值类型,那么自然会产生这样的问题:值类型是否可用作类型参数(type parameter)值。如果不是,那么这似乎大大限制了它们的用处。因此,值类型的设计一直包含这样的假设,即在增强的泛型中,值类型可以作为类型参数的值。 这与Java类型系统缺少顶级类型(top type)有...
-
下一篇
python字符串反转 高阶函数 @property与sorted(八)
(1)字符串反转 1倒序输出 s = 'abcde' print(s[::-1]) #输出: 'edcba' 2 列表reverse()操作 s = 'abcde' lt = list(s) lt.reverse() print(''.join(lt)) #输出: 'edcba' 3 二分法交换位置 s = 'abcde' lt = list(s) for i in range(len(l) // 2): lt[i], lt[-(i+1)] = lt[-(i+1)], lt[i] print(''.join(lt)) #输出: 'edcba' 4 列表生成式 s = 'abcde' print(''.join([s[i-1] for i in range(len(s), 0, -1)])) #输出: 'edcba' 5 栈的思想 s = 'abcde' lt = list(s) res = '' while lt: res += lt.pop() print(res) #输出: 'edcba' 6 递归的思路 def res_str(s): if len(s) == 1: return...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- MySQL数据库在高并发下的优化方案
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker快速安装Oracle11G,搭建oracle11g学习环境