Reputation: 465
I want to pass a blowfish encrypted string in a URL, and want to encode it like base64, but without any special character, something like base62 would be great (0-9a-zA-Z). So what I'm trying to do is converting the blowfish encrypted string using base64_encode, and convert base64 to base62.
I know about solutions how to make base64 url-safe, but I really don't want any special character in the string. convert_base() only works with base up to 36, the math extensions can convert up to base 62.
Has anybody a nice idea how to convert a base64 string to base62 using PHP?
Upvotes: 4
Views: 7094
Reputation: 4400
Without using GPM, the following should work up to Base85 without to much trouble. Surprisingly, this appears to be slightly faster than GPM, probably because it does not goto hex first.
<?php
/**
* Devide a large number represented as a binary string in the specified base
* and return the remainder.
*
* @param string &$binary
* @param int $base
* @param int $start
*/
function divmod(&$binary, $base, $divisor, $start = 0)
{
/** @var int $size */
$size = strlen($binary);
// Do long division from most to least significant byte, keep remainder.
$remainder = 0;
for ($i = $start; $i < $size; $i++) {
// Get the byte value, 0-255 inclusive.
$digit = ord($binary[$i]);
// Shift the remainder left by base N bits, append the last byte.
$temp = ($remainder * $base) + $digit;
// Calculate the value for the current byte.
$binary[$i] = chr($temp / $divisor);
// Carry the remainder to the next byte.
$remainder = $temp % $divisor;
}
return $remainder;
}
/**
* Produce a base62 encoded string from a large binary number.
*
* @param string $binary
* return string
*/
function encodeBase62($binary)
{
$charMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($charMap);
$size = strlen($binary);
$start = $size - strlen(ltrim($binary, "\0"));
$encoded = "";
for ($i = $start; $i < $size; ) {
// Do long division from most to least significant byte, keep remainder.
$idx = divmod($binary, 256, $base, $i);
$encoded = $charMap[$idx] . $encoded;
if (ord($binary[$i]) == 0) {
$i++; // Skip leading zeros produced by the long division.
}
}
$encoded = str_pad($encoded, $start, "0", STR_PAD_LEFT);
return $encoded;
}
/**
* Produce a large binary number from a base62 encoded string.
*
* @param string $ascii
* return string
*/
function decodeBase62($ascii)
{
$charMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($charMap);
$size = strlen($ascii);
$start = $size - strlen(ltrim($ascii, "0"));
// Convert the ascii representation to binary string.
$binary = "";
for ($i = $start; $i < $size; $i++) {
$byte = strpos($charMap, $ascii[$i]);
if ($byte === false) {
throw new OutOfBoundsException("Invlaid encoding at offset '{$ascii[$i]}'");
}
$binary .= chr($byte);
}
$decode = "";
for ($i = 0; $i < $size; ) {
// Do long division from most to least significant byte, keep remainder.
$idx = divmod($binary, $base, 256, $i);
$decode = chr($idx) . $decode;
if (ord($binary[$i]) == 0) {
$i++; // Skip leading zeros produced by the long division.
}
}
$decode = ltrim($decode, "\0");
$decode = str_pad($decode, $start, "\0", STR_PAD_LEFT);
return $decode;
}
$data = pack("N*", 1234567890);
//$data = pack("C*", 0x49, 0x96, 0x02, 0xD2);
var_dump(unpack("N1", $data));
$base62 = encodeBase62($data);
var_dump( $base62 ); // "1ly7vk"
$decoded = decodeBase62($base62);
var_dump(unpack("N1", $decoded)); // 1234567890
var_dump( strcmp($decoded, $data) === 0 ); // true
Upvotes: 3
Reputation: 465
The following seems to work fine, without using base64 at all:
gmp_strval( gmp_init( bin2hex('myencryptedstring'), 16), 62);
Upvotes: 1
Reputation: 12045
function base62encode($data) {
$outstring = '';
$l = strlen($data);
for ($i = 0; $i < $l; $i += 8) {
$chunk = substr($data, $i, 8);
$outlen = ceil((strlen($chunk) * 8)/6); //8bit/char in, 6bits/char out, round up
$x = bin2hex($chunk); //gmp won't convert from binary, so go via hex
$w = gmp_strval(gmp_init(ltrim($x, '0'), 16), 62); //gmp doesn't like leading 0s
$pad = str_pad($w, $outlen, '0', STR_PAD_LEFT);
$outstring .= $pad;
}
return $outstring;
}
function base62decode($data) {
$outstring = '';
$l = strlen($data);
for ($i = 0; $i < $l; $i += 11) {
$chunk = substr($data, $i, 11);
$outlen = floor((strlen($chunk) * 6)/8); //6bit/char in, 8bits/char out, round down
$y = gmp_strval(gmp_init(ltrim($chunk, '0'), 62), 16); //gmp doesn't like leading 0s
$pad = str_pad($y, $outlen * 2, '0', STR_PAD_LEFT); //double output length as as we're going via hex (4bits/char)
$outstring .= pack('H*', $pad); //same as hex2bin
}
return $outstring;
}
$str62 = base62_encode(base64_decode($str64)) // $str64 = our string base64 encoded
All credits go to Marcus Bointon
.
Upvotes: 5