I came across this blog post which describes how they encrypt their ID’s. For educational and entertainment purposes, I decided to try this out. I can’t quite get it, though.
As described in this blog post, I’m base 36 encoding the 3DES encrypted 64 bit padded value and keep getting the resulting string to be 25 characters long. How is it possible to get 13 characters, like the site claims, without using some stream cipher — the blog claims to use a 3DES block cipher.
Here’s the code I’m using:
require 'openssl'
SECRET_KEY = "secret"
ENCRYPTION_ALGO = "DES-EDE3-CBC"
def base36encode(s)
s.unpack('H*')[0].to_i(16).to_s 36
end
def base36decode(s36)
[s36.to_i(36).to_s(16)].pack 'H*'
end
def num_to_bits(n, bit_count=64)
#Array.new(bit_count) { |i| (n)[i] }.reverse!
sprintf('%064b', n).split("")
end
def bits_to_string(bits)
[bits.join("")].pack("B*")
end
def num_to_binstring(n, bit_count=64)
bits_to_string(num_to_bits(n, bit_count))
end
def binstring_to_num(str)
#elements = str.unpack("N*")
#(elements[0] << 32) | elements[1]
#
ans = 0
str.each_byte do |i|
ans = ans * 256 + i
end
ans
end
def encrypt(message, password)
cipher = OpenSSL::Cipher::Cipher.new(ENCRYPTION_ALGO)
cipher.encrypt
cipher.pkcs5_keyivgen(password)
ciphertext = cipher.update(message)
ciphertext << cipher.final
end
def decrypt(message, password)
cipher = OpenSSL::Cipher::Cipher.new(ENCRYPTION_ALGO)
cipher.decrypt
cipher.pkcs5_keyivgen(password)
decryptedtext = cipher.update(message)
decryptedtext << cipher.final
end
id = 12345678
puts "Encrypting: \"#{id}\""
num_string = num_to_binstring(id)
encrypted = encrypt(num_string, SECRET_KEY)
encoded = base36encode(encrypted).upcase
puts "Encrypted and encoded to: \"#{encoded}\" that's size is: #{encoded.length}\n"
decoded = base36decode(encoded.downcase)
decrypted = decrypt(decoded, SECRET_KEY)
string_num = binstring_to_num(decrypted)
puts "Decoded and decrypted to: \"#{string_num}\""
# ---- OUTPUT ---
# Encrypting: "12345678"
# Encrypted and encoded to: "49OMDVRHHMM24DVMODQU4X7JY" that's size is: 25
# Decoded and decrypted to: "12345678"
What size is the output of the encryption before you base36 encode? My guess is that your code is encrypting in CBC mode (given that your code mentions pkcs5) and you’re getting the IV and ciphertext out, a total of 16 bytes. I infer from the blog post that the author is using ECB mode, what you might think of as “raw” 3DES, which is usually wrong for most situations but seems plausible here.