I have this code in C # which generates an encrypted password.
I would like to make it compatible in Java.
Code C#
private static string ccs = "&Ux#B*******";
private static ICryptoTransform __Get_AES128_Transform(string password, bool AsDecryptor)
{
const int KEY_SIZE = 16;
var sha256CryptoServiceProvider = new SHA256CryptoServiceProvider();
var hash = sha256CryptoServiceProvider.ComputeHash(Encoding.Unicode.GetBytes(password));
var key = new byte[KEY_SIZE];
var iv = new byte[KEY_SIZE];
Buffer.BlockCopy(hash, 0, key, 0, KEY_SIZE);
if (AsDecryptor)
return new AesCryptoServiceProvider().CreateDecryptor(key, iv);
else
return new AesCryptoServiceProvider().CreateEncryptor(key, iv);
}
public static string Encrypt(this string Value)
{
byte[] Buffer = Encoding.Unicode.GetBytes(Value);
using (ICryptoTransform transform = __Get_AES128_Transform(ccs, false))
{
byte[] encyptedBlob = transform.TransformFinalBlock(Buffer, 0, Buffer.Length);
return Convert.ToBase64String(encyptedBlob);
}
}
Could anyone give a direction to decrypt in java?
My answer interoperable encrypt, I hope this helps you. Result can be decrypted from any platform - C# code or Android/Java code.
Encrypt from C# -> Decrypt from C#, Android, Java.
Encrypt from Android -> Decrypt from Android, Java, C#.
Encrypt from Java -> Decrypt from Java, Android, C#.
The result of encrypt contains the encrypted text + IV.
IV is generated using secure random algoritm.
Encrypt process
Transform plaintext => plaintextBytes
Transform password => passwordBytes => Sha256(passwordbytes) = passwordHashBytes
generate random IV bytes = ivBytes
encrypt using plaintextbytes and passwordHashBytes
join (encryptedbytes + ivBytes)
Transform to base64 joined bytes.
Decrypt process
Transform base64 string to bytes.
Split array encryptedBytes, ivBytes
Transform password => passwordBytes => Sha256(passwordbytes) = passwordHashBytes
Decrypt using ivBytes, EncryptedBytes, PasswordHashBytes
Transform DecryptedBytes to String
The IV is randomized in every Encrypt process to get different output with same text and avoid to intruders/attackers get original text easily.
The default mode is CBC.
The encoding used is UTF-8. UTF-8 is the most widely used encoding on the web. Check Usage of character encodings broken down by ranking
References
Initialization vector
Block cipher mode of operation
C# Implementation
using System;
using System.Security.Cryptography;
using System.Text;
public enum HashAlgorithm
{
MD5,
SHA1,
SHA256,
SHA384,
SHA512
}
public class HashManager
{
public static byte[] ToRawHash(byte[] data, HashAlgorithm algorithm)
{
byte[] hash;
switch (algorithm)
{
case HashAlgorithm.MD5:
MD5 md5 = MD5.Create();
hash = md5.ComputeHash(data, 0, data.Length);
return hash;
case HashAlgorithm.SHA1:
SHA1Managed sha1 = new SHA1Managed();
hash = sha1.ComputeHash(data);
return hash;
case HashAlgorithm.SHA256:
SHA256Managed sha256 = new SHA256Managed();
hash = sha256.ComputeHash(data);
return hash;
case HashAlgorithm.SHA384:
SHA384Managed sha384 = new SHA384Managed();
hash = sha384.ComputeHash(data);
return hash;
case HashAlgorithm.SHA512:
SHA512Managed sha512 = new SHA512Managed();
hash = sha512.ComputeHash(data, 0, data.Length);
return hash;
default:
throw new ArgumentException("Invalid Algorithm");
}
}
}
public class AesManager
{
private const int MAX_IV_LENGTH = 16;
private const int MAX_KEY_LENGTH = 32;
private static byte[] GenerateValidKey(byte[] keyBytes)
{
byte[] ret = new byte[MAX_KEY_LENGTH];
byte[] hash = HashManager.ToRawHash(keyBytes, HashAlgorithm.SHA256);
Array.Copy(hash, ret, MAX_KEY_LENGTH);
return ret;
}
public static byte[] EncryptRaw(byte[] PlainBytes, byte[] Key)
{
AesManaged AesAlgorithm = new AesManaged()
{
Key = GenerateValidKey(Key)
};
AesAlgorithm.GenerateIV();
var Encrypted = AesAlgorithm.CreateEncryptor().TransformFinalBlock(PlainBytes, 0, PlainBytes.Length);
byte[] ret = new byte[Encrypted.Length + MAX_IV_LENGTH];
Array.Copy(Encrypted, ret, Encrypted.Length);
Array.Copy(AesAlgorithm.IV, 0, ret, ret.Length - MAX_IV_LENGTH, MAX_IV_LENGTH);
return ret;
}
public static byte[] DecryptRaw(byte[] CipherBytes, byte[] Key)
{
AesManaged AesAlgorithm = new AesManaged()
{
Key = GenerateValidKey(Key)
};
byte[] IV = new byte[MAX_IV_LENGTH];
Array.Copy(CipherBytes, CipherBytes.Length - MAX_IV_LENGTH, IV, 0, MAX_IV_LENGTH);
AesAlgorithm.IV = IV;
byte[] RealBytes = new byte[CipherBytes.Length - MAX_IV_LENGTH];
Array.Copy(CipherBytes, RealBytes, CipherBytes.Length - MAX_IV_LENGTH);
return AesAlgorithm.CreateDecryptor().TransformFinalBlock(RealBytes, 0, RealBytes.Length); ;
}
public static String EncryptToBase64(String Plaintext, String Key)
{
byte[] PlainBytes = Encoding.UTF8.GetBytes(Plaintext);
return Base64Manager.ToBase64(EncryptRaw(PlainBytes, Encoding.UTF8.GetBytes(Key)), false);
}
public static String DecryptFromBase64(String CipherText, String Key)
{
byte[] CiPherBytes = Base64Manager.Base64ToByteArray(CipherText);
byte[] Encrypted = DecryptRaw(CiPherBytes, Encoding.UTF8.GetBytes(Key));
return Encoding.UTF8.GetString(Encrypted, 0, Encrypted.Length);
}
}
public class Base64Manager
{
public static byte[] Base64ToByteArray(String base64)
{
return Convert.FromBase64String(base64);
}
public static String ToBase64(byte[] data, Boolean insertLineBreaks = default(Boolean))
{
return insertLineBreaks ? Convert.ToBase64String(data, Base64FormattingOptions.InsertLineBreaks) : Convert.ToBase64String(data);
}
}
public class Program
{
public static void Main()
{
string plainText = "plain text";
string password = "password";
string encrypted = AesManager.EncryptToBase64(plainText, password);
Console.WriteLine(encrypted);
Console.WriteLine(AesManager.DecryptFromBase64(encrypted, password));
Console.ReadLine();
}
}
Android/Java Implementation
public enum HashAlgorithm {
SHA512("SHA-512"),
SHA256("SHA-256"),
SHA384("SHA-384"),
SHA1("SHA-1"),
MD5("MD5");
private String Value = "";
HashAlgorithm(String Value) {
this.Value = Value;
}
#Override
public String toString() {
return Value;
}
}
import java.security.MessageDigest;
public class HashManager {
public static byte[] toRawHash(byte[] data,
HashAlgorithm algorithm) throws Exception
{
byte[] buffer = data;
MessageDigest messageDigest = MessageDigest.getInstance(algorithm.toString());
messageDigest.reset();
messageDigest.update(buffer);
return messageDigest.digest();
}
}
Base64Manager class Android implementation
import android.util.Base64;
public class Base64Manager {
public static String toBase64(byte[] data,
boolean insertLineBreaks) throws Exception
{
String ret;
if (insertLineBreaks)
{
ret = Base64.encodeToString(data, Base64.DEFAULT);
}
else
{
ret = Base64.encodeToString(data, Base64.NO_WRAP);
}
return ret;
}
public static String toBase64(String data,
boolean insertLineBreaks) throws Exception
{
return toBase64(data.getBytes("UTF-8"), insertLineBreaks);
}
public static byte[] base64ToByteArray(String base64) throws Exception
{
return base64.contains(System.getProperty("line.separator")) ? Base64.decode(base64, Base64.DEFAULT) : Base64.decode(base64, Base64.NO_WRAP);
}
}
Base64Manager class Java8+ implementation
import java.util.Base64;
public class Base64Manager {
public static String toBase64(byte[] data, Boolean insertLineBreaks) throws Exception {
String ret;
if (insertLineBreaks) {
ret = Base64.getMimeEncoder().encodeToString(data);
} else {
ret = Base64.getEncoder().encodeToString(data);
}
return ret;
}
public static String toBase64(String data, Boolean insertLineBreaks) throws Exception {
return toBase64(data.getBytes("UTF-8" ), insertLineBreaks);
}
public static byte[] base64ToByteArray(String base64) throws Exception {
return base64.contains(System.getProperty("line.separator")) ? Base64.getMimeDecoder().decode(base64) : Base64.getDecoder().decode(base64);
}
}
import java.security.SecureRandom;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesManager {
private static final int MAX_IV_LENGTH = 16;
private static final int MAX_KEY_LENGTH = 32;
public static String decryptFromBase64(String cipherText, String key) throws Exception {
byte[] CiPherBytes = Base64Manager.base64ToByteArray(cipherText);
byte[] KeyBytes = key.getBytes("UTF-8");
return new String((decryptRaw(CiPherBytes, KeyBytes)));
}
public static byte[] generateValidKey(byte[] key) throws Exception {
return Arrays.copyOf(HashManager.toRawHash(key, HashAlgorithm.SHA256), MAX_KEY_LENGTH);
}
public static byte[] decryptRaw(byte[] cipherBytes, byte[] keyBytes) throws Exception {
byte[] IV = Arrays.copyOfRange(cipherBytes, cipherBytes.length - MAX_IV_LENGTH, cipherBytes.length);
byte[] RealBytes = Arrays.copyOf(cipherBytes, cipherBytes.length - MAX_IV_LENGTH);
Cipher AesAlgorithm = Cipher.getInstance("AES/CBC/PKCS5Padding" );
byte[] ValidKeyBytes = generateValidKey(keyBytes);
SecretKeySpec secretKeySpec = new SecretKeySpec(ValidKeyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(IV);
AesAlgorithm.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] Decrypted = AesAlgorithm.doFinal(RealBytes);
return Decrypted;
}
public static byte[] encryptRaw(byte[] plainBytes, byte[] keyBytes) throws Exception {
Cipher AesAlgorithm = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] ValidKeyBytes = generateValidKey(keyBytes);
SecretKeySpec secretKeySpec = new SecretKeySpec(ValidKeyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(generateIV());
AesAlgorithm.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] Encrypted = AesAlgorithm.doFinal(plainBytes);
byte[] ret = new byte[Encrypted.length + MAX_IV_LENGTH];
System.arraycopy(Encrypted, 0, ret, 0, Encrypted.length);
System.arraycopy(ivParameterSpec.getIV(), 0, ret, Encrypted.length, MAX_IV_LENGTH);
return ret;
}
private static byte[] generateIV() throws Exception {
return generateRandomArray(MAX_IV_LENGTH);
}
public static byte[] generateRandomArray(int size) {
SecureRandom RandomGenerator = new SecureRandom();
byte[] ret = new byte[size];
RandomGenerator.nextBytes(ret);
return ret;
}
public static String encryptToBase64(String plaintext, String key) throws Exception {
byte[] PlainBytes = plaintext.getBytes("UTF-8");
byte[] KeyBytes = key.getBytes("UTF-8");
return Base64Manager.toBase64(encryptRaw(PlainBytes, KeyBytes), false);
}
}
public class Program {
public static void main(String[] args) throws Exception
{
String plainText = "plain text";
String password = "password";
String encrypted = AesManager.encryptToBase64(plainText, password);
System.out.println(encrypted);
System.out.println(AesManager.decryptFromBase64(encrypted, password));
}
}
Output
Java
PS C:\Users\Megam\OneDrive\Documentos\NetBeansProjects\TestAes\target> java -cp .\TestAes-1.0-SNAPSHOT.jar com.mycompany.testaes.Program
leHBoUoB54bd9JEa9s9INohjVjmb2RZqVzrz2uYEstg=
plain text
PS C:\Users\Megam\OneDrive\Documentos\NetBeansProjects\TestAes\target> java -cp .\TestAes-1.0-SNAPSHOT.jar com.mycompany.testaes.Program
wC6o7cUqZn9KS88L11OuNR2lGDcJ6zBrK1etJEpMHEg=
plain text
PS C:\Users\Megam\OneDrive\Documentos\NetBeansProjects\TestAes\target> java -cp .\TestAes-1.0-SNAPSHOT.jar com.mycompany.testaes.Program
gfM1uVf2a3x6Q/N0XhpDI4svUZx4kTmcl0wkjJfNQrw=
plain text
PS C:\Users\Megam\OneDrive\Documentos\NetBeansProjects\TestAes\target>
Dotnet
PS C:\Users\Megam\source\repos\ConsoleApplication1\ConsoleAppCs\bin\Debug\net5.0> .\ConsoleAppCs.exe
4GTqXhjkh0tXNI6Q4hShSdPPrKcWVEqA5LRBn6fBCH8=
plain text
PS C:\Users\Megam\source\repos\ConsoleApplication1\ConsoleAppCs\bin\Debug\net5.0> .\ConsoleAppCs.exe
aYY1sxre9KfLqkA1XtvN4IwGau6mYInmuvkopFXuKQA=
plain text
PS C:\Users\Megam\source\repos\ConsoleApplication1\ConsoleAppCs\bin\Debug\net5.0> .\ConsoleAppCs.exe
7lesPYAHQxdiCN4Cv5yWyWJlf5pqIukznZzpZTOOFeg=
plain text
PS C:\Users\Megam\source\repos\ConsoleApplication1\ConsoleAppCs\bin\Debug\net5.0>
Related
I had issue when converting AES encryption from java to php, it show different value result in php, that should show the exact value from java
Java
public class AesUtil {
private final Cipher cipher;
public final static String passPhrase = "";
public final static String IV = ""; //32bit IV Lenght
public final static String SALT = "";
public final static int KEY_SIZE = 128;
public final static int ITERATION_COUNT = 10000;
public AesUtil() {
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw fail(e);
}
}
//Call this function to encrypt some String
public String encrypt(String passPhrase, String plaintext) {
try {
SecretKey key = generateKey(SALT, passPhrase);
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, IV, plaintext.getBytes("UTF-8"));
return base64(encrypted);
}
catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
cipher.init(encryptMode, key, new IvParameterSpec(hex(IV)));
return cipher.doFinal(bytes);
}
catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
throw fail(e);
}
}
private SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), ITERATION_COUNT, KEY_SIZE);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
}
catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw fail(e);
}
}
public static String random(int length) {
byte[] salt = new byte[length];
new SecureRandom().nextBytes(salt);
return hex(salt);
}
public static String base64(byte[] bytes) {
return new String(Base64.encodeBase64(bytes));
}
public static byte[] base64(String str) {
return Base64.decodeBase64(str.getBytes());
}
public static String hex(byte[] bytes) {
return Hex.encodeHexString(bytes);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
}
catch (DecoderException e) {
throw new IllegalStateException(e);
}
}
private IllegalStateException fail(Exception e) {
return new IllegalStateException(e);
}
}
this is my code in php version,
<?php
$password = "";
$salt = "";
$iv = ""; //32bit IV Lenght
$length = 16;
$salt = "";
$interation = 10000;
$key1 = mb_convert_encoding($password, "UTF-8");
$bytes = openssl_pbkdf2($key1, $salt, $length, $interation, "sha1");
$bytes2 = hash_pbkdf2("sha1", $password, $salt, $interation, $length, true);
$ciphertext_b64 = base64_encode(openssl_encrypt($plaintext,"aes-128-cbc", $bytes2,OPENSSL_RAW_DATA, $iv));
?>
Please tell me how to solve this problem, if you have any suggestion in different programming language it's no problem, thank you
I am generating a public and private key pair in javascript. The public key is being sent to the server where I have java code that encrypts the string. When I try to decrypt the string in Javascript using a private key I get an error. Here is the Javascript code I used.
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class CryptographyService {
private publicKey: string;
private privateKey: string;
public randomKey: string;
private keyPair: CryptoKeyPair;
public exportedKey: string;
constructor() {
/**Generate the public and private key */
this.generateKeys()
.then(() => {
return this.exportKey(this.keyPair.publicKey);
})
.then(() => {});
}
public setPublicKey(key: string) {
this.publicKey = key;
}
public setRandomKey(key: string) {
this.randomKey = key;
}
public setPrivateKey(key: string) {
this.privateKey = key;
}
public async encryptMessage(text: String) {
const { default: NodeRSA } = await import('node-rsa');
const key = NodeRSA(this.publicKey, 'pkcs8-public');
return key.encrypt(text, 'base64');
}
// For subscription key encryption and decryption
private async generateKeys() {
const keyPair = await crypto.subtle.generateKey(
{
name: 'RSA-OAEP', //algorithm
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256'
},
true,
['encrypt', 'decrypt']
);
this.keyPair = keyPair; /**{
publicKey:
privateKey:
} */
}
private async exportKey(key: CryptoKey) {
/** exportKey() it takes as input a CryptoKey object and gives you the key in an external, portable format.
* crypto.subtle.exportKey(format, key); : returns a promise
* spki format is used to import/export RSA or Elliptic Curve public keys.
*/
const exported = await crypto.subtle.exportKey('spki', key); //Returns an ArrayBuffer
const exportedAsString = this.ab2str(exported); // Converts ArrayBuffer to String
/**btoa encodes a string to base64 */
const exportedAsBase64 = window.btoa(exportedAsString);
this.exportedKey = exportedAsBase64;
}
// Uses private key to decrypt message sent from the backend
public async decryptMessage(input: string) {
const ciphertext = this.str2ab(input);
const decrypted = await window.crypto.subtle.decrypt(
{
name: 'RSA-OAEP'
},
this.keyPair.privateKey,
ciphertext
);
const dec = new TextDecoder();
const decodedMessage = dec.decode(decrypted);
return decodedMessage;
}
private ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
private str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
}
The java code to convert the string to PublicKey Object
String pubKey = loginRequest.publicKey;
PublicKey pk = null;
try {
byte[] keyBytes = Base64.decodeBase64(pubKey);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
pk = kf.generatePublic(spec);
} catch (Exception e) {
System.out.println("Exception in generating primary key: " + e.getMessage());
}
Encrypting the string with the publicKey
public static byte[] encrypt(String text, PublicKey key) {
byte[] cipherText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, key, oaepParams);
cipherText = cipher.doFinal(text.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return cipherText;
}
I get back an encrypted string when when I try to decode it in Javascript I get an error. Any idea what I need to change in my code to fix this?
I am trying to port my working python code for generating signature to java but they both are producing different signatures. Here is my code for python which is generating the signature.
import pdb
pdb.set_trace()
signer = PKCS1_v1_5.new(priv_key)
digest = SHA256.new()
digest.update(message)
val = signer.sign(digest)
return val
I am calling this function using this statement
signature = b64encode(sign(msg1, private))
Here the msg1 is
msg1 = 'test'.encode("utf8")
and private is the private key which I am importing using
RSA.importKey("<Location of key>"
I am trying to write a similar java code for implementing the same functionality with this given code
public static void main(String[] args) throws Exception {
String payload = "test";
String dig = makeDigest( payload, "SHA-256");
Key k = getPrivateKey("private_key.der");
String signature = encrypt(dig, "RSA", k);
System.out.print(signature);
}
public static String makeDigest(String payload, String digestAlgo) {
String strDigest = "";
try {
MessageDigest md = MessageDigest.getInstance(digestAlgo);
byte[] p_b = payload.getBytes("UTF-8");
md.update(p_b);
byte[] digest = md.digest();
char[] encoded = Hex.encodeHex(digest);
strDigest = new String(encoded);
System.out.println(strDigest);
}
catch (Exception ex) {
ex.printStackTrace();
}
return strDigest;
}
public static String encrypt(String sha, String encryptAlgo, Key k) {
String strEncrypted = "";
try {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(encryptAlgo,"BC");
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] encrypted = cipher.doFinal(sha.getBytes("UTF-8"));
//System.out.println(new String(encrypted));
byte[] encoded = Base64.getEncoder().encode(encrypted);
strEncrypted = new String(encoded);
}
catch (Exception ex) {
ex.printStackTrace();
}
return strEncrypted;
}
public static PrivateKey getPrivateKey(String filename)
throws Exception {
byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
Security.addProvider(new BouncyCastleProvider());
KeyFactory kf = KeyFactory.getInstance("RSA","BC");
return kf.generatePrivate(spec);
}
Both thee codes are generating different signatures. I feel something I am doing wrong in selecting the algorithm on Java side. I have tried with
RSA/NONE/PKCS1Padding
and
RSA/ECB/PKCS1Padding
but still, it is different signature as compared to python code.
Any lead on this will be very helpful.
I have a requirement in which I have to communicate with a
Web Service by sending three parameters that are encrypted: ID, key and initialization vector (IV). In order to do this, I need to use Triple DES (3DES/DESede) encryption as well as RSA encryption. The steps required to do this are:
Start with the ID in clear text. Decode the ID from a base64 string to an array of bytes. The original ID is not base64 encoded but this step must be performed.
Encrypt the base64 decoded ID using 3DES and generate the key and IV
Encrypt the key and IV generated with RSA (using a certificate provided)
Convert encrypted ID, encrypted key and IV to base64 strings.
I was provided with a C# example on how to do this and the code looks as displayed below:
namespace SampleEncryption
{
public class SampleWebServiceInvocation
{
private const string CERT_SUBJECT = "sample.cer";
public string GetReport()
{
ReportService.RService ws = new ReportService.RService();
ReportService.ReportRequest req = new ReportService.ReportRequest();
string key = string.Empty;
string iv = string.Empty;
string id = "1234567890123456";
string encrypt = new CryptEx().EncryptEx(id, CERT_SUBJECT, ref key, ref iv);
req.AccountNumber = encrypt;
req.Key = key;
req.InitializationVector = iv;
ReportService.ReportResponse resp = ws.getReport(req);
if (resp.Success)
return resp.RedirectUrl;
return string.Empty;
}
}
/// <summary>
/// CryptorEx
/// </summary>
public class CryptEx
{
private X509Certificate2 _certificate;
private byte[] _encryptedKey;
private byte[] _encryptedIV;
private byte[] _encryptedText;
private byte[] _decryptedKey;
private byte[] _decryptedIV;
private byte[] _decryptedText;
/// <summary>
/// Default (empty) constructor
/// </summary>
public CryptEx()
{
}
/// <summary>
/// Publicly exposed encrypt method
/// </summary>
public string EncryptEx(string decryptedText, string certSubject, ref string key, ref string iv)
{
string data;
_decryptedText = Convert.FromBase64String(decryptedText);
try
{
_certificate = GetSignerCert(certSubject);
_encryptedText = EncryptWithTripleDES();
EncryptWithRSA();
key = Convert.ToBase64String(_encryptedKey);
iv = Convert.ToBase64String(_encryptedIV);
data = Convert.ToBase64String(_encryptedText);
}
catch (Exception)
{
data = string.Empty;
}
return data;
}
private byte[] EncryptWithTripleDES()
{
//Create a crypto provider for the text
using (TripleDESCryptoServiceProvider tripDes = new TripleDESCryptoServiceProvider())
{
tripDes.Mode = CipherMode.CBC;
ICryptoTransform encryptor = tripDes.CreateEncryptor();
//Encrypt
using (MemoryStream ms = new MemoryStream())
{
CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
cs.Write(_decryptedText, 0, _decryptedText.Length);
cs.FlushFinalBlock();
cs.Close();
ms.Close();
_decryptedKey = tripDes.Key;
_decryptedIV = tripDes.IV;
return ms.ToArray();
}
}
}
private void EncryptWithRSA()
{
using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)_certificate.PublicKey.Key)
{
_encryptedKey = rsa.Encrypt(_decryptedKey, false);
_encryptedIV = rsa.Encrypt(_decryptedIV, false);
}
}
private static X509Certificate2 GetSignerCert(string certName)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
// First, find certificates that haven't expired
X509Certificate2Collection validCerts = store.Certificates.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection scorecardCerts = validCerts.Find(X509FindType.FindBySubjectName, certName, false);
if (scorecardCerts.Count == 0)
throw new ApplicationException(string.Format("Unable to find certificate with name of {0}", certName));
if (scorecardCerts.Count > 1)
throw new ApplicationException(string.Format("More than one certificate has a name of {0}\r\n{1}",
certName, GetSubjectNames(scorecardCerts)));
return scorecardCerts[0];
}
finally
{
store.Close();
}
}
private static string GetSubjectNames(X509Certificate2Collection signingCert)
{
StringBuilder sb = new StringBuilder();
foreach (X509Certificate2 cert in signingCert)
sb.AppendLine(cert.Subject);
return sb.ToString();
}
}
}
In my case, I need to do this but in Java, so here I expose the logic of my implementation.
For Triple DES:
public class DESedeCrypto implements SymmetricCryptoManager {
/**
* Constant for the algorithm being used for Triple DES (CBC)
*/
public static final String DESEDE_CBC_TRANSFORMATION_ALG = "DESede/CBC/NoPadding";
#Override
public DESedeEncryptionResult encrypt(byte[] srcData, String cryptoAlgorithm) throws Exception {
DESedeEncryptionResult result = null;
byte[] encryptedBytes = null;
byte[] initializationVector = null;
if (srcData == null || srcData.length == 0) {
throw new InvalidEncryptionTargetException();
}
KeyGenerator keyGen = KeyGenerator.getInstance("DESede");
SecretKey secretKey = keyGen.generateKey();
Cipher cipher = Cipher.getInstance(cryptoAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
result = new DESedeEncryptionResult();
if (cryptoAlgorithm.equals(DESEDE_CBC_TRANSFORMATION_ALG)) {
IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
initializationVector = spec.getIV();
result.setInitializationVector(initializationVector);
}
encryptedBytes = cipher.doFinal(srcData);
result.setResultBytes(encryptedBytes);
result.setKeyBytes(secretKey.getEncoded());
return result;
}
}
For RSA:
public class RSACrypto implements AsymmetricCryptoManager {
/**
* Tranformation algorithm for OAEP.
*/
private static final String OAEP_RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
/**
* Constructor of the class.
*/
public RSACrypto() {
}
#Override
public byte[] encryptWithCertificate(InputStream inputStream, byte[] targetBytes) throws Exception {
byte[] encryptedBytes = null;
if (targetBytes == null || targetBytes.length == 0) {
throw new InvalidEncryptionTargetException();
}
if (inputStream == null) {
throw new InvalidCertificatePathException("Resource specified for operation is not valid");
}
X509Certificate certificate = CryptoUtils.readCertificateFromInputStream(inputStream);
encryptedBytes = getEncryptedBytes(certificate, targetBytes);
return encryptedBytes;
}
private byte[] getEncryptedBytes(X509Certificate certificate, byte[] targetBytes) throws Exception {
byte[] encryptedBytes = null;
PublicKey publicKey = certificate.getPublicKey();
Cipher cipher = Cipher.getInstance(OAEP_RSA_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(targetBytes);
return encryptedBytes;
}
}
Here's how I read the certificate:
public static X509Certificate readCertificateFromInputStream(InputStream inputStream) throws Exception {
X509Certificate certificate = null;
CertificateFactory certFactory = CertificateFactory.getInstance(X509_CERT_TYPE);
certificate = (X509Certificate) certFactory.generateCertificate(inputStream);
return certificate;
}
And here is the logic of the whole encryption process:
String base64id = new String(CryptoUtils.encodeBase64(base64id.getBytes()));
DESedeEncryptionResult encryptionResult = desedeCrypto.encrypt(CryptoUtils.decodeBase64(base64id), DESedeCrypto.DESEDE_CBC_TRANSFORMATION_ALG);
byte[] rsaEncryptedKey = rsaCrypto.encryptWithCertificate(certForKeyInputStream, encryptionResult.getKeyBytes());
byte[] rsaEncryptedIv = rsaCrypto.encryptWithCertificate(certForIvInputStream, encryptionResult.getInitializationVector());
String encryptedId = CryptoUtils.getBase64EncodedStr(encryptionResult.getResultBytes());
String finalEncryptedKey = CryptoUtils.getBase64EncodedStr(rsaEncryptedKey);
String finalIv = CryptoUtils.getBase64EncodedStr(rsaEncryptedIv);
The ID used for testing is: 1234567890123456
When I send the results to the Web Service I receive a message that the encryption cannot be done. In order to make sure, I tested both implementations (Java and c#). C# generated values are working when I send them to the web service, but Java implementation is not working.
Do you know if there's some specific type of algorithm I'd need to add to the Java implementation, I mean in Triple DES we use DESede/CBC/NoPadding but maybe c# uses another. Same for RSA.
Maybe somebody can give a me clue or recommendation regarding this? Thanks in advance.
I'm implementing in Java a third party application, but some part of the application I'm get a encrypt string like this: eGlhV2xNbmdqSFBkbEhkZDNpZ3gwQT09
and have to decrypt.
The guy who code this application is no longer here, so I need some help make a decrypt code in Java.
this is the password :CB=Z8#P#0!N2/8$%3K-9C(5S9*FDH+0Z
public static class SystemCriptografia
{
#region Atributos
private static string chave = "CB=Z8#P#0!N2/8$%3K-9C(5S9*FDH+0Z";
private static SymmetricAlgorithm algoritmo = new RijndaelManaged();
#endregion
#region Métodos
#region Métodos privados
private static string base64Encode(string data)
{
byte[] encData_byte = new byte[data.Length];
encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
string encodedData = Convert.ToBase64String(encData_byte);
return encodedData;
}
private static string base64Decode(string data)
{
UTF8Encoding encoder = new UTF8Encoding();
Decoder utf8Decode = encoder.GetDecoder();
byte[] todecode_byte = Convert.FromBase64String(data);
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
char[] decoded_char = new char[charCount];
utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
string result = new String(decoded_char);
return result;
}
private static string Criptografa(string valor, string chave)
{
byte[] ByteValor = Encoding.UTF8.GetBytes(valor);
// Seta a chave privada
algoritmo.Mode = CipherMode.CBC;
algoritmo.Key = Encoding.Default.GetBytes(chave);
algoritmo.IV = Encoding.Default.GetBytes("brasilshopsoft07");
// Interface de criptografia / Cria objeto de criptografia
ICryptoTransform cryptoTransform = algoritmo.CreateEncryptor();
MemoryStream _memoryStream = new MemoryStream();
CryptoStream _cryptoStream = new CryptoStream(_memoryStream, cryptoTransform, CryptoStreamMode.Write);
// Grava os dados criptografados no MemoryStream
_cryptoStream.Write(ByteValor, 0, ByteValor.Length);
_cryptoStream.FlushFinalBlock();
// Busca o tamanho dos bytes encriptados
byte[] cryptoByte = _memoryStream.ToArray();
// Converte para a base 64 string para uso posterior em um xml
return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0));
}
private static string Descriptografa(string valor, string chave)
{
// Converte a base 64 string em num array de bytes
byte[] cryptoByte = Convert.FromBase64String(valor);
// Seta a chave privada
algoritmo.Mode = CipherMode.CBC;
algoritmo.Key = Encoding.Default.GetBytes(chave);
algoritmo.IV = Encoding.Default.GetBytes("brasilshopsoft07");
// Interface de criptografia / Cria objeto de descriptografia
ICryptoTransform cryptoTransform = algoritmo.CreateDecryptor();
MemoryStream _memoryStream = new MemoryStream(cryptoByte, 0, cryptoByte.Length);
CryptoStream _cryptoStream = new CryptoStream(_memoryStream, cryptoTransform, CryptoStreamMode.Read);
// Busca resultado do CryptoStream
StreamReader _streamReader = new StreamReader(_cryptoStream);
return _streamReader.ReadToEnd();
}
#endregion
public static string ToCriptografa(this string valor)
{
return Criptografa(valor, chave);
}
public static string ToDescriptografa(this string valor)
{
return Descriptografa(valor, chave);
}
public static string ToCriptografaQueryString(this string valor)
{
return base64Encode(Criptografa(valor, chave));
}
public static string ToDescriptografaQueryString(this string valor)
{
return Descriptografa(base64Decode(valor), chave);
}
#endregion
}
and this is the java code that I'm trying to do :
public class Criptografia {
private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
private static final int KEY_SIZE = 256;
public static void main(final String[] args) throws Exception {
System.out.println(decryptAuthorizationString(
"eGlhV2xNbmdqSFBkbEhkZDNpZ3gwQT09", "CB=Z8#P#0!N2/8$%3K-9C(5S9*FDH+0Z"));
}
private static String decryptAuthorizationString(final String authString,
final String password) {
try {
// --- check if AES-256 is available
if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
throw new IllegalStateException("Unlimited crypto files not present in this JRE");
}
// --- create cipher
final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
// --- create the key and initial vector bytes
final byte[] passwordEncoded = password.getBytes(UTF_16LE);
final byte[] keyData = Arrays.copyOf(passwordEncoded, KEY_SIZE
/ Byte.SIZE);
final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());
// --- init cipher
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"),
new IvParameterSpec(ivBytes));
// --- decode & decrypt authentication string
final byte[] authBytes = Base64.decode(authString);
final byte[] decryptedData = cipher.doFinal(authBytes);
// WARNING: may still decrypt to wrong string if
// authString or password are incorrect -
// BadPaddingException may *not* be thrown
return new String(decryptedData, UTF_16LE);
} catch (BadPaddingException | IllegalBlockSizeException e) {
// failure to authenticate
return null;
} catch (final GeneralSecurityException e) {
throw new IllegalStateException(
"Algorithms or unlimited crypto files not available", e);
}
}
}
The issues of your code:
Wrong character set chosen. If Encoding.Default is UTF-8 in C#, then the "password" and IV encodings must also be "UTF-8" in Java.
The IV is not derived from the key, but also a fixed value.
The ciphertext is actually doubly Base64 encoded. I guess somebody took "two is better than one" too literal.
Full code:
private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
private static final int KEY_SIZE = 256;
private static String UTF_8 = "UTF-8";
public static void main(final String[] args) throws Exception {
System.out.println(decryptAuthorizationString(
"eGlhV2xNbmdqSFBkbEhkZDNpZ3gwQT09", "CB=Z8#P#0!N2/8$%3K-9C(5S9*FDH+0Z"));
}
private static String decryptAuthorizationString(final String authString,
final String password) throws UnsupportedEncodingException {
try {
// --- check if AES-256 is available
if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
throw new IllegalStateException("Unlimited crypto files not present in this JRE");
}
// --- create cipher
final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
// --- create the key and initial vector bytes
final byte[] passwordEncoded = password.getBytes(UTF_8);
final byte[] ivBytes = "brasilshopsoft07".getBytes(UTF_8);
// --- init cipher
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(passwordEncoded, "AES"),
new IvParameterSpec(ivBytes));
// --- decode & decrypt authentication string
final byte[] authBytes = Base64.decode(authString);
final byte[] authBytes2 = Base64.decode(authBytes);
final byte[] decryptedData = cipher.doFinal(authBytes2);
// WARNING: may still decrypt to wrong string if
// authString or password are incorrect -
// BadPaddingException may *not* be thrown
return new String(decryptedData, UTF_8);
} catch (BadPaddingException | IllegalBlockSizeException e) {
// failure to authenticate
return null;
} catch (final GeneralSecurityException e) {
throw new IllegalStateException(
"Algorithms or unlimited crypto files not available", e);
}
}
Output:
1
Notes:
Using a fixed IV is insecure. The IV must be chosen randomly in order reach semantic security. It doesn't have to be secret, so it can be sent along with the ciphertext. A common way is to prepend it to the ciphertext and slice it off before decryption.
Lose the second Base64 encoding. It just takes away space, but doesn't provide any useful feature.
Always use a specific encoding. Encoding.Default is nice for testing, but it may change depending on the machine it's running on and you will lose compatibility between multiple clients/servers.
Authenticate ciphertexts! If you don't add a message authentication code or use an authenticated mode like GCM or EAX, then an attacker may manipulate the ciphertext and you will not be able to determine this. This can go so far as to completely recover the plaintext of specific encrypted messages (padding oracle attack).