I am going to integrate two web applications written in different platforms (Java and Ruby),
I have to use common encryption algorithm for password in both application.
Is there any common encryption/decryption algorithm for both? If yes, please mention any useful link or any example.
It would be highly appreciated. Thanks in advance
In addition during my digging out it I found,
I have used Base64 with DES in both, interesting thing is that Characters and special characters give me same result in both but as i adding any number like (1,2,3), half of result is same and half encryption is something different.
*Ruby Code
require 'openssl'
require 'base64'
c = OpenSSL::Cipher::Cipher.new("des")
c.encrypt
c.key ="REPPIFY_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
e = c.update("ankit#123")
e << c.final
puts Base64.encode64(e)
Output: Cbe9GslMs8mh33jAOD9qsw==
*Java Code
I am defining only encryption method here:-
public static String encryptPassword(String pass) {
public static final String DESKEY = "REPPIFY_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
System.out.println("Here is my password = "+pass);
DESKeySpec keySpec = null;
SecretKeyFactory keyFactory = null;
SecretKey key = null;
Cipher cipher = null;
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] cleartext = null;
String encrypedPwd = null;
String pass = "ankit#123";
try {
keySpec = new DESKeySpec(DESKEY.getBytes("UTF8"));
keyFactory = SecretKeyFactory.getInstance("DES");
key = keyFactory.generateSecret(keySpec);
if(pass!=null) {
cleartext = pass.getBytes("UTF8");
cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
encrypedPwd = base64encoder.encode(cipher.doFinal(cleartext));
}
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} // cipher is not thread safe
catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
System.out.println("Here I am printing encrypted pwd = "+encrypedPwd);
return encrypedPwd;
}
Output in Java :- Cbe9GslMs8mWn9yTmZrUiw==
Well, in the Ruby world, I'd recommend BCrypt, which is also favored by popular authentication plugins like Devise. I'm not very familiar with Java but a quick search suggests that there's BCrypt implementation in Java too:
http://www.mindrot.org/projects/jBCrypt/
EDIT - BCrypt is 1-way encryption, mainly for use in hashing passwords. If you are looking for something that will encrypt and decrypt then you'll have to look at something else. Seeing as you mentioned it's for passwords I'd suggest you only want 1-way encryption though.
I got the answer...just change a following line in ruby code and then you can use base64 decoder with DES in both:
c = OpenSSL::Cipher::Cipher.new("DES-ECB")
require 'openssl'
require 'base64'
c = OpenSSL::Cipher::Cipher.new("DES-ECB")
c.encrypt
c.key ="REPPIFY_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
e = c.update("ankit#123")
e << c.final
puts Base64.encode64(e)
Related
I have the following code on Java that decrypts AES encryption and I need to do the same on Node.js
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(String myKey) {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String decrypt(String strToDecrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
I have tried using Crypt under the following code, but it doesn't give me the same results
var aesDecrypt = (text, password, bit) => {
var decipher = crypto.createDecipheriv('aes-' + bit + '-ecb', password, Buffer.alloc(0));
decipher.setAutoPadding(false);
return Buffer.concat([
decipher.update(text, 'base64'),
decipher.final()
]).toString();
};
How could I mimick that Java code from above into Node.js?
As James says, the Java code is hashing (and truncating) the password to form the key. Also it does use standard padding. The following works for ASCII data:
const crypto = require ('crypto');
const mydecrypt = (pw,ctx) => {
var h = crypto.createHash('sha1'); h.update(pw,'utf8'); var k = h.digest().slice(0,16);
var d = crypto.createDecipheriv('aes-128-ecb', k, Buffer.alloc(0));
return Buffer.concat([d.update(ctx,'base64'), d.final()]) .toString();
}
console.log(mydecrypt('password','ks7qtmk7kt5riV/Qyy3glQ=='));
->
testdata
It may not work for non-ASCII data. Java new String(byte[]) uses a JVM-dependent encoding which may be UTF8 or may be something different depending on your platform, build, and environment, none of which you described. OTOH nodejs Buffer.toString() always uses UTF8. You may need to change it to toString(somethingelse) to match the Java.
If this 'password' is truly a password, i.e. chosen or even remembered by one or more human(s), using a simple hash of it is very weak and will probably be broken if used for anything not utterly trivial; you should use a Password-Based Key Derivation Function designed for the purpose by someone competent, like older (PKCS5) PBKDF2 or newer bcrypt, scrypt, or argon2. However, that's not a programming question and is offtopic here; it has been discussed many times and at length on https://crypto.stackexchange.com and https://security.stackexchange.com .
All I need is just to get this lines of code in C#, Actually I can't find X509EncodedKeySpec in C#:
byte[] keyByte = Base64.decode(publicKey);
// generate public key
X509EncodedKeySpec s = new X509EncodedKeySpec(keyByte);
KeyFactory factory = null;
try
{
factory = KeyFactory.getInstance("RSA");
}
catch (NoSuchAlgorithmException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Key pubKey = null;
try
{
pubKey = factory.generatePublic(s);
}
catch (InvalidKeySpecException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
As of .NET Core 3.0 ImportSubjectPublicKeyInfo is available
https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsa.importsubjectpublickeyinfo?view=netcore-3.0
You should be able to use it as follows:
using (RSA rsa = RSA.Create())
{
rsa.ImportSubjectPublicKeyInfo(publicKey, out _);
//do your stuff
}
X509EncodedKeySpec is Java's version of X.509 SubjectPublicKeyInfo. .NET doesn't have any API built-in for reading SubjectPublicKeyInfo. For best results, you want to have a full certificate, then use cert.GetRSAPublicKey().
If you can get the transmitted as the RSA Modulus and RSA (Public) Exponent values then you could build the RSAParameters object to load into a key.
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(new RSAParameters { Modulus = modulus, Exponent = exponent });
// do public key things
}
referring this, I have to encrypt using algorithm AGCM256-KW. I am using Java Cryptography and I didn't find any such algorithm. Closest I found was AES_256/GCM/NoPadding but it has no KW (Key wrapping).
here is my test code
public void testEncryption(String algo) {
String shared_secret = "LyQnklSrxsk3Ch2+AHi9HoDW#//x1LwM123QP/ln";
try {
// Step 1 - Create SHA-256 digest of the shared key
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(shared_secret.getBytes("UTF-8"));
// Step 2 - generate a 256 bit Content Encryption Key(CEK)
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
SecretKey cek = kg.generateKey();
// Step 3 - encrypt the CEK using 256 bit digest generated in Step 1
// and 96 bit random IV. Algorithm should be
// random 96 bit Initialize Vector
SecureRandom random = new SecureRandom();
// byte iv[] = new byte[96];
// random.nextBytes(iv);
byte iv[] = random.generateSeed(96);
System.out.println("IV: " + toBase64(iv) + " length: " + iv.length);
IvParameterSpec ivspec = new IvParameterSpec(iv);
GCMParameterSpec gspec = new GCMParameterSpec(96, iv);
// encrypt
Cipher cipher = Cipher.getInstance(algo);
System.out.println(String.format("CEK Cipher alg:%S provider:%S", cipher.getAlgorithm(),
cipher.getProvider().getName()));
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(digest, "AES"), gspec);
byte[] result = cipher.doFinal(cek.getEncoded());
System.out.println(String.format("Encrypted CEK :%S", toBase64(result)));
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Update 1
I think I can use jose4j library which has APIs for JWE.
Yes, the Visa Token Services appear to be using JWE (now RFC 7516) so you can use jose4j for this. Here's some sample code that shows encrypting and decrypting some content with JWE using A256GCMKW and AGCM256:
// shared secret hashed to key from your example
String shared_secret = "LyQnklSrxsk3Ch2+AHi9HoDW#//x1LwM123QP/ln";
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(shared_secret.getBytes("UTF-8"));
JsonWebEncryption jwe = new JsonWebEncryption();
// A256GCMKW for key wrap
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A256GCMKW);
// A256GCM for content encryption
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_256_GCM);
// the key (from above)
jwe.setKey(new SecretKeySpec(digest, "AES"));
// whatever content you want to encrypt
jwe.setPayload("some important content to be encrypted and integrity protected");
// Produce the JWE compact serialization, which is where the actual encryption is done.
// The JWE compact serialization consists of five base64url encoded parts
// combined with a dot ('.') character in the general format of
// <header>.<encrypted key>.<initialization vector>.<ciphertext>.<authentication tag>
String serializedJwe = jwe.getCompactSerialization();
// Do something with the JWE. Like send it to some other party over the clouds
// and through the interwebs.
System.out.println("JWE compact serialization: " + serializedJwe);
// That other party, the receiver, can then use JsonWebEncryption to decrypt the message.
JsonWebEncryption receiverJwe = new JsonWebEncryption();
// Set the compact serialization on new Json Web Encryption object
receiverJwe.setCompactSerialization(serializedJwe);
// Symmetric encryption, like we are doing here, requires that both parties have the same key.
// The key will have had to have been securely exchanged out-of-band somehow.
receiverJwe.setKey(new SecretKeySpec(digest, "AES"));
// Get the message that was encrypted in the JWE. This step performs the actual decryption steps.
String plaintext = receiverJwe.getPlaintextString();
// And do whatever you need to do with the clear text message.
System.out.println("plaintext: " + plaintext);
Let's assume you do indeed require AES in GCM mode (I've never heard of AGCM, but I guess this is a logical assumption that it means AES/GCM). Then the following could be used for (un)wrapping a secret key. Note that I didn't get this to work using IvParameterSpec, at least not for the Oracle JCE.
SecretKey sk = new SecretKeySpec(new byte[16], "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, new byte[12]);
cipher.init(Cipher.WRAP_MODE, sk, gcmSpec);
byte[] wrappedKey = cipher.wrap(sk);
System.out.println(Hex.toHexString(wrappedKey));
cipher.init(Cipher.UNWRAP_MODE, sk, gcmSpec);
SecretKey unwrap = (SecretKey) cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
System.out.println(Hex.toHexString(unwrap.getEncoded()));
Note that using SIV mode should probably be somewhat preferred for wrapping keys as you do do not require to store an IV and authentication tag together with the wrapped key (storing the IV is not shown in the example). The above code relies on a unique IV for security (also not shown).
Obviously it's also not a good idea to wrap a key with itself. Sorry, I got a bit lazy here; I've just shown how to use the cipher.
The "KW" refers to "key wrapping," as defined in RFC 3394. The name for this algorithm in the JCE is "AESWrap". So, the transformation should be "AESWrap/GCM/NoPadding". As Maarten pointed out, logically this operation should configure the Cipher in WRAP_MODE.
I have a private key file encripted with DES/ECB/PKCS5Padding (56 bit DES key generated by a secret phrase) and I want to decrypt it.
I don't know why, but everytime I try to decript, the method doFinal of my cipher class is throwing this error:
javax.crypto.BadPaddingException: Given final block not properly
padded at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.SunJCE_f.b(DashoA13*..) at
com.sun.crypto.provider.DESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..) at...
Here is my code:
public static PrivateKey readPrivateKeyFromFile(File file, String chaveSecreta) {
try {
SecureRandom r = new SecureRandom(chaveSecreta.getBytes());
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
keyGen.init(56, r);
Key key = keyGen.generateKey();
byte[] privateKeyBytes = decryptPKFile(file, key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = null;
try {
privateKey = keyFactory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException e) {
JOptionPane.showMessageDialog(null, "Erro 01, tente mais tarde");
}
return privateKey;
} catch (NoSuchAlgorithmException e) {
JOptionPane.showMessageDialog(null, "Erro 02, tente mais tarde");
}
return null;
}
public static byte[] decryptPKFile(File file, Key key){
try{
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] cipherText = readBytes(file);
cipher.init(Cipher.DECRYPT_MODE, key);
System.out.println(cipher);
System.out.println(cipherText);
byte[] text = cipher.doFinal(cipherText);
return text;
}catch(Exception e){
e.printStackTrace();
return null;
}
}
public static byte[] readBytes(File file) {
try {
FileInputStream fs = new FileInputStream(file);
byte content[] = new byte[(int) file.length()];
fs.read(content);
return content;
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado!");
e.printStackTrace();
} catch (IOException ioe) {
System.out.println("Erro ao ler arquivo!");
ioe.printStackTrace();
}
return null;
}
Any syggestions?
You're trying to decrypt ciphertext with a random number generator created using a specific seed. However, you don't specify the algorithm, and the algorithm may change internally as well. Android is even known to generate a fully random value instead for some versions.
You need to use a SecretKeyFactory not a KeyGenerator. And you will of course need the 8-byte key data. The only way to retrieve this in your case is to find the SecureRandom algorithm/implementation before and re-calculate the key.
Now any ciphertext will decrypt with any key. DES ECB only provides (some sort of) confidentiality, not integrity. The problem is that it will decrypt into garbage. Now if you try to remove the padding from garbage you will likely get a padding error.
If you're "lucky" - once in about 256 times - you will get a result. This happens when the decrypted block ends with 01 or 0202, that's valid padding. The result will - of course - be garbage as well, but it will not end with a BadPaddingException. In your case the SecureRandom instance is likely to return the same incorrect value over and over though, so this may never happen.
In the future, please use PBKDF2 and feed it the encoded password. Clearly note the character encoding used, Java SE uses the lowest 8 bits of the char array. Never ever use String.getBytes() as the default encoding may differ between systems.
I've been chipping away at a school assignment for 3 days, and finally finished it today, error-free and working fine! Except, I was testing it on Java 1.7, and the school servers (where the professor will compile it) run 1.6. So, I tested my code on 1.6, wanting to cover all my bases, and I get a BadPaddingException upon decryption.
[EDIT] Warning: this code does not follow common security practices and should not be used in production code.
Originally, I had this, which works fine on 1.7 (sorry, lots of code.. all relevant..):
public static String aes128(String key, String data, final int direction) {
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
try {
if (direction == ENCRYPT) {
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
However, my BadPaddingException catch block executes upon decryption:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at CipherUtils.aes128(CipherUtils.java:112)
at CipherUtils.decryptFile(CipherUtils.java:44)
at decryptFile.main(decryptFile.java:21)
This is what I tried to fix it (basically, I added all the padding/unpadding myself, and used NoPadding):
public static String aes128(String key, String data, final int direction) {
// PADCHAR = (char)0x10 as String
while (key.length() % 16 > 0)
key = key + PADCHAR; // Added this loop
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} catch (InvalidAlgorithmParameterException e) {
return null; // Added this catch{}
}
try {
if (direction == ENCRYPT) {
while (data.length() % 16 > 0)
data = data + PADCHAR; // Added this loop
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
When using this, I just get gibberish in and out:
Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)
It is also worth noting that 1.6 and 1.7 produce different encrypted strings.
For example, on 1.7, encrypting xy (including a SHA-1 hash) with key hi produces:
XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=
On 1.6, the same thing produces:
nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=
I didn't expect the assignment to take so long, so my time has run out and it does need to be done tonight. If there is no answer by then, however, I'll just leave a note to my teacher regarding this. It appears to be some issue that was fixed in 1.7... though hopefully can be remedied through the correct addition/fix in my code.
Thanks a ton for everyone's time!
First off:
For almost all systems, encrypting the same plaintext twice should always (i.e. with very very high probability) produce different ciphertext.
The traditional example is that it allows a CPA adversary to distinguish E("attack at dawn") from E("attack at dusk") with only two queries. (There are a handful of systems where you want deterministic encryption, but the right way to do this is "synthetic IV" or cipher modes like CMC and EME.)
Ultimately, the problem is that SecureRandom() is not intended for key derivation.
If the input "key" is a passphrase, you should be using something like PBKDF2 (or scrypt() or bcrypt()).
Additionally, you should be using an explicit charset, e.g. String.getBytes("UTF-8").
If the input "key" is a key, the most common string representation is a hexdump. Java doesn't include an unhexing function, but there are several here.
If the input is a "master key" and you want to derive a subkey, then you should be hashing it with other data. There's not much point if the subkey is always the same.
Additional nitpicks:
Your code is vulnerable to a padding oracle attack; you really should be verifying a MAC before doing anything with the data (or better, using an authenticated encryption mode).
In your second listing, you explicitly reuse the IV. Bad! Assuming CBC mode, the IV used should be unpredictable; SecureRandom is useful here.
I've been looking over and over and I have to agree with NullUserException. The problem is the use of SecureRandom. This means that you never really know what your key is and therefore it is not necessarily ever the same key.
encKey comes from SecureRandom, which is seeded by the key provided. Therefore, if the key is the same, the seed is the same, so the random should be the same...
...unless of course Oracle (or another provider) changes the implementation between versions.
Okay, adding more information that I researched. I think this answer was most helpful.
Get password and cleartext from the user, and convert them to byte arrays.
Generate a secure random salt.
Append the salt to the password and compute its cryptographic hash. Repeat this many times.
Encrypt the cleartext using the resulting hash as the initialization vector and/or secret key.
Save the salt and the resulting ciphertext.
To me, it sounds like SecureRandom is used once to generate a salt but then salt must be saved with the cypher text in order to undo the cyphering process. Additional security comes from repetition and variance of steps (obscurity).
Note: I couldn't find any consensus that these steps are best practices.