I need help figuring out why Java encryption fails on my machine running Linux Mint 17.2 Rafaela. My application is not able to decrypt previously encrypted values using the RC4 algorithm.
I am testing with Java 8 u112 and I installed the JCE, but that did not help.
Here is the minimum sample I created, which works on my Windows machine:
import javax.xml.bind.DatatypeConverter;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class CryptoTest
{
private static final String ADMIN_PASSWORD = "admin";
private static final String ADMIN_ENCRYPTED_PASSWORD = "532C05C5B5"; // RC4 encrypted password using KEY
private static final String ADMIN_AUTH_KEY = "1391a8a860b7d6e2e86df513700e490c16dae47cdae227ca"; // PBKDF2(username,password,salt)
private static final String CRYPTO_ALGORITHM = "RC4";
protected static String encryptPassword(String passwordDataToEncrypt, String userskey) throws Exception
{
SecureRandom sr = new SecureRandom(userskey.getBytes());
KeyGenerator kg = KeyGenerator.getInstance(CRYPTO_ALGORITHM);
kg.init(sr);
SecretKey sk = kg.generateKey();
Cipher cipher = Cipher.getInstance(CRYPTO_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, sk);
return bytesToHex(cipher.doFinal(passwordDataToEncrypt.getBytes()));
}
private static String bytesToHex(byte[] in)
{
return DatatypeConverter.printHexBinary(in);
}
private static byte[] hexStringToByteArray(String s)
{
return DatatypeConverter.parseHexBinary(s);
}
protected static String decryptPassword(byte[] toDecryptPassword, String key) throws Exception
{
SecureRandom sr = new SecureRandom(key.getBytes());
KeyGenerator kg = KeyGenerator.getInstance(CRYPTO_ALGORITHM);
kg.init(sr);
SecretKey sk = kg.generateKey();
Cipher cipher = Cipher.getInstance(CRYPTO_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, sk);
return new String(cipher.doFinal(toDecryptPassword));
}
public static void assertEquals(String arg1, String arg2)
{
if (! arg1.equals(arg2))
{
System.out.println(String.format("%s does not equal %s", arg1, arg2));
}
}
public static void testGetDecryptedPassword() throws Exception
{
String decryptedPassword = decryptPassword(hexStringToByteArray(ADMIN_ENCRYPTED_PASSWORD), ADMIN_AUTH_KEY);
assertEquals(ADMIN_PASSWORD, decryptedPassword);
}
public static void testGetEncryptedPassword() throws Exception
{
String encryptedPassword = encryptPassword(ADMIN_PASSWORD, ADMIN_AUTH_KEY);
assertEquals(ADMIN_ENCRYPTED_PASSWORD, encryptedPassword);
}
public static void testEncryptAndDecryptPasswords() throws Exception
{
String originalPassword = "password";
String encryptedPassword = encryptPassword(originalPassword, ADMIN_AUTH_KEY);
String decryptedPassword = decryptPassword(hexStringToByteArray(encryptedPassword), ADMIN_AUTH_KEY);
assertEquals(originalPassword, decryptedPassword);
originalPassword = "This is a STRONG password 4 me!!!##$^";
encryptedPassword = encryptPassword(originalPassword, ADMIN_AUTH_KEY);
decryptedPassword = decryptPassword(hexStringToByteArray(encryptedPassword), ADMIN_AUTH_KEY);
assertEquals(originalPassword, decryptedPassword);
}
public static void main(final String[] args)
{
try
{
int strength = Cipher.getMaxAllowedKeyLength("AES");
if ( strength > 128 ){
System.out.printf("isUnlimitedSupported=TRUE,strength: %d%n",strength);
} else {
System.out.printf("isUnlimitedSupported=FALSE,strength: %d%n",strength);
}
testGetDecryptedPassword();
testGetEncryptedPassword();
testEncryptAndDecryptPasswords();
}
catch (Exception e)
{
System.out.printf("Caught exception: %s\n", e.getMessage());
e.printStackTrace(System.out);
}
}
}
The output on my linux box is:
isUnlimitedSupported=TRUE,strength: 2147483647
admin does not equal <junk>
532C05C5B5 does not equal 5D16D89D2F
password does not equal <junk>
This is a STRONG password 4 me!!!##$^ does not equal <junk>
Where <junk> is a bunch of unprintable chars.
Your code assumes that you get the same SecretKey each time you initialize your SecureRandom() with the same password in the following code:
SecureRandom sr = new SecureRandom(userskey.getBytes());
KeyGenerator kg = KeyGenerator.getInstance(CRYPTO_ALGORITHM);
kg.init(sr);
SecretKey sk = kg.generateKey();
You really can't make that assumption, and you should not use that approach. The SecureRandom is part of the JCA architecture where the actual implementation you get when you instantiates a new SecureRandom(..) depends on which security providers are available on your system, and which priority each provider have.
If you need to make encryption keys from passwords, you should use a key derivation function invented for that like PBKDF2.
Related
I'm new in Java and want to sign some data in c# and verify in Java with RSA and SHA512.
C#:
static string SignData(string message, RSAParameters privateKey)
{
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] originalData = encoder.GetBytes(message);
rsa.ImportParameters(privateKey);
signedBytes = rsa.SignData(originalData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
rsa.PersistKeyInCsp = false;
}
return Convert.ToBase64String(signedBytes);
}
Java:
static boolean verifySignature512(String message, String sign, PublicKey publicKey) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] contentDigest = digest.digest(message.getBytes(Charset.forName("UTF-8")));
Signature signature = Signature.getInstance("Sha512withRSA");
signature.initVerify(publicKey);
signature.update(contentDigest);
return signature.verify(Base64.getDecoder().decode(sign));
}
I used RSA and public and private keys. Java function always return false with no error or exception.
If I remove MessageDigest like below it starts working, Is it ok and secure?
static boolean verifySignature512(String message, String sign, PublicKey publicKey) throws Exception {
// MessageDigest digest = MessageDigest.getInstance("SHA-512");
// byte[] contentDigest = digest.digest(message.getBytes(Charset.forName("UTF-8")));
Signature signature = Signature.getInstance("Sha512withRSA");
signature.initVerify(publicKey);
signature.update(message.getBytes(Charset.forName("UTF-8")));
return signature.verify(Base64.getDecoder().decode(sign));
}
The signature verification on Java-side has to fail as you are using different hashing algorithms on both sides.
In C# you are using SHA1 ('HashAlgorithmName.SHA1') and the Java-part is using SHA512 ('Signature signature = Signature.getInstance("Sha512withRSA");').
The following code is using SHA1 as hash algorithm but you can easily change this (on all codelines :-) to SHA256 or SHA512.
The C#-code verifies the signature with the public key, the same public key (encoded as PEM) is used in the Java-code
for verification only.
Security warning: My example codes are using UNSECURE 512-bit RSA-keys that should not used in production.
There is not proper exception handling and you are using the padding 'RSASignaturePadding.Pkcs1' which should
NOT used anymore as well.
This is the output of my C#-code:
Should I use MessageDigest to verify a digital signature that signed in C#?
signedData: mU2bcCMEhG13xG9sKwhaA//dnw2+rbLkwz2737cNU5kb2EBenJIEJ+bA596XccCVKUKPanxMUFoVw2fl8HhCNw==
The data was verified.
That's the Java-output:
RSA instance: SHA1withRSA
The data was verified.
C#-code:
using System;
using System.Security.Cryptography;
using System.Text;
class RSACSPSample {
static void Main() {
try {
Console.WriteLine("Should I use MessageDigest to verify a digital signature that signed in C#?");
// Create a UnicodeEncoder to convert between byte array and string.
ASCIIEncoding ByteConverter = new ASCIIEncoding();
string message = "this is the important message to sign";
// get private and public key ### SAMPLE and UNSECURE 512 bit RSA keypair
var privateKey = "<RSAKeyValue><Modulus>mfgthqgvK5P6kP00ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN47KA0ZQ==</Modulus><Exponent>AQAB</Exponent><P>8VCRao0hZmIv5gVGFLqOD/7n6TQKlekA96U1QVzimKM=</P><Q>o1bchWA5ddDd59FED37QcrakoTXNoxRspFtsLDKEp1c=</Q><DP>ugF0VUE7wYNlkFP4VPoHjuTZNbRbhHn5uOmrRxqlvyk=</DP><DQ>XoGggC9Hr8pUyo9DIPAP7X+Ny5TU0Vm87m/TK9Ni+2s=</DQ><InverseQ>YqOHEP8dgCvz5Q8nhpQgdrKfdlmjkNAFxKF7j3pm09I=</InverseQ><D>mCpGy/rxS08e5iXn26LRQvvm5UfyLKMNZWmAGk2QF8cRGFB7dds/SI9wGTC9xyOoF4N2kWzYdLx+dYbR9lqwbQ==</D></RSAKeyValue>";
var publicKey = "<RSAKeyValue><Modulus>mfgthqgvK5P6kP00ojzA68+tGMwjEacduojFSukazKPXrZ8Q5XjzfqgJmDQ3wcWe3hWK92O3z/tmAuN47KA0ZQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
// Create a new instance of the RSACryptoServiceProvider class
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(512);
RSAalg.PersistKeyInCsp = false;
RSAalg.FromXmlString(privateKey);
RSAParameters rsaParameters = RSAalg.ExportParameters(true);
String signedData = SignData(message, rsaParameters);
Console.WriteLine("signedData: " + signedData);
// verify with xml-public key
RSAalg.FromXmlString(publicKey);
rsaParameters = RSAalg.ExportParameters(false);
bool verifiedData = VerifyData(message, signedData, rsaParameters);
// Verify the data and display the result to the
// console.
if (VerifyData(message, signedData, rsaParameters)) {
Console.WriteLine("The data was verified.");
}
else {
Console.WriteLine("The data does not match the signature.");
}
}
catch(ArgumentNullException) {
Console.WriteLine("The data was not signed or verified");
}
}
static string SignData(string message, RSAParameters privateKey)
{
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] originalData = encoder.GetBytes(message);
rsa.ImportParameters(privateKey);
signedBytes = rsa.SignData(originalData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
rsa.PersistKeyInCsp = false;
}
return Convert.ToBase64String(signedBytes);
}
public static bool VerifyData(string message, string signedData, RSAParameters rsaParameters)
{
byte[] messageBytes;
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
try
{
var encoder = new UTF8Encoding();
messageBytes = encoder.GetBytes(message);
signedBytes = Convert.FromBase64String(signedData);
rsa.ImportParameters(rsaParameters);
return rsa.VerifyData(messageBytes, new SHA1CryptoServiceProvider(), signedBytes);
}
catch(CryptographicException e) {
Console.WriteLine(e.Message);
return false;
}
}
}
Java-code:
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class MainSha1 {
public static void main(String[] args) throws GeneralSecurityException {
System.out.println("Should I use MessageDigest to verify a digital signature that signed in C#?");
String message = "this is the important message to sign";
// this is a SAMPLE and UNSECURE RSA 512 bit key
String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJn4LYaoLyuT+pD9NKI8wOvPrRjMIxGn\n" +
"HbqIxUrpGsyj162fEOV4836oCZg0N8HFnt4Vivdjt8/7ZgLjeOygNGUCAwEAAQ==\n" +
"-----END PUBLIC KEY-----";
String signedData = "HS4qvrXpqu97me7yDt9lWXp+QLjKMO8FY4kiUiGhMhi6KmXQXCtmcUWSbg0i+LXv7u5ueRiQNeBnu6UCbPhZLg==";
String rsaInstanceString = "SHA1withRSA";
System.out.println("RSA instance: " + rsaInstanceString);
PublicKey publicKey = getPublicKeyFromString(publicKeyPem);
boolean verifyData = verifyRsa(publicKey, rsaInstanceString, message.getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(signedData));
if (verifyData = true) {
System.out.println("The data was verified.");
} else {
System.out.println("The data could NOT get verified.");
}
}
public static PublicKey getPublicKeyFromString(String key) throws GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
publicKeyPEM = publicKeyPEM.replaceAll("[\\r\\n]+", "");
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = (PublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
return pubKey;
}
public static Boolean verifyRsa(PublicKey publicKey, String rsaInstanceString, byte[] messageByte,
byte[] signatureByte) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
Signature publicSignature = Signature.getInstance(rsaInstanceString);
publicSignature.initVerify(publicKey);
publicSignature.update(messageByte);
return publicSignature.verify(signatureByte);
}
}
Im Trying to do my homework to create a class called Password that implements the Encryptable interface.
Im trying using RSA Algorythm.
I use some RSA code references from the Google and resulting my code below.
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Key;
import java.util.Arrays;
import javax.crypto.Cipher;
import java.util.Scanner;
public class Password
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
String password = sc.nextLine();
KeyPair keyPair = RSAKeyPair.keyPairRSA();
Key publicKey = keyPair.getPublic();
Key privateKey = keyPair.getPrivate();
System.out.println("Original: " + password);
byte[] encrypted = RSAEncryptDecrypt.encrypt(password, privateKey);
System.out.println("Encrypted: " + new String(encrypted));
byte[] decrypted = RSAEncryptDecrypt.decrypt(encrypted, publicKey);
System.out.println("Decrypted: " + new String(decrypted));
}
}
final class RSAConstants {
private RSAConstants() {
}
public static final String ALGORITHM = "RSA";
public static final int ALGORITHM_BITS = 2048;
}
class RSAKeyPair {
public static KeyPair keyPairRSA() {
KeyPairGenerator generator = null;
try {
generator = KeyPairGenerator.getInstance(RSAConstants.ALGORITHM);
} catch (Exception e) {
e.printStackTrace();
}
if (generator != null) {
generator.initialize(RSAConstants.ALGORITHM_BITS);
KeyPair keyPair = generator.genKeyPair();
return keyPair;
}
return null;
}
}
class RSAEncryptDecrypt {
public static byte[] encrypt(String original, Key privateKey) {
if (original != null && privateKey != null) {
byte[] bs = original.getBytes();
byte[] encData = convert(bs, privateKey, Cipher.ENCRYPT_MODE);
return encData;
}
return null;
}
public static byte[] decrypt(byte[] encrypted, Key publicKey) {
if (encrypted != null && publicKey != null) {
byte[] decData = convert(encrypted, publicKey, Cipher.DECRYPT_MODE);
return decData;
}
return null;
}
private static byte[] convert(byte[] data, Key key, int mode) {
try {
Cipher cipher = Cipher.getInstance(RSAConstants.ALGORITHM);
cipher.init(mode, key);
byte[] newData = cipher.doFinal(data);
return newData;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
My Input is:
InterstellarGalactica
All goes smooth except for the result of Encrypted Password
Resulting below
Original: InterstellarGalactica
Encrypted: Sªë/H?ù,X?U4??A???ìñáQ
÷? *?7*??d?'å?Ñ¡w °??? Pè???«{?D÷??cB???'É »???qªîÉDë??~hb??z8?çÿ?hí?{mè?{*îèGê??WÅ{x??ï.5¼?úü;e??G?-F?shèn?FI
áh`UƒIàB!?åäô+D<&"?)?????ß!??3ä?¬???â???<?¬Ü?{ #ó12B?òt?ƒòÆr²Ä·oHQ?ë?«ú?°?î??Äy?:X^<?
&:ryb\?¼
Decrypted: InterstellarGalactica
Why do it is became a meaningless character?
Is there anything wrong with my code?
Can you explain how to do it in proper way(if there is)?
Thanks in advance.
You are using RSA in the wrong way:
In RSA you use the public key for encryption and the private key for decryption.
You however use the private key for encryption and the public key for decryption:
byte[] encrypted = RSAEncryptDecrypt.encrypt(password, privateKey);
byte[] decrypted = RSAEncryptDecrypt.decrypt(encrypted, publicKey);
Additionally please never convert a byte[] that contains binary data to String. If you want to print binary data convert it for example to a hexadecimal or base64 String instead. Or if you want to print it as a number use BigInteger.
// output Base64 encoded
System.out.println(java.util.Base64.getEncoder().encode(encrypted));
// out hexadecimal (uses Apache commons codec library
System.out.println(org.apache.commons.codec.binary.Hex.encodeHexString(encrypted));
// out hexadecimal without external library)
System.out.println(new java.math.BigInteger(1, encrypted).toString(16))
// Output as large number (useful for manual RSA calculations)
System.out.println(new java.math.BigInteger(1, encrypted));
So usually i use one java file to encrypt and decrypt a string to hex with AES,
then my angular app want to consume api, that use the result of it.
this is my old java code
package decryptoor;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Formatter;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoAndroidKoplak {
private static final String TEXT_ENCODING = "UTF-8";
private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static final String ENCRYPTION_ALGORITM = "AES";
private static final String TAG = "Crypto";
private Cipher cipher;
private IvParameterSpec initialVector;
// private static void DEBUG(String msg){
// if(IDefines.DEBUG_LOG_TRACE){
// Log.i(TAG, msg);
// }
// }
public CryptoAndroidKoplak() {
try {
cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
initialVector = new IvParameterSpec(new byte[16]);
} catch (Exception e) {
System.out.println(e.toString());
}
}
public String encryptString(String plainText, String key) throws Exception {
return toHexString(encrypt(plainText, key)).toUpperCase();
}
public byte[] encrypt(String plainText, String key) throws Exception {
byte[] byteKey = getKeyBytes(key);
byte[] plainData = plainText.getBytes(TEXT_ENCODING);
SecretKeySpec keySpec = new SecretKeySpec(byteKey, ENCRYPTION_ALGORITM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, initialVector);
return cipher.doFinal(plainData);
}
public String decryptString(String encryptedText, String key) throws Exception {
return new String(decrypt(encryptedText, key));
}
public byte[] decrypt(String encryptedText, String key) throws Exception {
byte[] byteKey = getKeyBytes(key);
byte[] encryptData = hexToAscii(encryptedText);
SecretKeySpec keySpec = new SecretKeySpec(byteKey, ENCRYPTION_ALGORITM);
cipher.init(Cipher.DECRYPT_MODE, keySpec, initialVector);
return cipher.doFinal(encryptData);
}
public static String toMD5(String text) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] data = text.getBytes(TEXT_ENCODING);
return toHexString(md.digest(data));
}
public static String toSHA1(String text) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] data = text.getBytes(TEXT_ENCODING);
return toHexString(md.digest(data));
}
private static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : bytes) {
formatter.format("%02x", b);
}
return sb.toString();
}
private static byte[] hexToAscii(String hexStr) {
byte[] buff = new byte[hexStr.length() / 2];
int offset = 0;
for (int i = 0; i < hexStr.length(); i += 2) {
String str = hexStr.substring(i, i + 2);
buff[offset++] = (byte) Integer.parseInt(str, 16);
}
return buff;
}
private static byte[] getKeyBytes(String key) throws UnsupportedEncodingException {
byte[] keyBytes = new byte[16];
byte[] parameterKeyBytes = key.getBytes("UTF-8");
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}
}
and this is my code in angular
import { Injectable } from '#angular/core';
import * as CryptoJS from 'crypto-js';
#Injectable({
providedIn: 'root'
})
export class Encryption {
constructor() {}
encryptAesToString(stringToEncrypt: string, key: string): string {
// first way
// let encrypted;
// try {
// encrypted = CryptoJS.AES.encrypt(JSON.stringify(stringToEncrypt), key);
// } catch (e) {
// console.log(e);
// }
// encrypted = CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
// return encrypted;
// second way
// var b64 = CryptoJS.AES.encrypt(stringToEncrypt, key).toString();
// var e64 = CryptoJS.enc.Base64.parse(b64);
// var eHex = e64.toString(CryptoJS.enc.Hex);
// return eHex;
// third way
const key2 = CryptoJS.enc.Utf8.parse(key);
const iv = CryptoJS.enc.Utf8.parse(key);
const encrypted = CryptoJS.AES.encrypt(stringToEncrypt, key2, {
keySize: 16,
iv: iv,
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
let eHex = CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
return encrypted;
}
decryptAesformString(stringToDecrypt: string, key: string): string {
let decrypted: string = '';
try {
const bytes = CryptoJS.AES.decrypt(stringToDecrypt, key);
if (bytes.toString()) {
decrypted = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
}
} catch (e) {
console.log(e);
}
return decrypted;
}
}
i have try three code, the first one doesn't return hex, so i try 2 more ways but it doesn't show same encrypted string with the old java code so i cant consume the api.
any idea why this happen?
if you have better way to encrypt and decrypt with key that more simple both in angular and java, it will really help.
many thanks
after give up on how to make it same with my old java code, finally i try to make a new one hehe...
so after i read this answer, then i understand CryptoJS (library that i use in angular) implements the same key derivation function as OpenSSL. so i choose to use basic CryptoJS function to encrypt my string like this
var text = "The quick brown fox jumps over the lazy dog. 👻 👻";
var secret = "René Über";
var encrypted = CryptoJS.AES.encrypt(text, secret);
encrypted = encrypted.toString();
console.log("Cipher text: " + encrypted);
after that, what i need to do is make new java file to encrypt and decrypt aes OpenSsl, and i get what i need here in this answer. i use robert answer, cause accepted answer not really give me what i need.
but like the first answer mentioned, to encrypt and decrypt in this way, we have to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy. Otherwise, AES with key size of 256 won't work and throw an exception:(you won't need JCE with up-to-date java version)
so i add some functionality to force using AES with key size of 256 without to install JCE here. note to use this, actually isnt recomended, please read the comment in ericson answer
then this is my final code to encrypt and decrypt like OpenSsl
package decryptoor;
import groovy.transform.CompileStatic;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URLEncoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.SecureRandom;
import static java.nio.charset.StandardCharsets.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
/**
* Mimics the OpenSSL AES Cipher options for encrypting and decrypting messages using a shared key (aka password) with symetric ciphers.
*/
#CompileStatic
class OpenSslAes {
/** OpenSSL's magic initial bytes. */
private static final String SALTED_STR = "Salted__";
private static final byte[] SALTED_MAGIC = SALTED_STR.getBytes(US_ASCII);
static String encryptAndURLEncode(String password, String clearText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException, UnsupportedEncodingException {
String encrypted = encrypt(password, clearText);
return URLEncoder.encode(encrypted, UTF_8.name() );
}
/**
*
* #param password The password / key to encrypt with.
* #param data The data to encrypt
* #return A base64 encoded string containing the encrypted data.
*/
static String encrypt(String password, String clearText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException {
removeCryptographyRestrictions();
final byte[] pass = password.getBytes(US_ASCII);
final byte[] salt = (new SecureRandom()).generateSeed(8);
final byte[] inBytes = clearText.getBytes(UTF_8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] data = cipher.doFinal(inBytes);
data = array_concat(array_concat(SALTED_MAGIC, salt), data);
return Base64.getEncoder().encodeToString( data );
}
/**
* #see http://stackoverflow.com/questions/32508961/java-equivalent-of-an-openssl-aes-cbc-encryption for what looks like a useful answer. The not-yet-commons-ssl also has an implementation
* #param password
* #param source The encrypted data
* #return
*/
static String decrypt(String password, String source) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
removeCryptographyRestrictions();
final byte[] pass = password.getBytes(US_ASCII);
final byte[] inBytes = Base64.getDecoder().decode(source);
final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, SALTED_MAGIC.length);
if (!Arrays.equals(shouldBeMagic, SALTED_MAGIC)) {
throw new IllegalArgumentException("Initial bytes from input do not match OpenSSL SALTED_MAGIC salt value.");
}
final byte[] salt = Arrays.copyOfRange(inBytes, SALTED_MAGIC.length, SALTED_MAGIC.length + 8);
final byte[] passAndSalt = array_concat(pass, salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < 48; i++) {
final byte[] hashData = array_concat(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("MD5");
hash = md.digest(hashData);
keyAndIv = array_concat(keyAndIv, hash);
}
final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
return new String(clear, UTF_8);
}
private static byte[] array_concat(final byte[] a, final byte[] b) {
final byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
private static void removeCryptographyRestrictions() {
if (!isRestrictedCryptography()) {
return;
}
try {
/*
* Do the following, but with reflection to bypass access checks:
*
* JceSecurity.isRestricted = false; JceSecurity.defaultPolicy.perms.clear();
* JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
*/
final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
isRestrictedField.setAccessible(true);
setFinalStatic(isRestrictedField, true);
isRestrictedField.set(null, false);
final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
defaultPolicyField.setAccessible(true);
final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
final Field perms = cryptoPermissions.getDeclaredField("perms");
perms.setAccessible(true);
((Map<?, ?>) perms.get(defaultPolicy)).clear();
final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
instance.setAccessible(true);
defaultPolicy.add((Permission) instance.get(null));
}
catch (final Exception e) {
e.printStackTrace();
}
}
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
private static boolean isRestrictedCryptography() {
// This simply matches the Oracle JRE, but not OpenJDK.
return "Java(TM) SE Runtime Environment".equals(System.getProperty("java.runtime.name"));
}
}
I'm writing an app which got a lot of security constraints:
It needs to store files securely encrypted, and must be able to decrypt them. Also, an Operator needs to be able to decrypt the file without the app.
To archive this, I generated a KeyPair on my PC, put the public part in my app, generate an AES SecretKey Key inside the app, encrypt and save it with my public key (for operator purposes), and put the unencrypted key in AndroidKeyStore.
To Encrypt a message, I receive the SecretKey from KeyStore, encrypt my message, get the IV I used as well as the encryptedSecretKey, and write them in a defined order to a byte array (iv->encryptedSecretKey->encryptedMessage).
To Decrypt I try the same in reverse: get the byte array, read the iv and encryptedSecretKey, and pass the rest (encryptedMessage) to my cypher to decrypt.
The problem is, that cipher.doFinal(encryptedMessage) is throwing an
javax.crypto.AEADBadTagExceptionwhich is caused by android.security.KeyStoreException: Signature/MAC verification failed.
I already checked that the encrypted message and the one I want to decrypt are exactly the same. I'm having no idea what I am doing wrong.
The class I use is the following:
package my.company.domain;
import android.content.Context;
import android.content.SharedPreferences;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.support.annotation.NonNull;
import android.util.Base64;
import android.util.Log;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoHelper {
public static final String TAG = CryptoHelper.class.getSimpleName();
private static final String KEY_ALIAS = "OI1lTI1lLI1l0";
private static final char[] KEY_PASSWORD = "Il0VELI1lO".toCharArray();
private static final String PREF_NAME = "CryptoPrefs";
private static final String KEY_ENCRYPTED_SECRET = "encryptedSecret";
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private static final int IV_SIZE = 12;
private static final int IV_BIT_LEN = IV_SIZE * 8;
//generate 128 bit key (16), other possible values 192(24), 256(32)
private static final int AES_KEY_SIZE = 16;
private static final String AES = KeyProperties.KEY_ALGORITHM_AES;
private static final String AES_MODE = AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE;
private static final String RSA = KeyProperties.KEY_ALGORITHM_RSA;
private static final String RSA_MODE = KeyProperties.KEY_ALGORITHM_RSA + "/" + KeyProperties.BLOCK_MODE_ECB + "/" + KeyProperties.ENCRYPTION_PADDING_NONE;
private static final String RSA_PROVIDER = "AndroidOpenSSL";
private final Context mContext;
private final SharedPreferences mPrefs;
private SecureRandom mSecureRandom;
private KeyStore mAndroidKeyStore;
private PublicKey mPublicKey;
private byte[] mEncryptedSecretKey;
public CryptoHelper(Context context) {
mContext = context;
mSecureRandom = new SecureRandom();
mPrefs = mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
try {
mAndroidKeyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
mAndroidKeyStore.load(null);
} catch (KeyStoreException e) {
Log.wtf(TAG, "Could not get AndroidKeyStore!", e);
} catch (Exception e) {
Log.wtf(TAG, "Could not load AndroidKeyStore!", e);
}
}
public void reset() throws KeyStoreException {
mAndroidKeyStore.deleteEntry(KEY_ALIAS);
}
public byte[] encrypt(byte[] message){
SecretKey secretKey = getSecretKey();
try {
Cipher cipher = Cipher.getInstance(AES_MODE);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey());
byte[] cryptedBytes = cipher.doFinal(message);
byte[] iv = cipher.getIV();
byte[] encryptedSecretKey = getEncryptedSecretKey();
ByteBuffer buffer = ByteBuffer.allocate(IV_BIT_LEN + encryptedSecretKey.length + cryptedBytes.length);
buffer
.put(iv)
.put(encryptedSecretKey)
.put(cryptedBytes);
return buffer.array();
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}
public byte[] encrypt(String message){
return encrypt(message.getBytes(StandardCharsets.UTF_8));
}
public byte[] decrypt(byte[] bytes){
ByteBuffer buffer = ByteBuffer.wrap(bytes);
byte[] iv = new byte[IV_SIZE];
buffer.get(iv);
byte[] unused = getEncryptedSecretKey();
buffer.get(unused);
byte[] encryptedMessage = new byte[bytes.length - IV_SIZE - unused.length];
buffer.get(encryptedMessage);
try {
Cipher cipher = Cipher.getInstance(AES_MODE);
GCMParameterSpec parameterSpec = new GCMParameterSpec(IV_BIT_LEN, iv);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), parameterSpec);
byte[] decryptedMessage = cipher.doFinal(encryptedMessage);
return decryptedMessage;
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}
public String decryptToString(byte[] bytes){
return new String(decrypt(bytes), StandardCharsets.UTF_8);
}
public byte[] decrypt(FileInputStream fileToDecrypt){
byte[] buffer = null;
try {
buffer = new byte[fileToDecrypt.available()];
fileToDecrypt.read(buffer);
buffer = decrypt(buffer);
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
public PublicKey getPublicKey() {
if (null == mPublicKey) {
mPublicKey = readPublicKey();
}
return mPublicKey;
}
public byte[] getEncryptedSecretKey() {
if (null == mEncryptedSecretKey){
mEncryptedSecretKey = Base64.decode(mPrefs.getString(KEY_ENCRYPTED_SECRET, null), Base64.NO_WRAP);
}
return mEncryptedSecretKey;
}
private void saveEncryptedSecretKey(byte[] encryptedSecretKey){
String base64EncryptedKey = Base64.encodeToString(encryptedSecretKey, Base64.NO_WRAP);
mPrefs.edit().putString(KEY_ENCRYPTED_SECRET, base64EncryptedKey).apply();
}
protected SecretKey getSecretKey(){
SecretKey secretKey = null;
try {
if (!mAndroidKeyStore.containsAlias(KEY_ALIAS)){
generateAndStoreSecureKey();
}
secretKey = (SecretKey) mAndroidKeyStore.getKey(KEY_ALIAS, KEY_PASSWORD);
} catch (KeyStoreException e) {
Log.wtf(TAG, "Could not check AndroidKeyStore alias!", e);
secretKey = null;
} catch (GeneralSecurityException e) {
e.printStackTrace();
secretKey = null;
}
return secretKey;
}
private void generateAndStoreSecureKey()
throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, KeyStoreException, BadPaddingException, IllegalBlockSizeException {
SecretKey secretKey = generateSecureRandomKey();
PublicKey publicKey = getPublicKey();
Cipher keyCipher = Cipher.getInstance(RSA_MODE, RSA_PROVIDER);
keyCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedSecretKeyBytes = keyCipher.doFinal(secretKey.getEncoded());
saveEncryptedSecretKey(encryptedSecretKeyBytes);
KeyProtection keyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_VERIFY)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
mAndroidKeyStore.setEntry(KEY_ALIAS, new KeyStore.SecretKeyEntry(secretKey), keyProtection);
}
protected PublicKey readPublicKey() {
DataInputStream dis = null;
PublicKey key = null;
try {
dis = new DataInputStream(mContext.getResources().getAssets().open("public_key.der"));
byte[] keyBytes = new byte[dis.available()];
dis.readFully(keyBytes);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory facotory = KeyFactory.getInstance(RSA);
key = facotory.generatePublic(spec);
} catch (Exception e) {
key = null;
} finally {
if (null != dis) {
try {
dis.close();
} catch (IOException e) {
Log.wtf(TAG, "Cannot Close Stream!", e);
}
}
}
return key;
}
#NonNull
protected SecretKey generateSecureRandomKey() {
return new SecretKeySpec(generateSecureRandomBytes(AES_KEY_SIZE), AES);
}
#NonNull
protected byte[] generateSecureRandomBytes(int byteCount) {
byte[] keyBytes = new byte[byteCount];
mSecureRandom.nextBytes(keyBytes);
return keyBytes;
}
}
And I Test it like this:
#Test
public void testCrypto() throws Exception {
CryptoHelper crypto = new CryptoHelper(InstrumentationRegistry.getTargetContext());
crypto.reset();
String verySecretOpinion = "we're all doomed";
byte[] encrypt = crypto.encrypt(verySecretOpinion);
Assert.assertNotNull("Encrypted secret is Null!", encrypt);
Assert.assertFalse("encrypted Bytes are the same as Input!", new String(encrypt, StandardCharsets.UTF_8).equals(verySecretOpinion));
String decryptedString = crypto.decryptToString(encrypt);
Assert.assertNotNull("Decrypted String must be Non-Null!", decryptedString);
Assert.assertEquals("Decrypted String doesn't equal encryption input!", verySecretOpinion, decryptedString);
}
By the way minSdkVersion is 25, so higher than Marshmallow
UPDATE:
Fixed Cipher.DECRYPT_MODE to ENCRYPT_MODE on saving the SecretKey thx to James K Polk's comment
it works If I switch from BlockMode GCM to BlockMode CBC (and changing the GCMParameterSpec to IvParamterSpec but loose the verification of the GCM Mode.
There are at least two problems with the Operator interface. First, you RSA encrypt the secret key using the wrong Cipher mode: you used DECRYPT mode when you should have used encrypt. Secondly, you are using RSA without any padding. You need to use a real padding mode, one of the OEAP padding modes is recommended.
An error in the encryption side occurs when sizing the buffer used to hold the result:
ByteBuffer buffer = ByteBuffer.allocate(IV_BIT_LEN + encryptedSecretKey.length + cryptedBytes.length);
allocates too much space. IV_BIT_LEN should probably be changed to IV_SIZE to get the correctly sized ByteBuffer.
The last mistake is the failure to account for the GCM authentication tag length when setting the GCMParameterSpec on the decrypt side. You initialized the tag length in this line
GCMParameterSpec parameterSpec = new GCMParameterSpec(IV_BIT_LEN, iv);
but that's incorrect, the tag length is unrelated to the IV. Since you did not explicitly set the GCMParameterSpec on the encrypt side you got the default tag length, which happens to be 128.
You can retrieve the tag length on the encrypt side by calling
cipher.getParameters().getParameterSpec(GCMParameterSpec.class) to get the parameter spec. From this you can retrieve both the tag length and the iv. You should probably consider the tag length, 16 bytes = 128 bits, to be a hard-coded constant and not transmit it. The receiver should similar assume the tag length is 128 bits.
Thanx to evry one help me ..but still there is 2 problem after editing the code
import java.io.*;
import java.math.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class RCC4 {
public RCC4(){}
public static void main(String[] args)throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException{
String test = "testisperfect";
System.out.println(RCC4.keyGet());
byte b[] = RCC4.keyGet().getBytes();
byte plain[] = test.getBytes();
**byte c[] = RCC4.encrypt(plain,b);**
**byte p[] = RCC4.decrypt(c,b);**
**System.out.println(new String(c)) ;
System.out.println(new String(p));**
}
public static byte[] encrypt(byte[] plaintext,byte[] keyBytes)
{
byte[] e = null;
try
{
Key key = new SecretKeySpec(keyBytes,"RC4");
Cipher enCipher = Cipher.getInstance("RC4");
**enCipher.init(Cipher.ENCRYPT_MODE ,key);**
e = enCipher.doFinal(plaintext);
}
catch(Exception ex)
{
ex.printStackTrace();
}
return e;
}
public static byte[] decrypt(byte[] ciphertext,byte[] keyBytes)
{
byte de[] = null;
try
{
Key key = new SecretKeySpec(keyBytes,"RC4");
Cipher deCipher = Cipher.getInstance("RC4");
**deCipher.init(Cipher.DECRYPT_MODE, key);**
de = deCipher.doFinal(ciphertext);
}
catch(Exception e)
{
e.printStackTrace();
}
return de;
}
public static Key getKey()
{
Key key = null;
try
{
SecureRandom sr = new SecureRandom();
KeyGenerator kg = KeyGenerator.getInstance("RC4");
kg.init(128,sr);
key = kg.generateKey();
}catch(Exception e)
{
e.printStackTrace();
}
return key;
}
public static String keyGet()
{
Key k = RCC4.getKey();
byte[] b = k.getEncoded();
BigInteger big = new BigInteger(b);
String s = big.toString();
return s;
}
}
When I press "Build file" it says process completed but at running file a message says
112670544188765215715791498302542646231
java.security.InvalidKeyException: Illegal key size or default parameters
at RCC4.encrypt(RCC4.java:37)
at RCC4.main(RCC4.java:23)
java.security.InvalidKeyException: Illegal key size or default parameters
at RCC4.decrypt(RCC4.java:53)
at RCC4.main(RCC4.java:24)
Exception in thread "main" java.lang.NullPointerException
at java.lang.String.<init>(String.java:479)
at RCC4.main(RCC4.java:26)
Process completed.
These Lines are indicated as *
Answer to original question:
Key key = new SecretKeySpec(byte[]keyBytes,RC4);
should be
Key key = new SecretKeySpec(keyBytes, "RC4");
Also,
deCipher.init(Cipher.WHATEVER, keyBytes);
should be
deCipher.init(Cipher.WHATEVER, key);
Then it compiles, however there's still some issue with the application logic. That's up to you again :).
Answer to new question:
The problem was the unneeded usage of SecretKeySpec. Somewhere between getKey(), keyGet() all the byte[] games and SecretKeySpec it got wrong somehow. I didn't have the patience to track it, so I just deleted it and made the code somewhat more readable to make sure I didn't miss anything. I think you will still understand it since it still is basically your code and it is much simpler now.
import java.security.*;
import javax.crypto.*;
public class RCC4 {
public static void main(String[] args) throws Exception {
String plain = "testisperfect";
Key key = RCC4.getKey();
String encrypted = RCC4.encrypt(plain, key);
String decrypted = RCC4.decrypt(encrypted, key);
System.out.println(encrypted);
System.out.println(decrypted);
}
private static String rc4(String plaintext, int mode, Key key) throws Exception {
Cipher cipher = Cipher.getInstance("RC4");
cipher.init(mode, key);
return new String(cipher.doFinal(plaintext.getBytes()));
}
public static String encrypt(String plaintext, Key key) throws Exception {
return rc4(plaintext, Cipher.ENCRYPT_MODE, key);
}
public static String decrypt(String ciphertext, Key key) throws Exception {
return rc4(ciphertext, Cipher.DECRYPT_MODE, key);
}
public static Key getKey() throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("RC4");
SecureRandom sr = new SecureRandom();
kg.init(128, sr);
return kg.generateKey();
}
}
You need import SecretKeySpec which is under package javax.crypto.spec.
You're not calling the method correctly in that you're passing in the parameter type with the parameter. Parameter types are only shown when declaring a method, not when calling the method.
In other words, it's not
Key key = new SecretKeySpec(byte[] keyBytes, RC4);
it's instead
Key key = new SecretKeySpec(keyBytes, RC4);
You will of course need to have a keyBytes variable declared and initialized before trying to pass it into the parameter of this method.
package test;
import java.io.*;
import java.math.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class RCC4 {
public RCC4() {
}
public static void main(String[] args) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, IOException {
String test = "testisperfect";
System.out.println(RCC4.keyGet());
byte b[] = RCC4.keyGet().getBytes();
byte plain[] = test.getBytes();
byte c[] = RCC4.encrypt(plain, b);
byte p[] = RCC4.decrypt(c, b);
System.out.println(new String(c));
System.out.println(new String(p));
}
public static byte[] encrypt(byte[] plaintext, byte[] keyBytes) {
byte[] e = null;
try {
Key key = new SecretKeySpec(keyBytes, "RC4");
Cipher enCipher = Cipher.getInstance("RC4");
enCipher.init(Cipher.ENCRYPT_MODE, key);
e = enCipher.doFinal(plaintext);
} catch (Exception ex) {
ex.printStackTrace();
}
return e;
}
public static byte[] decrypt(byte[] ciphertext, byte[] keyBytes) {
byte de[] = null;
try {
Key key = new SecretKeySpec(keyBytes, "RC4");
Cipher deCipher = Cipher.getInstance("RC4");
deCipher.init(Cipher.DECRYPT_MODE, RCC4.getKey());
de = deCipher.doFinal(ciphertext);
} catch (Exception e) {
e.printStackTrace();
}
return de;
}
public static Key getKey() {
Key key = null;
try {
SecureRandom sr = new SecureRandom();
KeyGenerator kg = KeyGenerator.getInstance("RC4");
kg.init(128, sr);
key = kg.generateKey();
} catch (Exception e) {
e.printStackTrace();
}
return key;
}
public static String keyGet() {
Key k = RCC4.getKey();
byte[] b = k.getEncoded();
BigInteger big = new BigInteger(b);
String s = big.toString();
return s;
}
}