Base58是用于Bitcoin中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。

相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+"和"/"符号。

设计Base58主要的目的是:

避免混淆。在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似。
不使用"+"和"/"的原因是非字母或数字的字符串作为帐号较难被接受。
没有标点符号,通常不会被从中间分行。
大部分的软件支持双击选择整个字符串。
但是这个base58的计算量比base64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。

而且长度也比base64稍微多了一点。

public class Base58 {
private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
.toCharArray();
private static final int BASE_58 = ALPHABET.length;
private static final int BASE_256 = 256;

private static final int[] INDEXES = new int[128];
static {
for (int i = 0; i < INDEXES.length; i++) {
INDEXES[i] = -1;
}
for (int i = 0; i < ALPHABET.length; i++) {
INDEXES[ALPHABET[i]] = i;
}
}

public static String encode(byte[] input) {
if (input.length == 0) {
return "";
}

input = copyOfRange(input, 0, input.length);

int zeroCount = 0;
while (zeroCount < input.length && input[zeroCount] == 0) {
++zeroCount;
}

byte[] temp = new byte[input.length * 2];
int j = temp.length;

int startAt = zeroCount;
while (startAt < input.length) {
byte mod = divmod58(input, startAt);
if (input[startAt] == 0) {
++startAt;
}

temp[--j] = (byte) ALPHABET[mod];
}

while (j < temp.length && temp[j] == ALPHABET[0]) {
++j;
}

while (--zeroCount >= 0) {
temp[--j] = (byte) ALPHABET[0];
}

byte[] output = copyOfRange(temp, j, temp.length);
return new String(output);
}

public static byte[] decode(String input) {
if (input.length() == 0) {
return new byte[0];
}

byte[] input58 = new byte[input.length()];

for (int i = 0; i < input.length(); ++i) {
char c = input.charAt(i);

int digit58 = -1;
if (c >= 0 && c < 128) {
digit58 = INDEXES[c];
}
if (digit58 < 0) {
throw new RuntimeException("Not a Base58 input: " + input);
}

input58[i] = (byte) digit58;
}


int zeroCount = 0;
while (zeroCount < input58.length && input58[zeroCount] == 0) {
++zeroCount;
}


byte[] temp = new byte[input.length()];
int j = temp.length;

int startAt = zeroCount;
while (startAt < input58.length) {
byte mod = divmod256(input58, startAt);
if (input58[startAt] == 0) {
++startAt;
}

temp[--j] = mod;
}


while (j < temp.length && temp[j] == 0) {
++j;
}

return copyOfRange(temp, j - zeroCount, temp.length);
}

private static byte divmod58(byte[] number, int startAt) {
int remainder = 0;
for (int i = startAt; i < number.length; i++) {
int digit256 = (int) number[i] & 0xFF;
int temp = remainder * BASE_256 + digit256;
number[i] = (byte) (temp / BASE_58);
remainder = temp % BASE_58;
}

return (byte) remainder;
}

private static byte divmod256(byte[] number58, int startAt) {
int remainder = 0;
for (int i = startAt; i < number58.length; i++) {
int digit58 = (int) number58[i] & 0xFF;
int temp = remainder * BASE_58 + digit58;
number58[i] = (byte) (temp / BASE_256);
remainder = temp % BASE_256;
}

return (byte) remainder;
}

private static byte[] copyOfRange(byte[] source, int from, int to) {
byte[] range = new byte[to - from];
System.arraycopy(source, from, range, 0, range.length);
return range;
}
}
我用这个来组合加密后的数据,这样不容易和其他字符混淆

Base58.encode(secret.getBytes());
secret是组装好的String

解密也是同样:

new String(Base58.decode(secret));

