I'm looking for a way to encrypt a string with cipher "DESede/ECB/PKCS5Padding" on windows phone 8.
Unlike Win8 RT, there isn't SymmetricKeyAlgorithmProvider.
I tried to add a BounceCastle c# library, as suggested here, but it seems that there isn't cipher "DESede/ECB/", but only "DESede/CBC/" with IV Key, while i want ECB without IV key.
Any suggestion will be appreciated. Thanks :)
EDIT: Solution with BounceCastle library:
// DESEDE, not DESEDE/CBC!!!
var cipher = CipherUtilities.GetCipher("DESEDE");
byte[] byte_key = Encoding.UTF8.GetBytes(string_key);
var param_key = new DesEdeParameters(byte_key);
byte[] data = Encoding.UTF8.GetBytes(string_data);
cipher.Init(true, param_key);
var data_encrypted = cipher.DoFinal(data);
This is 3DES Encrypt-Decrypt-Encrypt cipher with PKCS#5 padding, and it should be supported by BouncyCastle (search for 3DES ECB).
Also it is supported by SecureBlackbox library (however, it is commercial).
Related
I am trying to generate encrypted text in Node using Crypto.js using its AES algorithm. It works in Js for encryption and decryption both. Similarly I tried implementing same in Python using pycrypto and it does encryption and decryption both in python. But the problem arises when I want to use encrypted cipher text from JS to decrypt in Python. The problem is that the encrypted text generated in JS is different from what is generated in Python.
Here is the JS based usage of AES:
import CryptoJS from "crypto-js";
let str = "lol";
let salt = "ABCDEFGHIJKLMNOP";
let key = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP";
salt = CryptoJS.enc.Hex.parse(salt);
key = CryptoJS.enc.Hex.parse(key);
const options = {
iv: salt,
padding: CryptoJS.pad.ZeroPadding,
mode: CryptoJS.mode.CFB,
};
// function to encrypt the string
function encrypt(str, salt, key) {
const cipher = CryptoJS.AES.encrypt(str, key, options);
console.log("cipher", CryptoJS.enc.Hex.stringify(cipher.ciphertext));
return cipher.ciphertext.toString(CryptoJS.enc.Base64);
}
// function to decrypt the string
function decrypt(str, salt, key) {
const cipher = CryptoJS.AES.decrypt(str, key, options);
return cipher.toString(CryptoJS.enc.Utf8);
}
const encrypted = encrypt(str, salt, key);
console.log("encrypted", encrypted);
const decrypted = decrypt(encrypted, salt, key);
console.log("decrypted", decrypted);
// OUTPUT
// encrypted sd4Xpz/ws8x2j+cgF17t6A==
// decrypted lol
Here is the Python based usage of AES:
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
str = "lol";
salt = b"ABCDEFGHIJKLMNOP";
key = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP";
enc_dec_method = 'utf-8'
def encrypt(str_to_enc, str_key, salt):
aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt)
hx_enc = aes_obj.encrypt(str_to_enc.encode('utf8'))
df = b64encode(hx_enc)
mret = b64encode(hx_enc).decode(enc_dec_method)
return mret
def decrypt(str_to_dec, str_key, salt):
aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt)
str_tmp = b64decode(str_to_dec.encode(enc_dec_method))
str_dec = aes_obj.decrypt(str_tmp)
mret = str_dec.decode(enc_dec_method)
return mret
test_enc_text = encrypt(str, key, salt)
test_dec_text = decrypt(test_enc_text, key, salt)
print(f"Encrypted Text: {test_enc_text}")
print(f"Decrypted Text: {test_dec_text}")
# OUTPUT
# Encrypted Text: o9XB
# Decrypted Text: lol
I tired encrypting and decrypting in both the languages and check the the documentation and source code on both the libraries.
Did also trying checking if I am missing encoding or decoding, but had no luck.
What key concept am I missing here which can help bridge the gap of compatibility here?
The codes are incompatible because:
Key and IV are not hex encoded, so the hex encoder must not be used in the CryptoJS code. If Utf-8 encoding is to be applied as in the Python code, the Utf-8 encoder must be used instead.
salt = CryptoJS.enc.Utf8.parse(salt);
key = CryptoJS.enc.Utf8.parse(key);
CFB is a stream cipher mode that does not require padding. However, in the current CryptoJS code, Zero padding is used (CryptoJS.pad.ZeroPadding). Instead, the padding has to be disabled (which does not happen implicitly, unlike in the Python code):
padding: CryptoJS.pad.NoPadding,
CFB is configured with an additional parameter, the segment size, which specifies the number of bits encrypted per encryption step. The CryptoJS code supports only one segment size, 128 bits. With PyCryptodome the segment size can be configured, by default 8 bits are used. For compatibility it must be changed to 128 bits:
aes_obj = AES.new(str_key.encode('utf-8'), AES.MODE_CFB, salt, segment_size=128)
With these changes both codes are compatible.
Test: Both codes provide the following ciphertext for the following plaintext:
plaintext: The quick brown fox jumps over the lazy dog
ciphertext: m0T/7e04eV49RTcgd7KtwHxSOavNzHNwlrvjt1YmmHidBy4rHS0oovclKQ==
Note that what you call salt is actually the initialization vector (IV). The term salt is more commonly used in the context of key derivation functions (like PBKDF2).
Regarding security: The values used for salt and IV are OK for testing, but in a real world scenario a random byte sequence has to be applied for the IV for each encryption, which has to be passed along with the ciphertext to the decrypting side (generally concatenated).
Also, as key no passphrase has to be used, but a random byte sequence. A passphrase (a strong one of course) may only be applied in combination with a key derivation function.
I am writing Python Decryption logic to decrypt the message (client-side it is encrypted using Java).
Python code:
def decrypt(self, text):
decode = base64.b64decode(text)
cryptor = AES.new(InKey, MODE_CBC, InIV)
plain_text = cryptor.decrypt(decode)
return unpad(plain_text)
I have received below key & IV values (sample values) from the client team:
InKey =
"6asd6587daIs8g2qvi3rJbM9sdasd6cb2kdYC0TOy5zEgTo+8LrQn0UJZAmJCtmX......"(it
is length of 684 chars) InIV =
"7as76cascsagoKtID7z1nUakJqzj+Dwl9cL9Q2/zBFbs0Sg3Kw6US8yvvzbkyg2bnjGHWofIWrhMQ/Bcde...."
(it is length of 684 chars)
Question: While running the above python code, getting the below error. How to convert the above IV value into 16bytes in size and also key-value length?
ValueError: Incorrect IV length (it must be 16 bytes long)
existing Decryption Java Client follows Hybrid pattern(first using RSA private key to pull two params(IV & Key)...and then AES cipher to decrypt actual message):
Pseudo code:
................................
//Step-1) Generate RSA-PrivateKey
................................
RSAPrivateKey serverPrivateKey = KeyUtils.getPrivateKey("lib", PrivKey);
/* here PrivKey refers to .der certificate
and FYI- KeyUtils actually refers to a class, that generate RSA PrivateKey with below code:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(privKeyBytes);
return (RSAPrivateKey)keyFactory.generatePrivate(ks);
*/
................................
//Step-2) use RSA-PrivateKey to decrypt the message
................................
MessageEncryption encryptor = new MessageEncryption(serverPrivateKey);
/* initializes Cipher(RSA instance) with passed RSA PrivateKey
this.m_asymmetricalKey = serverPrivateKey;
this.m_asymmetricalCipher = Cipher.getInstance(this.m_asymmetricalKey.getAlgorithm());
*/
encryptor.decryptMessage(JSONresponseBody); // invokes below mentioned method
...............
decryptMessage(JSONObject jsonMessage)
{
.............................
IvParameterSpec initializationVector = new IvParameterSpec(this.m_asymmetricalCipher.doFinal(StringCodec.decodeBase64(jsonMessage.getString("iv").getBytes())));
this.m_asymmetricalCipher.init(2, this.m_asymmetricalKey);
Key secretKey = new SecretKeySpec(this.m_asymmetricalCipher.doFinal(StringCodec.decodeBase64(jsonMessage.getString("key").getBytes())), "AES");
Cipher symmetricalCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
symmetricalCipher.init(2, secretKey, initializationVector);
return new String(symmetricalCipher.doFinal(StringCodec.decodeBase64(jsonMessage.getString("message").getBytes())));
.............................
}
..............
So at Python how to get right key-size & IV, is there way to implement similar hybrid pattern at java or does it any other approach to deal with at python ?
Thanks.
Your Java-side code does the following things:
Read the PKCS8-encoded RSA key
Decode the base64-encoded IV
Decrypt the IV using the private key (a side-note: encrypting IV makes very little sense to me)
Decode the base64-encoded AES key
Decrypt the AES key using the private key
Decode the base64-encoded cyphertext
Decrypt the ciphertext using the obtained IV and AES key
You need to re-implement each of the above steps in Python. Some are trivial, like base64-decoding. The others depend on the cryptography libraries you're using.
I have the following requirements regarding encryption of url parameters. As I am newly to CryptoJS I can not figure out how to perform all steps using CryptoJS. Can anybody help me with example code to perform this steps?
Encrypt Requirements
Generate a secret key from the given password.
Receive the password which is coupled with the system name. (This is already available)
Generate a random nonce in byte-array as the password’s salt which has a length 16-bytes.
Set the number of iterations for secret key as 65536.
Set the length of secret key as 256-bit (not bytes).
Set the algorithm name for key derivation using PBKDF2 with HMAC and SHA-2.
Set the algorithm name of key generation as “AES”.
Create a secret key with the function of library or tool with all sets above.
Encrypt the JSON-string with the above generated secret key.
Generate a random nonce in byte-array as the IV (initial vector) which has a length 12-bytes for encryption.
Select encrypt mode for the crypto in your function or tool for encryption.
Set the name of crypto algorithm which is for AES in GCM mode and without any padding.
Set the length of authentication tag for GCM as 128-bits (not bytes).
Convert your JSON-string which contains customer information to a byte-array(plainText) with character set UFT-8.
Encrypt the plainText with all sets and the secret key in last step to a new byte-array(cipherText)
Prefix the generated IV and password’s salt to the cipherText .
Create a new byte-array (cipherTextWithIvSalt).
Add the 12-bytes IV in cipherTextWithIvSalt at first.
Add the 16-bytes password’s salt in cipherTextWithIvSalt after that.
Add the cipherText in cipherTextWithIvSalt at last.
cipherTextWithIvSalt is now in form “IV + salt + cipherText”
Encode the cipherTextWithIvSalt.
Encode the cipherTextWithIvSalt to a new byte-array (encodedCipherTextWithIvSalt) by using Base64
Build a displayable encrypted string.
Build a new string from byte-array encodedCipherTextWithIvSalt with character set UTF-8.
I have tried to do it myself but it does not work here is the example of my js code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
let secretKey = generateKey(salt);
let iv = CryptoJS.lib.WordArray.random(96 / 8);
let JSON_String = "{"Name":"Max", "Firstname":"Mustermann"}";
let plainText = CryptoJS.enc.Utf8.parse(JSON_String);
let cipherText = encryptJSONString(plainText, secretKey, iv);
let cipherTextWithIvSalt = prefixCipherTextWithSaltAndIV(iv, secretKey, cipherText);
let encodedCipherTextWithIvSalt = CryptoJS.enc.Base64.parse(cipherTextWithIvSalt);
let displayableText = CryptoJS.enc.Utf8.stringify(encodedCipherTextWithIvSalt);
function generateKey(password) {
var salt = CryptoJS.lib.WordArray.random(128 / 8);
return CryptoJS.PBKDF2(password, salt, { keySize: 256 / 32, iterations: 65536, hasher: CryptoJS.algo.SHA256 });
}
function encryptJSONString(plainText, secretKey, iv) {
//mode: CryptoJS.mode.GCM
return CryptoJS.AES.encrypt(plainText, secretKey, { iv: iv, padding: CryptoJS.pad.NoPadding });
}
function prefixCipherTextWithSaltAndIV(iv, secretKey, cipherText) {
let byteArray = [];
byteArray.push(iv.words);
byteArray.push(secretKey.words);
byteArray.push(cipherText);
return byteArray;
}
Thank you very much in advance!
I want to use CTR mode in DES algorithm in python by using PyCryptodome package. My code presented at the end of this post. However I got this error: "TypeError: Impossible to create a safe nonce for short block sizes". It is worth to mention that, this code work well for AES algorithm but it does not work for DES, DES3 , Blowfish and etc (with 64 block size). To my knowledge CTR mode can be applied in the 64 block cipher algorithms.
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
data = b'My plain text'
key = get_random_bytes(8)
cipher = DES.new(key, DES.MODE_CTR)
ct_bytes = cipher.encrypt(data)
nonce = cipher.nonce
cipher = DES.new(key, DES.MODE_CTR, nonce=nonce)
pt = cipher.decrypt(ct_bytes)
print("The message was: ", pt)
Thanks alot.
The library defines the nonce
as that part of the counter block that is not incremented.
Since the block is only 64 bits long, it is hard to securely define how long that nonce should be,
given the danger of wraparound (if you encrypt a lot of blocks) or nonce reuse (if you generate the nonce randomly).
You can instead decide that the nonce is not present, the counter takes the full 64 bits and a random initial value.
iv = get_random_bytes(8)
cipher = DES.new(key, nonce=b'', initial_value=iv)
Finally, I guess that this is only an exercise.
DES is a very weak cipher, with a key length of only 56 bits and a block size of only 64 bits.
Use AES instead.
bs = DES.block_size
plen = bs - len(plaintext) % bs
padding = [plen] * plen
padding = pack('b' * plen, *padding)
key = get_random_bytes(8)
nonce = Random.get_random_bytes(4)
ctr = Counter.new(32, prefix=nonce)
cipher = DES.new(key, DES.MODE_CTR,counter=ctr)
ciphertext = cipher.encrypt(plaintext+padding)
I'm working on the porting of an instance messaging application from Java to JavaME ,that also implements cryptography. The problem is that I want to send my public key to the server. The desktop client has this code for this job:
byte[] encoded_public_key=publick_key.getEncoded();
And the server has this code to retrieve the key:
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encoded_public_key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey puKey = keyFactory.generatePublic(publicKeySpec);
Now I've looked the API for the getEncoded and it says that it returns the DER-encoded form of the key as a byte array (http://www.docjar.com/docs/api/sun/s...tml#getEncoded)
My implementation for that in JavaME was this:
RSAPublicKeyStructure public_key_JAVAME=new RSAPublicKeyStructure(modulus,exponent);
byte[] DER_encoded_public_key_JAVAME=public_key_JAVAME.getDEREncoded();
//the getEncoded functions returns exact the same byte array.
However when I try to retrieve the JavaME created DER encoded key with the server code ,in other words when I try this:
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(DER_encoded_public_key_JAVAME);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey puKey = keyFactory.generatePublic(publicKeySpec);
I get
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:188)
at java.security.KeyFactory.generatePublic(KeyFactory.java:304)
Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.x509.X509Key.decode(X509Key.java:380)
at sun.security.x509.X509Key.decode(X509Key.java:386)
at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:66)
at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:281)
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:184)
Interesting point : The DER encoded key from the normal Java (using the getencoded() function) is a byte array is 162 bytes long while the SAME key DER encoded in JavaME using bouncy castle is 140 bytes long. Shouldn't these 2 DER encoded key be of the same lenght?I mean it's the same key in DER encoded format so they should be the same.
What am I doing wrong?
True I didn't notice that.Problem is do you know how to create a subjectPublickeyInfo object from a PublicKey in bouncyCastle? I've tried:
ByteArrayInputStream bIn = new ByteArrayInputStream(RSApublickey.toString().getbytes());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(bIn).readObject());
But it didn't work. I also tried :
ByteArrayInputStream(RSApublicKeyStructure.getEncoded());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(bIn).readObject());
Actually I did expect that not to work , but well I had to try it . So how can I create a Subjectpublickeyinfo from RSAkeyparameters?( This is one of the points where the obscurity of bouncy's castle API really shines I guess)
Again thank you for your response you've been of great help.You've put me on the right track.
DER-encoding is just a standard for encoding. Saying that a key is DER-encoded is equivalent to saying it is XML-encoded: you need to agree on how it is DER-/XML-encoded to be able to decode it.
In this case your RSAPublicKeyStructure.getEncoded() returns the key as the DER-encoding of an ASN.1 RSAPublicKey:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
The X509EncodedKeySpec on the other hand expects to be handed the DER-encoding of an ASN.1 PublicKeyInfo:
PublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
PublicKey BIT STRING
}
To create a PublicKeyInfo using BouncyCastle do this (courtesy of GregS):
RSAPublicKeyStructure rsaPublicKey = /* ... */
AlgorithmIdentifier rsaEncryption = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(rsaEncryption, rsaPublicKey);
byte[] encodedPublicKeyInfo = publicKeyInfo.getEncoded();