I want to convert this below piece of code into java but I am unable to do, Basically I have to implement 'crypto' module in Java. Thanks in advance!
let encKey = "0Z8ZUcy1Qh8lnt199MTwTPEe2g1E2tE3";
encKey = crypto.createHash('sha256').update(encKey).digest('bin').slice(0, 32);
let char = String.fromCharCode(0x0);
let iv = char + char + char + char + char + char + char + char + char + char + char + char + char + char + char + char;
let decryptor = crypto.createDecipheriv("aes-256-cbc", encKey, iv);
let dec = decryptor.update(someAuthString, 'base64', 'utf8') + decryptor.final('utf8');
dec = removePKCS5Padding(dec);
removePKCS5Padding
function removePKCS5Padding(text) {
let pad = ord(text[text.length - 1]);
pad = text.substr(0, -1 * pad)
if (_.isEmpty(pad)) {
return text;
} else {
return pad;
}
}
First you don't need to implement the whole module, just particular algorithms from it.
Second whoever wrote that code didn't know what they were doing. SHA-256 already produces a 32-byte value (always) so .slice(0,32) accomplishes nothing. And createCipher[iv] and createDecipher[iv] for a block mode already add and remove 'PKCS5' padding automatically unless explicitly disabled. (Prior to PKCS5v2.1 it was technically more correct to say PKCS7 or PKCS5/7, but in practice people often don't bother, and Java calls it PKCS5. OpenSSL, which nodejs crypto uses internally, punts and calls it PKCS padding -- although there are several PKCS1 paddings which are quite different, and which OpenSSL also implements.)
byte[] keyIn = "0Z8ZUcy1Qh8lnt199MTwTPEe2g1E2tE3" .getBytes("ASCII");
// if any non-ASCII char(s) must select same encoding nodejs does, I believe utf8
// instead of string form can use e.g. StandardCharsets.US_ASCII
byte[] keyHash = MessageDigest.getInstance("SHA-256") .doFinal(keyIn);
// or "sha-256" Java crypto names are case-insensitive
// can separate steps with hasher = .getInstance(); hasher.update(keyIn); result = hasher.doFinal()
// but cannot do fluent-style result = .getInstance() .update(keyIn) .doFinal()
byte[] iv = new byte[16]; // Java automatically fills numeric array with (binary) zeros
Cipher dec = Cipher.getInstance("AES/CBC/PKCS5Padding");
dec.init (Cipher.DECRYPT_MODE, new SecretKeySpec(keyHash,"AES"), new IvParameterSpec(iv));
String clear = new String( dec.doFinal (Base64.getDecoder().decode( someAuthString )), "UTF-8");
// or StandardCharsets.UTF_8
Related
The padding problem
1) I'm encrypting a message in ruby using a public key with PKCS1_PADDING.
2) Then converting the output (which is ASCII-8BIT encoded) to hex and sending it to the android devie.
3) On android converting the hex to byte array & decrypting it using private key, I am getting a lot of additional chars. (ON android side its defaulted to RSA/NONE/PKCS1Padding).
Example:
Expected string: hello how are you doing ?
Actual string: V')f�rBA�;\�:�D��.a�~�A#�.P�(� �l��-�ך��\�0}�nj.F�#Ƨ�Wr[��k��Ez��o��偣�r�����K����1D�涮���U!�t�.UI?�gA��|X��o#v�K��Ə����'��n�F������
P܆�0��9m9*u�٘S�1�������<>�L�?��;3�_���~�-)�$�����Ũ
*"���%/Oѡ�k#��hello how are you doing?
JAVA CODE:
public String Decrypt (String result,String privKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
PrivateKey privateKey = getPrivateKeyFromString(privKey);
Cipher cipher1 = Cipher.getInstance("RSA");
cipher1.init(Cipher.DECRYPT_MODE, privateKey);
String decrypted="";
try {
byte[] bytes = hexStringToByteArray(result);
byte[] decryptedBytes = cipher1.doFinal(bytes);
decrypted = new String(decryptedBytes);
}catch (Exception e)
{
e.printStackTrace();
}
return decrypted;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
RUBY CODE:
require 'openssl'
require 'base64'
public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn6fT8ScFrW2FR5bxTeFzsD77nN1W+gL5XUB1yQVNL699y6WISopbQ6lls76XvKfyhJHn7ca8i5rDRXrNnaY1BVvX9n/jKWLw13AQcVG4SjMewMQbW1KXOWFe2cltGxB7dX+4xlnxRtXz26xtOpEoBdMN2LBB39WdMghaLIrzcNu9uj363KK8szs9x9rO9E5BNfaqePFwajJoOXjkc5PUwRHeW2DodQnKfxJhaBwotoBbD6zrx+XPqpEzXD7XLjq2i/MGEuw6XGLCGQ+/zaytiYCDe8gboQ5WkWQtfa0FALve9zguqjpoNouWaK4SBq1kyeFKsdsbmZLC8NdJlSruUQIDAQAB"
rsa_public_key = OpenSSL::PKey::RSA.new(Base64.decode64(public_key))
encrypted_string = rsa_public_key.public_encrypt('hello how are you doing ?', OpenSSL::PKey::RSA::PKCS1_PADDING)
encrypted_string.unpack("H*")
For maximum portability you should use "RSA/ECB/PKCS1Padding" as initialization string for the cipher.
This string has actually been defined in the Java Standard Algorithm Names as required for any Java implementation. Of course Android isn't officially Java yet, but you can be sure that Google will try and make sure that it will be Java as close as it gets. So this should be compatible with any Java(-ish) implementation.
What is not required by Sun is that the mode of operation ("ECB" in above string) and padding scheme ("PKCS1Padding") are the default for "RSA". That why you have to specify those explicitly. Never rely on provider-specific defaults - except when specifying the random number generator.
What you currently get is the "RSA/ECB/NoPadding" scheme which leaves all the padding intact. So when you look at the plaintext size in bytes it will be identical to the size in bytes of the modulus. And the contents will be the PKCS#1-padding, which is (mostly) randomized for each encryption. Random bytes cannot be converted to text easily, so what you get back mainly looks like garbage.
Notes:
"ECB" is a bit of a misnomer by Sun, it should have been "None" as only one block of plaintext can be encrypted (in general);
you should also make explicit the character set when converting bytes to string, even though Android has UTF-8 as default (Java on Windows uses Windows-1252 encoding!);
the best random number generation is generally pretty platform specific, so using a specific algorithm may actually lower the security of your implementation, doubly so for the ill-defined "SHA1PRNG".
My output that I have to match is from Java DESede using a BouncyCastle Jar from 2005 ... I am very close...
Here is my output in Java (which is correct) followed by my output in C# ... if you view in an editor, you will see they ALMOST match, except in C# where it has a forward slash "/" in Java it has "%2F", "%2D" and at the end where C# has is an "=" , Java has "%3D". Any ideas? (I added spaces to show they match up - but you will only see them in an editor.)
F3e8sdZ%2F951IRiguIAVqfDLyWptqlbWik5tvFzItcxJCEmupzD9wXp%2BDzIbrf2J2dPpXyEXL2QU%3D (Java - Correct)
F3e8sdZ/ 951IRiguIAVqfDLyWptqlbWik5tvFzItcxJCEmupzD9wXp+ DzIbrf2J2dPpXyEXL2QU= (C# - Close?)
Here is my C# Code:
public static string DoubleTrippleDESede(string strToEncode, ref string symKey, ref ICryptoTransform cipher)
{
try
{
//byte[] input = Encoding.UTF8.GetBytes("DESede (3DES) Encryption in RAILO CFML");
byte[] input = Encoding.UTF8.GetBytes(strToEncode);
//byte[] key = Convert.FromBase64String("ru8femXhTm9jwdGdhb/4Sw==");
byte[] key = Convert.FromBase64String(symKey);
TripleDESCryptoServiceProvider algorithm = new TripleDESCryptoServiceProvider();
algorithm.Mode = CipherMode.ECB;
algorithm.BlockSize = 64;
algorithm.KeySize = 192; // 24 byte key
algorithm.Key = key; //Original
//algorithm.Key = key.CopyTo(algorithm.Key,)
cipher = algorithm.CreateEncryptor();
byte[] encrypted = cipher.TransformFinalBlock(input, 0, input.Length);
Debug.WriteLine("encrypted (.NET): {0}", Convert.ToBase64String(encrypted));
return Convert.ToBase64String(encrypted);
}
catch (Exception ex)
{
return ex.Message;
}
}
Any guidance would be greatly appreciated!!!! I've been at this for 2 weeks and finally can taste victory (I think!?)
Your Java output appears to have additionally been urlencoded. You should be able to call System.Uri.EscapeDataString() to match your present output.
I try to implement this Java method in Python, but it seems to hard to rewrite it in pure Python.
public static String CalculateHash(String input, String token) {
SecretKeySpec signingKey = new SecretKeySpec(token.getBytes(), "HmacSHA1");
Mac mac = null;
mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
assert mac != null;
byte[] bytes = mac.doFinal(input.getBytes(Charset.forName("UTF-8")));
String form = "";
for (byte aByte : bytes) {
String str = Integer.toHexString(((int) aByte) & 0xff);
if (str.length() == 1) {
str = "0" + str;
}
form = form + str;
}
return form;
}
I tried this one, but it generates other hash.
def sign_request():
from hashlib import sha1
import hmac
# key = CONSUMER_SECRET& #If you dont have a token yet
key = "CONSUMER_SECRET&TOKEN_SECRET"
# The Base String as specified here:
raw = "BASE_STRING" # as specified by oauth
hashed = hmac.new(key, raw, sha1)
# The signature
return hashed.digest().encode("base64").rstrip('\n')
What and how should I use in standart Python library to rewrite it? Thank you
Your python code and java code don't match in the fact that the python code uses base 64, while the java code uses hexadecimal (base 16).
You should change the phyton code to use base16 for its output, this can be done with the hex() function, caring to correctly pad the number with the correct numbers of 0 characters the java code does.
I'm trying to encrypt a message in Java and decrypt it in Python. Unfortunately i'm just starting with python and am not able to get the decryption working.
That's my Java Code:
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = sr.generateSeed(16);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKey aesKey = keygen.generateKey();
//save byte array in text file to recreate key later
byte[] encodedKey = aesKey.getEncoded();
new File("myPath\\AESKey.txt");
FileOutputStream fos = new FileOutputStream("myPath\\AESKey.txt");
//save AesKey in first 16 bytes and Initial Vector in next 16 bytes
fos.write(encodedKey);
fos.write(iv);
fos.close();
String secretText = "Hello cryptography";
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
byte[] encrypted = cipher.doFinal(secretText.getBytes());
BASE64Encoder myEncoder = new BASE64Encoder();
String encodedSecretText = myEncoder.encode(encrypted);
new File("myPath\\encodedSecretText.txt");
FileOutputStream fos2 = new FileOutputStream("myPath\\encodedSecretText.txt");
fos2.write(encodedSecretText.getBytes());
fos2.close();
I was able to decrypt the message with java, but not with python. I hope someone can show me how to do this.i copied the part with padding from another answer and assume that's the problem.
I get the message: ord() expected string of length 1, but int found.
Python:
from Crypto import Random
from Crypto.Cipher import AES
import base64
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
#read bytes of aesKey
file = open("myPath/AESKey.txt","rb")
aesKey = file.read(16)
iv = file.read(16)
file.close()
sec = open("myPath/encodedSecretText.txt")
for line in sec:
encodedSecretText = line.rstrip()
sec.close()
class AESCipher:
def __init__( self, key ):
self.key = key
def encrypt( self, raw ):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
def decrypt( self, enc ):
enc = base64.b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[16:] ))
aes = AESCipher(aesKey)
print(aes.decrypt(encodedSecretText))
Thanks for any hint.
You're calling ord on an integer. Which is obviously illegal. The whole point of ord is that you give it a Unicode character, as a string, and it gives you back the numerical value of the code point.
So, why do you have a number? Well, I'm not sure what you expected to have, but let's look at what you actually have. If s[-1] is an integer, then s is some kind of sequence of integers. And s is the result of calling cipher.decrypt(). As the documentation for that function says, it returns a byte string. This isn't a specific type, just a description of a type—you can find out what the actual return value is with some basic debugging, maybe like this:
cipher = AES.new(self.key, AES.MODE_CBC, iv )
plaintext = cipher.decrypt(enc[16:])
print(type(plaintext), repr(plaintext))
return unpad(plaintext)
But I'm going to guess that it's a bytes object, which (quoting from http://docs.python.org/3/library/functions.html#bytes) …
is an immutable sequence of integers in the range 0 <= x < 256.
So, s[-1] is an integer in the range [0, 256). Hence the error.
So, what should you be doing instead? Well, why are you trying to call ord? You have a byte. Presumably what you want is a byte. So… just don't call anything there.
Meanwhile, there's at least one other serious error in your code:
for line in sec:
encodedSecretText = line.rstrip()
sec.close()
As pasted, this will raise an IndentationError. And if you indent both the second and third lines, you'll get an error for reading from a closed file. So presumably you want to indent just the second one. In which case, what you're doing is going through all of the lines, stripping the trailing whitespace off each, and then doing nothing with them. At the end of the loop, encodedSecretText holds the last line of encoded text, and all of the other lines are long forgotten and unrecoverable.
If you want to read all of the text into a list of lines, you will want something like this:
encodedSecretText = []
for line in sec:
encodedSecretText.append(line.rstrip())
Or, if you want to read it into one big string, with the newlines removed, you could do the above and then encodedSecretText = b''.join(encodedSecretText), or just do skip the whole loop and do encodedSecretText = sec.read().replace(b'\n', b'').
I will post my code. Sorry for the confusion.
StringBuilder texto1 = new StringBuilder("LALALLA");
byte[] x = texto1.toString().getBytes();
try {
Cipher cifrado = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cifrado.init(Cipher.ENCRYPT_MODE, key1.getPublic());
x = cifrado.doFinal(x);
String texto;
texto = new String(x, "UTF-8");
JOptionPane.showInputDialog(publicKey.toString());
String teste = "";
for (int i = 0; i < x.length; i++) {
teste += x[i];
}
jTextPane1.setText(teste);
//cifrado.init(Cipher.DECRYPT_MODE, privatekey);
byte[] y;
// x= texto.getBytes();
//y = cifrado.doFinal(texto.getBytes());
//texto = new String(y,"UTF-8");
jTextPane2.setText(x.toString());
} ...
That's the code in an action on a button. Everytime that I run this code, with the same keys, texto1 on encryption returns a different result like [B#52a0b1e1 or [B#3e55abb3
The toString() method of arrays in Java doesn't display the content of the array. Instead, it shows the component type and an identifier based on the location of the array in memory.
If you want to see the content of the array, you have to iterate over its elements. And, in this case, you'll have to decide how you want to encode the byte elements to text. It looks like you are trying to do this with your variable teste, but I'd recommend something like this:
StringBuilder buf = new StringBuilder();
for (byte b : x)
buf.append(String.format("%02X", b));
String teste = buf.toString();
This will generate a hexadecimal representation of your ciphertext. You can't create a String from random 8-bit values as you attempt with the variable texto, because the bytes will generally not form valid UTF-8 encoding sequences. You'll end up with a lot of replacement characters in the text (�).
With a hex (or base-64) encoding, you will see that the cipher text still varies randomly. That's because RSA's PKCS #1 padding schemes uses random data to pad the message before encryption. This deliberate feature of RSA prevents an attacker from recognizing when the same message is being sent.