/*
* Copyright 2011 Google Inc.
* Copyright 2018 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.bitcoinj.core; import java.math.BigInteger;
import java.util.Arrays; /**
* Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings.
* <p>
* Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet.
* <p>
* You may want to consider working with {@link PrefixedChecksummedBytes} instead, which
* adds support for testing the prefix and suffix bytes commonly found in addresses.
* <p>
* Satoshi explains: why base-58 instead of standard base-64 encoding?
* <ul>
* <li>Don't want 0OIl characters that look the same in some fonts and
* could be used to create visually identical looking account numbers.</li>
* <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>
* <li>E-mail usually won't line-break if there's no punctuation to break at.</li>
* <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>
* </ul>
* <p>
* However, note that the encoding/decoding runs in O(n&sup2;) time, so it is not useful for large data.
* <p>
* The basic idea of the encoding is to treat the data bytes as a large number represented using
* base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
* number of leading zeros (which are otherwise lost during the mathematical operations on the
* numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
*/
public class Base58 {
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;
}
} /**
* Encodes the given bytes as a base58 string (no checksum is appended).
*
* @param input the bytes to encode
* @return the base58-encoded string
*/
public static String encode(byte[] input) {
if (input.length == 0) {
return "";
}
// Count leading zeros.
int zeros = 0;
while (zeros < input.length && input[zeros] == 0) {
++zeros;
}
// Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
input = Arrays.copyOf(input, input.length); // since we modify it in-place
char[] encoded = new char[input.length * 2]; // upper bound
int outputStart = encoded.length;
for (int inputStart = zeros; inputStart < input.length; ) {
encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
if (input[inputStart] == 0) {
++inputStart; // optimization - skip leading zeros
}
}
// Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
++outputStart;
}
while (--zeros >= 0) {
encoded[--outputStart] = ENCODED_ZERO;
}
// Return encoded string (including encoded leading zeros).
return new String(encoded, outputStart, encoded.length - outputStart);
} /**
* Encodes the given version and bytes as a base58 string. A checksum is appended.
*
* @param version the version to encode
* @param payload the bytes to encode, e.g. pubkey hash
* @return the base58-encoded string
*/
public static String encodeChecked(int version, byte[] payload) {
if (version < 0 || version > 255)
throw new IllegalArgumentException("Version not in range."); // A stringified buffer is:
// 1 byte version + data bytes + 4 bytes check code (a truncated hash)
byte[] addressBytes = new byte[1 + payload.length + 4];
addressBytes[0] = (byte) version;
System.arraycopy(payload, 0, addressBytes, 1, payload.length);
byte[] checksum = Sha256Hash.hashTwice(addressBytes, 0, payload.length + 1);
System.arraycopy(checksum, 0, addressBytes, payload.length + 1, 4);
return Base58.encode(addressBytes);
} /**
* Decodes the given base58 string into the original data bytes.
*
* @param input the base58-encoded string to decode
* @return the decoded data bytes
* @throws AddressFormatException if the given string is not a valid base58 string
*/
public static byte[] decode(String input) throws AddressFormatException {
if (input.length() == 0) {
return new byte[0];
}
// Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
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) {
throw new AddressFormatException.InvalidCharacter(c, i);
}
input58[i] = (byte) digit;
}
// Count leading zeros.
int zeros = 0;
while (zeros < input58.length && input58[zeros] == 0) {
++zeros;
}
// Convert base-58 digits to base-256 digits.
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; // optimization - skip leading zeros
}
}
// Ignore extra leading zeroes that were added during the calculation.
while (outputStart < decoded.length && decoded[outputStart] == 0) {
++outputStart;
}
// Return decoded data (including original number of leading zeros).
return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
} public static BigInteger decodeToBigInteger(String input) throws AddressFormatException {
return new BigInteger(1, decode(input));
} /**
* Decodes the given base58 string into the original data bytes, using the checksum in the
* last 4 bytes of the decoded data to verify that the rest are correct. The checksum is
* removed from the returned data.
*
* @param input the base58-encoded string to decode (which should include the checksum)
* @throws AddressFormatException if the input is not base 58 or the checksum does not validate.
*/
public static byte[] decodeChecked(String input) throws AddressFormatException {
byte[] decoded = decode(input);
if (decoded.length < 4)
throw new AddressFormatException.InvalidDataLength("Input too short: " + decoded.length);
byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
byte[] actualChecksum = Arrays.copyOfRange(Sha256Hash.hashTwice(data), 0, 4);
if (!Arrays.equals(checksum, actualChecksum))
throw new AddressFormatException.InvalidChecksum();
return data;
} /**
* Divides a number, represented as an array of bytes each containing a single digit
* in the specified base, by the given divisor. The given number is modified in-place
* to contain the quotient, and the return value is the remainder.
*
* @param number the number to divide
* @param firstDigit the index within the array of the first non-zero digit
* (this is used for optimization by skipping the leading zeros)
* @param base the base in which the number's digits are represented (up to 256)
* @param divisor the number to divide by (up to 256)
* @return the remainder of the division operation
*/
private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
// this is just long division which accounts for the base of the input digits
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;
}
}

