I would like to convert an OpenSSH ecdsa public key string(.pub file) to a BCECPublicKey instance.
What I want to achieve it the reverse of this code:
BCECPublicKey publicKey = ...;
byte[] point = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(publicKey.getEncoded())).getPublicKeyData().getOctets();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
dataOutputStream.writeInt("ecdsa-sha2-nistp256".getBytes().length);
dataOutputStream.write("ecdsa-sha2-nistp256".getBytes());
dataOutputStream.writeInt("nistp256".getBytes().length);
dataOutputStream.write("nistp256".getBytes());
dataOutputStream.writeInt(point.length);
dataOutputStream.write(point);
String base64 = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
This is what I've tried:
// Valid ecdsa-sha2-nistp256 public key string from a .pub file.
String base64 = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG93uDMAjwxpPFXgLFFs7FzWZXrQRaXnBMqmHaRN/5JRzljuqYAUAkW98HvFxGKrnb2JdW3X785AxLNzVhiiw+4=";
byte[] bytes = Base64.getDecoder().decode(base64);
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
// java.lang.IllegalArgumentException: Incorrect length for infinity encoding
ECPoint point = ecSpec.getCurve().decodePoint(bytes);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
PublicKey pk = keyFactory.generatePublic(publicKeySpec);
But this doesn't seem to work.
Is there an easy way to do this with bouncy castle?
You know you created the blob by concatenating six things, only the sixth of which was the actual point encoding, so how on Earth could you imagine that using all of the blob as a point encoding would be correct?
The clean and robust way is to parse the blob back into its pieces and extract the point encoding; the dirty way is to just assume the blob is, as expected, for ecdsa-sha2-nistp256 (and uncompressed) so the last 65 bytes are the point encoding:
String base64 = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG93uDMAjwxpPFXgLFFs7FzWZXrQRaXnBMqmHaRN/5JRzljuqYAUAkW98HvFxGKrnb2JdW3X785AxLNzVhiiw+4=";
byte[] bytes = Base64.getDecoder().decode(base64), temp;
if( clean ){
DataInputStream instr = new DataInputStream (new ByteArrayInputStream (bytes));
temp = new byte[instr.readInt()]; instr.read(temp);
if( !Arrays.equals(temp,"ecdsa-sha2-nistp256".getBytes())) throw new Exception ("bad key");
temp = new byte[instr.readInt()]; instr.read(temp);
if( !Arrays.equals(temp,"nistp256".getBytes())) throw new Exception ("bad key");
temp = new byte[instr.readInt()]; instr.read (temp);
}else{
temp = Arrays.copyOfRange(bytes, bytes.length-65, bytes.length);
}
ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec ("secp256r1");
org.bouncycastle.math.ec.ECPoint point = ecSpec.getCurve().decodePoint (temp);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
PublicKey pk = keyFactory.generatePublic(new org.bouncycastle.jce.spec.ECPublicKeySpec(point, ecSpec));
Related
I don't know why an error is coming up.
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
I understand that this error occurs when the incorrect key is used during the decryption. However, if you look at the test results result below, you can see that both C# and Java are the same (Key, IV, Salt is Base64 encoded).
C# Test Result
Java Test Result
It's the same!(Key, IV, Salt)
But the current BadpaddingException error is generated. What could be the problem?
I am attaching my source file.
C# (Encryption)
class AES {
private readonly static string keyStr = "This is Key";
private readonly static string vector = "This is Vector";
public static Rfc2898DeriveBytes MakeKey(string password){
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes);
Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(keyBytes, saltBytes, 65536);
return result;
}
public static Rfc2898DeriveBytes MakeVector(string vector){
byte[] vectorBytes = System.Text.Encoding.UTF8.GetBytes(vector);
byte[] saltBytes = SHA512.Create().ComputeHash(vectorBytes);
Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(vectorBytes, saltBytes, 65536);
return result;
}
public static void Encrypt(String inputFile, String outputFile) {
using (RijndaelManaged aes = new RijndaelManaged()){
//Create Key and Vector
Rfc2898DeriveBytes key = AES.MakeKey(AES.keyStr);
Rfc2898DeriveBytes vector = AES.MakeVector(AES.vector);
//AES256
aes.BlockSize = 128;
aes.KeySize = 256;
// It is equal in java
// Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key.GetBytes(32); //256bit key
aes.IV = vector.GetBytes(16); //128bit block size
//processing Encrypt
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] encrypted;
using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
byte[] inputBytes = File.ReadAllBytes(inputFile);
csEncrypt.Write(inputBytes, 0, inputBytes.Length);
}
encrypted = msEncrypt.ToArray();
}
string encodedString = Convert.ToBase64String(encrypted);
File.WriteAllText(outputFile, encodedString);
}
}
}
Java (Decryption)
public class AES256File {
private static final String algorithm = "AES";
private static final String blockNPadding = algorithm+"/CBC/PKCS5Padding";
private static final String password = "This is Key";
private static final String IV = "This is Vector";
private static IvParameterSpec ivSpec;
private static Key keySpec;
public static void MakeKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] keyBytes = password.getBytes("UTF-8");
// C# : byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes);
byte[] saltBytes = digest.digest(keyBytes);
//256bit
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 65536, 256);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
setKeySpec(secret);
}
public static void MakeVector(String IV) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] vectorBytes = IV.getBytes("UTF-8");
byte[] saltBytes = digest.digest(vectorBytes);
// 128bit
PBEKeySpec pbeKeySpec = new PBEKeySpec(IV.toCharArray(), saltBytes, 65536, 128);
Key secretIV = factory.generateSecret(pbeKeySpec);
byte[] iv = new byte[16];
System.arraycopy(secretIV.getEncoded(), 0, iv, 0, 16);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
setIvSpec(ivSpec);
}
public void decrypt(File source, File dest) throws Exception {
Cipher c = Cipher.getInstance(blockNPadding);
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
fileProcessing(source, dest, c);
}
public void fileProcessing(File source, File dest, Cipher c) throws Exception{
InputStream input = null;
OutputStream output = null;
try{
input = new BufferedInputStream(new FileInputStream(source));
output = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[input.available()];
int read = -1;
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
byte[] deryptedBytes = c.doFinal(buffer); // -----------------------> Error!! Showing!
byte[] decodedBytes = Base64.getDecoder().decode(deryptedBytes);
String decodeString = new String(decodedBytes, "UTF-8");
decodedBytes = decodeString.getBytes(StandardCharsets.UTF_8);
output.write(decodedBytes);
}finally{
if(output != null){
try{output.close();}catch(IOException e){}
}
if(input != null){
try{input.close();}catch(IOException e){}
}
}
}
I have verified as below.
Verification Key and IV in C#
//Key Verification
var salt = Convert.ToBase64String(saltBytes);
Console.Write("Salt Result : ");
Console.WriteLine(salt);
var result_test = Convert.ToBase64String(result.GetBytes(32));
Console.Write("Key Test Result: ");
Console.WriteLine(result_test);
//IV Verification (Salt is Using same code)
var result_test = Convert.ToBase64String(result.GetBytes(16));
Console.Write("IV Test Result: ");
Console.WriteLine(result_test);
Verification Key and IV in Java
//Key Verification
/* print Salt */
String base64 = Base64.getEncoder().encodeToString(saltBytes);
System.out.println("Salt Result : " + base64);
/* print Key */
String result_test = Base64.getEncoder().encodeToString(key);
System.out.println("Key Test Result : " + result_test);
/* print generated Key */
System.out.println("Secret Key Result : " + Base64.getEncoder().encodeToString(secret.getEncoded()));
//IV Verification (Salt is Using same code)
/* print IV */
String result_test = Base64.getEncoder().encodeToString(iv);
System.out.println("IV Test Result : " + result_test);
/* print generated IV */
System.out.println("IV Result : " + Base64.getEncoder().encodeToString(ivSpec.getIV()));
Updated
c# .netframework 4.5 / Java8 modified what #Topaco said and confirmed that it worked well.
I want to say thank you very much to #Topaco and #Gusto2, and I'm going to make changes to the parts that have been modified in security, just as #Gusto2 said!
1) In the C# Encrypt-method the plain text is encrypted first and then Base64-encoded. Thus, in the decryption process the data must be Base64-decoded first and then decrypted. Currently this is handled in the wrong order i.e. the data are decrypted first and then decoded. Therefore, in the Java fileProcessing-method replace
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
with
while((read = input.read(buffer)) != -1) {
byte[] bufferEncoded = buffer;
if (read != buffer.length) {
bufferEncoded = Arrays.copyOf(buffer, read);
}
byte[] bufferDecoded = Base64.getDecoder().decode(bufferEncoded);
output.write(c.update(bufferDecoded));
}
2) It's not necessary to pass buffer (or bufferDecoded) to the doFinal-method, since that was already done in the update-method. Thus,
byte[] deryptedBytes = c.doFinal(buffer);
must be replaced with
output.write(c.doFinal());
3) Since the Base64-decoding is already done in 1) in the try-block all lines following the doFinal-statement have to be removed. Overall, this results in
try {
input = new BufferedInputStream(new FileInputStream(source));
output = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[input.available()];
int read = -1;
while((read = input.read(buffer)) != -1) {
byte[] bufferEncoded = buffer;
if (read != buffer.length) {
bufferEncoded = Arrays.copyOf(buffer, read);
}
byte[] bufferDecoded = Base64.getDecoder().decode(bufferEncoded);
output.write(c.update(bufferDecoded));
}
output.write(c.doFinal());
}
4) The size of the buffer has to be a multiple of 4 in order to ensure a proper Base64-decoding. Thus, it's more reliable to replace
byte[] buffer = new byte[input.available()];
with
byte[] buffer = new byte[4 * (input.available() / 4)];
As long as the data are read in one chunk (which is not guaranteed, see e.g. https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html#available()) there is no problem. However, if the data are read in several chunks it's important to read a multiple of 4 bytes, otherwise the Base64-decoding will fail. That can be easily proved by using a buffer size which isn't a multiple of 4. This point must also be considered if the buffer size is explicitly defined with regard to larger files.
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
byte[] deryptedBytes = c.doFinal(buffer)
you are decrypting the input to a file, then you are using the same cipher instance to decrypt the the last read chunk (again) into a separate array not to the file
quick fix:
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
output.write(c.doFinal()); // write the padded block
if you want to create and print a decrypted String, you need to create a new Cipher instance (or maybe it will be enough to reinitialize the instance, I am not sure) assuming the buffer contains the whole input
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// assuming the buffer contains the whole input again
byte[] deryptedBytes = c.doFinal(buffer); // decrypting the whole file again
correct approach:
IV is used to securely reuse the same encryption key for multiple encryptions. So if your key is not random, you should generate new random IV for each encryption (and pass the IV along the ciphertext, most often prepended). Otherwise the encryption is not semantically secure and you may create opening for the two pad attack. So deriving IV from the key may not be very secure.
I advice to use any MAC (authentication code) passed along the ciphertext to ensure integrity (e.g. HMAC)
you are still reading all the file input fully into memory, what would not work for REALLY LARGE files. You may initialize the buffer to an arbitrary length (a few MB?) and process the input file as chunked
I'm trying to encrypt data in my C# client with public RSA key from the Java server. To do this I generated KeyPair in Java
KeyPairGenerator.java
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
Key pub = kp.getPublic();
Key pvt = kp.getPrivate();
String outFile = "rsa_key";
FileOutputStream outPvt = new FileOutputStream(outFile + ".key");
outPvt.write(pvt.getEncoded());
outPvt.close();
FileOutputStream outPub = new FileOutputStream(outFile + ".pub");
outPub.write(pub.getEncoded());
outPub.close();
It gave me two files (rsa_key.key and rsa_key.pub) and then I encoded public key with Base64 so C# could read it:
PublicKeyBase64.java
String keyFile2 = "rsa_key.pub";
Path path2 = Paths.get(keyFile2);
byte[] bytes2 = Files.readAllBytes(path2);
X509EncodedKeySpec ks2 = new X509EncodedKeySpec(bytes2);
KeyFactory kf2 = KeyFactory.getInstance("RSA");
PublicKey pub = kf2.generatePublic(ks2);
Base64.Encoder encoder = Base64.getEncoder();
String outFile = "en_rsa_key";
Writer out = new FileWriter(outFile + ".pub");
out.write(encoder.encodeToString(pub.getEncoded()));
out.close();
Then I created my C# class to encrypt data
Encrypter.cs
class Encrypter
{
private const string PATH = "..\\..\\key\\en_rsa_key.pub";
private string PublicKey;
public Encrypter()
{
PublicKey = File.ReadAllText(PATH);
Console.WriteLine(PublicKey.Length);
}
public string encryptData(string dataToEncrypt)
{
Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(PublicKey));
DerSequence publicKeySequence = (DerSequence)obj;
DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());
DerInteger modulus = (DerInteger)publicKey[0];
DerInteger exponent = (DerInteger)publicKey[1];
RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(parameters);
//Console.WriteLine(dataToEncrypt);
byte[] encryptedData = rsa.Encrypt(Encoding.UTF8.GetBytes(dataToEncrypt), true);
//Console.WriteLine(Convert.ToBase64String(encryptedData));
return Convert.ToBase64String(encryptedData);
}
}
And at last my Decrypter class
public class Decrypter {
private static final String PATH = "I:\\rsa_key.key";
private File privateKeyFile = new File(PATH);
private RSAPrivateKey privateKey;
public Decrypter() throws Exception {
DataInputStream dis = new DataInputStream(new FileInputStream(privateKeyFile));
byte[] privateKeyBytes = new byte[(int)privateKeyFile.length()];
dis.read(privateKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privSpec);
}
public String decrypt(String data) throws Exception{
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return new String(cipher.doFinal(data.getBytes()));
}
}
I send encrypted data in C# via WebSocket and receive it without any problem because data while sending and receiving is the same. The problem is that data length is 344 characters (bytes) so when I decrypt my data it shows me and error: javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
I'm using 2048 bit key so it's 256B - 11B for padding that's why it's 245B. But the problem is that it always generates 344B and it doesn't depend on message to encode length. For example encoding "A" gives 344B and "Hello world" also 344B.
In this topic people say to use symmetric key, Encrypt the data with the symmetric key and then Encrypt the symmetric key with rsa. But it won't help because any encrypted string in my code has 344B size, so I won't be able to decrypt encrypted symmetric key.
What's wrong with my code?
I am given a Rijndael .Net encrypted file and .Net RSA XML Key and asked to decrypt it in Java.
The key provided to me is 256 bit.
I have parsed the RSA XML file and generated the public Key in Java. I tried to decrypt using the generated key, however I am getting the exception Illegal Key Size, I think I am doing something wrong in my Java code.
Can any one please help to check if anything is wrong with my code?
.Net encryption code:
public static void EncryptFile(string fileIn, string fileOut,
string publicKeyName, string publicKeyFile)
{
try
{
// Read the public key from key file
StreamReader sr = new StreamReader(publicKeyFile);
string strKeyText = sr.ReadToEnd();
sr.Close();
//Initialize Key container and Crypto service provider
RSACryptoServiceProvider rsa;
CspParameters cspp = new CspParameters();
cspp.KeyContainerName = publicKeyName;
rsa = new RSACryptoServiceProvider(cspp);
rsa.FromXmlString(strKeyText);
rsa.PersistKeyInCsp = true;
// Create instance of Rijndael for
// symetric encryption of the data.
RijndaelManaged alg = new RijndaelManaged();
// Key size is set to 256 for strong encryption
alg.KeySize = 256;
alg.BlockSize = 256;
// Cipher Mode is set to CBC to process the file in chunks
alg.Mode = CipherMode.CBC;
// Set padding mode to process the last block of the file
alg.Padding = PaddingMode.ISO10126;
ICryptoTransform transform = alg.CreateEncryptor();
// Use RSACryptoServiceProvider to
// enrypt the Rijndael key.
byte[] KeyEncrypted = rsa.Encrypt(alg.Key, false);
// Create byte arrays to contain
// the length values of the key and IV.
int intKeyLength = KeyEncrypted.Length;
byte[] LenK = BitConverter.GetBytes(intKeyLength);
int intIVLength = alg.IV.Length;
byte[] LenIV = BitConverter.GetBytes(intIVLength);
using (FileStream fsOut = new FileStream(fileOut, FileMode.Create))
{
// Write the following to the FileStream
// for the encrypted file (fsOut):
// - length of the key
// - length of the IV
// - ecrypted key
// - the IV
// - the encrypted cipher content
fsOut.Write(LenK, 0, 4);
fsOut.Write(LenIV, 0, 4);
fsOut.Write(KeyEncrypted, 0, intKeyLength);
fsOut.Write(alg.IV, 0, intIVLength);
// Now write the cipher text using
// a CryptoStream for encrypting.
using (CryptoStream cs = new CryptoStream(fsOut, transform, CryptoStreamMode.Write))
{
// intBlockSizeBytes can be any arbitrary size.
int intBlockSizeBytes = alg.BlockSize / 8;
byte[] DataBytes = new byte[intBlockSizeBytes];
int intBytesRead = 0;
using (FileStream fsIn = new FileStream(fileIn, FileMode.Open))
{
// By encrypting a chunk at
// a time, you can save memory
// and accommodate large files.
int intCount;
int intOffset = 0;
do
{
// if last block size is less than encryption chunk size
// use the last block size and padding character is used
// for remaining bytes
if (intBlockSizeBytes > (fsIn.Length - fsIn.Position))
{
intBlockSizeBytes = ((int)(fsIn.Length - fsIn.Position));
DataBytes = new byte[intBlockSizeBytes];
}
// read data bytes
intCount = fsIn.Read(DataBytes, 0, intBlockSizeBytes);
intOffset += intCount;
// write it into crypto stream
cs.Write(DataBytes, 0, intCount);
intBytesRead += intBlockSizeBytes;
} while (intCount > 0);
// close input file
fsIn.Close();
}
// close crypto stream
cs.FlushFinalBlock();
cs.Close();
}
// close output file
fsOut.Close();
}
}
catch
{
throw;
}
}
Java Code that I wrote to decrypt it:
byte[] expBytes = Base64.decodeBase64(pkey.getExponentEle().trim());
byte[] modBytes = Base64.decodeBase64(pkey.getModulusEle().trim());
byte[] dBytes = Base64.decodeBase64(pkey.getdEle().trim());
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponent = new BigInteger(1, expBytes);
BigInteger d = new BigInteger(1, dBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponent);
PublicKey pubKey = factory.generatePublic(pubSpec);
final byte[] keyData = Arrays.copyOf(pubKey.getEncoded(), 256
/ Byte.SIZE);
final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());
AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"), paramSpec);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("decrypted: " + new String(decrypted));
If I change cipher initialization to cipher.init(Cipher.DECRYPT_MODE, pubKey);, then I am getting the error Invalid AES key length: 162 bytes
You are using the public key wrong way. Do you really understad how the C# program works? what parameters is it using?
You are just using the public key bits as the AES key (even I don't realy understand how do you get 162 bytes from it).
This is example of "hybrid encryption" - the data themselves are encrypted by a random AES key (in this you claim it's 256 bit) and the AES key (in this case the IV too) is encrypted by the RSA public key. In Java there are many examples how to do that.
Even to decrypt the AES key you should know parameters used to encrypt it (RSA/ECB/PKCS5Padding, RSA-AOEP, ...), though it should be inside the XML.
Comming to the parameters - you are using PKCS5Padding, but check the .NET code, it's different
I have a problem when decrypting XML type my file my returns incomplete data algorithm and rare symbols.
public File decryptFile(File fileInput, X509Certificate certificate) throws BadPaddingException, Exception {
try (DataInputStream dis = new DataInputStream(new FileInputStream(fileInput))) {
byte[] encryptedKeyBytes = new byte[dis.readInt()];
dis.readFully(encryptedKeyBytes);
PublicKey publicKey = certificate.getPublicKey();
rsaCipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] rijndaelKeyBytes = rsaCipher.doFinal(encryptedKeyBytes);
SecretKey rijndaelKey = new SecretKeySpec(rijndaelKeyBytes, "Rijndael");
byte[] iv = new byte[16];
dis.read(iv);
IvParameterSpec spec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, rijndaelKey, spec);
try (CipherInputStream cis = new CipherInputStream(dis, cipher)) {
try (FileOutputStream fos = new FileOutputStream(fileInput.getAbsolutePath() + ".xml")) {
byte[] data = new byte[16];
int theByte;
while ((theByte = cis.read(data)) != -1) {
System.out.print(new String(data));
fos.write(data, 0, theByte);
}
System.out.println("\n\n");
}
}
}
return new File(fileInput.getAbsolutePath() + ".xml");
}
this code returns me the data
</ctaAbonBenef><distPago>00000</distPago><item>00000</item><pagoPoder>N</p�|���[�[W�Z�5��Q�
I think this has to do with UTF-8, but I can not solve.
Now I can also believe that it is the encryption algorithm to use, I leave just in case.
public static void generateFileEncrypt(File fileInput, PrivateKey privateKey, String folderSave) throws Exception {
String fileOutput = folderSave + "\" + fileInput.getName() + ENCRYPTED_FILENAME_SUFFIX;
DataOutputStream output = new DataOutputStream(new FileOutputStream(fileOutput));
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, privateKey);
KeyGenerator rijndaelKeyGenerator = KeyGenerator.getInstance("Rijndael");
rijndaelKeyGenerator.init(128);
Key rijndaelKey = rijndaelKeyGenerator.generateKey();
byte[] encodedKeyBytes = rsaCipher.doFinal(rijndaelKey.getEncoded());
output.writeInt(encodedKeyBytes.length);
output.write(encodedKeyBytes);
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);
output.write(iv);
IvParameterSpec spec = new IvParameterSpec(iv);
Cipher symmetricCipher = Cipher.getInstance("Rijndael/CBC/PKCS5Padding");
symmetricCipher.init(Cipher.ENCRYPT_MODE, rijndaelKey, spec);
try (
CipherOutputStream cos = new CipherOutputStream(output, symmetricCipher);
FileInputStream fis = new FileInputStream(fileInput)) {
int theByte;
byte[] data = new byte[16];
while ((theByte = fis.read(data)) != -1) {
System.out.print(new String(data));
cos.write(data, 0, theByte);
}
System.out.println("\n\n");
cos.flush();
}
}
Thanks in advance.
I haven't digested all your code; I stopped when I saw you trying to decrypt with the public key, and encrypting with the private key. That's sort of like a digital signature, but your padding will be all wrong and you should use the Signature class if that is what your really want to do.
The public key is used to encrypt, or to verify a digital signature. Use the private key to decrypt, and see if that resolves your problem.
You are still doing it wrong. Don't call it "encryption" if the key isn't private.
But anyway, I think the printing to stdout looks wrong because you are converting the entire buffer to text. The last block is likely to be padded, so it won't decode to valid text—it's padding; it wasn't part of the input file, and you aren't writing it to the decrypted file, but you are printing it.
Change to encrypt with the public key, decrypt with the private key, and then change your printing to this:
System.out.print(new String(data, 0, theByte));
Even better would be to specify the character set of the data (probably UTF-8, since it's the default for XML).
I think u should do the opposite. encrypt with the public key and decrypt with the private key..
In a larger application doing other things - I need to encrypt and decrypt a file. So I have been looking around and have implemented these two core functions that basically use RSA keys to wrap a random AES key that encrypts a file. The symmetric key and iv are written to the start of the file.
I'm getting an exception ("javax.crypto.BadPaddingException: Decryption error") in the decrypt function portion of below. On the unpackKeyandIV line -- the doFinal. Specifically this line is the Exception point:
Object[] keyIv = unpackKeyAndIV(xCipher.doFinal(keyBlock));
I've checked and remade the RSA key pairs. I've also checked the save/load of the keyBlock.
My gut is the problem has something to do with how I write/read the keyBlock --- or encoding perhaps?
One goal is to keep the RSA/AES instance as generic as possible so as not to need Bouncy Castle or extra Java security unlimited strength extensions.
Any thoughts on where I might be going wrong.
Thanks in advance.
[Final update: This code below is working. Error was passing in a corrupted privKey]
// RSA_INSTANCE = "RSA";
// ASSYM_CRYPTO_STR = 1024;
// SYM_CRYPTO_STR = 128;
// SYM_CRYPTO = "AES";
// AES_INSTANCE = "AES/CTR/NoPadding";
//
// File in = plain input file
// File out = encrypted output file
// Key pubKey = public Key (that wraps a random AES key)
public static void encryptFile(File in, File out, Key pubKey) throws Exception {
FileInputStream fin;
FileOutputStream fout;
int nread = 0;
byte[] inbuf = new byte[1024];
fout = new FileOutputStream(out);
fin = new FileInputStream(in);
SecureRandom random = new SecureRandom();
// symmetric wrapping
Key sKey = createKeyForAES(Config.SYM_CRYPTO_STR, random);
IvParameterSpec sIvSpec = createCtrIvForAES(0, random);
// encrypt symmetric key with RSA/pub key
Cipher xCipher = Cipher.getInstance(Config.RSA_INSTANCE);
xCipher.init(Cipher.ENCRYPT_MODE, pubKey, random);
byte[] keyBlock = xCipher.doFinal(packKeyAndIv(sKey, sIvSpec));
fout.write(keyBlock);
// encrypt data with symmetric key
Cipher sCipher = Cipher.getInstance(Config.AES_INSTANCE);
sCipher.init(Cipher.ENCRYPT_MODE, sKey, sIvSpec);
// Now read our file and encrypt it.
while((nread = fin.read(inbuf)) > 0) {
fout.write(sCipher.update(inbuf, 0, nread)); // cannot be null, by construction
}
// NB doFinal() cannot return null, but can return a zero-length array, which is benign below.
fout.write(sCipher.doFinal());
fout.flush();
fin.close();
fout.close();
}
// Decrypt File
public static void decryptFile(File in, File out, Key privKey) throws Exception {
FileInputStream fin;
FileOutputStream fout;
int nread = 0;
byte[] inbuf = new byte[1024];
fout = new FileOutputStream(out);
fin = new FileInputStream(in);
byte[] keyBlock = new byte[128];
nread = fin.read(keyBlock);
Cipher xCipher = Cipher.getInstance(Config.RSA_INSTANCE);
Cipher sCipher = Cipher.getInstance(Config.AES_INSTANCE);
// symmetric key/iv unwrapping step
xCipher.init(Cipher.DECRYPT_MODE, privKey);
Object[] keyIv = unpackKeyAndIV(xCipher.doFinal(keyBlock));
// decryption step
sCipher.init(Cipher.DECRYPT_MODE, (Key)keyIv[0], (IvParameterSpec)keyIv[1]);
while((nread = fin.read(inbuf)) >0) {
fout.write(sCipher.update(inbuf,0,nread));
}
fout.write(sCipher.doFinal());
fout.flush();
fin.close();
fout.close();
}
public static byte[] packKeyAndIv(Key key, IvParameterSpec ivSpec) throws IOException {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
bOut.write(ivSpec.getIV());
bOut.write(key.getEncoded());
return bOut.toByteArray();
}
public static Object[] unpackKeyAndIV(byte[] data) {
byte[] keyD = new byte[16];
byte[] iv = new byte[data.length - 16];
return new Object[] {
new SecretKeySpec(data, 16, data.length - 16, "AES"),
new IvParameterSpec(data, 0, 16)
};
}
Per edits and comments. Error was a corrupted privKey being passed into the decrypt function. Above code works fine.
try adding the following under your constructor -
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());