Base58在java程序中应用的更多相关文章

  1. Linux上从Java程序中调用C函数

    原则上来说,"100%纯Java"的解决方法是最好的,但有些情况下必须使用本地方法.特别是在以下三种情况: 需要访问Java平台无法访问的系统特性和设备: 通过基准测试,发现Jav ...

  2. 在网页程序或Java程序中调用接口实现短信猫收发短信的解决方案

    方案特点: 在网页程序或Java程序中调用接口实现短信猫收发短信的解决方案,简化软件开发流程,减少各应用系统相同模块的重复开发工作,提高系统稳定性和可靠性. 基于HTTP协议的开发接口 使用特点在网页 ...

  3. 如何在java程序中调用linux命令或者shell脚本

    转自:http://blog.sina.com.cn/s/blog_6433391301019bpn.html 在java程序中如何调用linux的命令?如何调用shell脚本呢? 这里不得不提到ja ...

  4. Java程序中调用Python脚本的方法

    在程序开发中,有时候需要Java程序中调用相关Python脚本,以下内容记录了先关步骤和可能出现问题的解决办法. 1.在Eclipse中新建Maven工程: 2.pom.xml文件中添加如下依赖包之后 ...

  5. 在java程序中访问windows有用户名和密码保护的共享目录

    在java程序中访问windows有用户名和密码保护的共享目录 Posted on 2015-11-20 14:03 云自无心水自闲 阅读(3744) 评论(0)  编辑  收藏 --> Jav ...

  6. java程序中默认浮点形值常量是什么类型的?如何区分不同类型的浮点型整数值常量?

    java程序中默认浮点形值常量是什么类型的 默认的所有的浮点型数值都是double型

  7. java程序中默认整形值常量是什么类型的?如何区分不同类型的整型数值常量?

    java程序中默认整形值常量是什么类型的?如何区分不同类型的整型数值常量? 整数值默认就是int类型,只有在数值常量后面加“L”或“l”才表明该常量是long型

  8. java程序中获取kerberos登陆hadoop

    本文由作者周梁伟授权网易云社区发布. 一般我们在使用kbs登陆hadoop服务时都直接在shell中调用kinit命令来获取凭证,这种方式简单直接,只要获取一次凭证之后都可以在该会话过程中重复访问.但 ...

  9. Derby安装,创建数据库,在Java程序中使用Derby

    1,下载并安装Derby: 下载地址:http://db.apache.org/derby /derby_downloads.html,下载最新版本. 我用的是10.5.3.0. 解压缩到任意文件夹, ...

  10. Java程序中解决数据库超时与死锁

    Java程序中解决数据库超时与死锁 2011-06-07 11:09 佚名 帮考网 字号:T | T   Java程序中解决数据库超时与死锁,每个使用关系型数据库的程序都可能遇到数据死锁或不可用的情况 ...

随机推荐

  1. 调试npm下载的组件

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  2. 云原生爱好者周刊:STUNner 助你在 K8s 集群中使用 WebRTC 服务

    开源项目推荐 STUNner 目前大多数内网穿透服务都依赖于 STUN 服务或者 TURN 服务,但这些服务大多数都是公用的,即使是私有化部署,也没法迁移到 Kubernetes 的环境中,因为 Ku ...

  3. Java 面向对象高级

    文章目录 1.静态 1.1 static修饰成员变量 1.2 static修饰成员变量的应用场景 1.3 static修饰成员方法 1.4 工具类 1.5 static的注意事项 1.6 static ...

  4. IntelliJ IDEA 设置背景图片 随机快捷更换背景图片(附高清4K背景图N张)

    前言 开发过程枯燥乏味,何不为自己工作中添加点乐子呢,除了主题模板,我们可以设置IntelliJ IDEA 的背景图片,换个背景图,换个心情. IntelliJ IDEA 设置背景图片 注意: 必要的 ...

  5. 初识GO语言--基础命令

  6. ATC:多快好省,无参数token reduction方法 | ECCV'24

    来源:晓飞的算法工程笔记 公众号,转载请注明出处 论文: Agglomerative Token Clustering 论文地址:https://arxiv.org/abs/2409.11923 论文 ...

  7. 国产化UOS适配

    配置本地iso镜像为apt源 把镜像放到目录下 /home/gaohongyu/debian/uniontechos-server-20-enterprise-1030-arm64.iso 创建目录 ...

  8. 基于Java+SpringBoot+Mysql实现的古诗词平台功能设计与实现五

    一.前言介绍: 1.1 项目摘要 随着信息技术的迅猛发展和数字化时代的到来,传统文化与现代科技的融合已成为一种趋势.古诗词作为中华民族的文化瑰宝,具有深厚的历史底蕴和独特的艺术魅力.然而,在现代社会中 ...

  9. 4. jenkins的配置

    Maven安装和配置 ​ 在Jenkins集成服务器上,我们需要安装Maven来编译和打包项目. 安装Maven 先上传Maven软件到192.168.66.101 tar -xzf apache-m ...

  10. Air780E软件指南:UDP应用示例

    一.UDP概述 UDP(用户数据报协议,UserDatagramProtocol)是一种无连接的.不可靠的传输层协议,主要用于实现网络中的快速通讯.以下是UDP通讯的主要特点: 1.1 无连接通讯: ...