use gcm for file encryption over http
This commit is contained in:
parent
b7c64cd19d
commit
58d80f58be
|
@ -1,26 +1,7 @@
|
|||
package eu.siacs.conversations.entities;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.utils.MimeUtils;
|
||||
|
||||
public class DownloadableFile extends File {
|
||||
|
@ -29,8 +10,7 @@ public class DownloadableFile extends File {
|
|||
|
||||
private long expectedSize = 0;
|
||||
private String sha1sum;
|
||||
private Key aeskey;
|
||||
private String mime;
|
||||
private byte[] aeskey;
|
||||
|
||||
private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
|
||||
|
@ -84,85 +64,24 @@ public class DownloadableFile extends File {
|
|||
byte[] iv = new byte[16];
|
||||
System.arraycopy(key, 0, iv, 0, 16);
|
||||
System.arraycopy(key, 16, secretKey, 0, 32);
|
||||
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
||||
this.aeskey = secretKey;
|
||||
this.iv = iv;
|
||||
} else if (key.length >= 32) {
|
||||
byte[] secretKey = new byte[32];
|
||||
System.arraycopy(key, 0, secretKey, 0, 32);
|
||||
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
||||
this.aeskey = secretKey;
|
||||
} else if (key.length >= 16) {
|
||||
byte[] secretKey = new byte[16];
|
||||
System.arraycopy(key, 0, secretKey, 0, 16);
|
||||
this.aeskey = new SecretKeySpec(secretKey, "AES");
|
||||
this.aeskey = secretKey;
|
||||
}
|
||||
}
|
||||
|
||||
public Key getKey() {
|
||||
public byte[] getKey() {
|
||||
return this.aeskey;
|
||||
}
|
||||
|
||||
public InputStream createInputStream() {
|
||||
if (this.getKey() == null) {
|
||||
try {
|
||||
return new FileInputStream(this);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
IvParameterSpec ips = new IvParameterSpec(iv);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, this.getKey(), ips);
|
||||
Log.d(Config.LOGTAG, "opening encrypted input stream");
|
||||
return new CipherInputStream(new FileInputStream(this), cipher);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
|
||||
return null;
|
||||
} catch (NoSuchPaddingException e) {
|
||||
Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
|
||||
return null;
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
|
||||
return null;
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
|
||||
return null;
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OutputStream createOutputStream() {
|
||||
if (this.getKey() == null) {
|
||||
try {
|
||||
return new FileOutputStream(this);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
IvParameterSpec ips = new IvParameterSpec(this.iv);
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, this.getKey(), ips);
|
||||
Log.d(Config.LOGTAG, "opening encrypted output stream");
|
||||
return new CipherOutputStream(new FileOutputStream(this),
|
||||
cipher);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Log.d(Config.LOGTAG, "no such algo: " + e.getMessage());
|
||||
return null;
|
||||
} catch (NoSuchPaddingException e) {
|
||||
Log.d(Config.LOGTAG, "no such padding: " + e.getMessage());
|
||||
return null;
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.d(Config.LOGTAG, "invalid key: " + e.getMessage());
|
||||
return null;
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage());
|
||||
return null;
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public byte[] getIv() {
|
||||
return this.iv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,17 @@ package eu.siacs.conversations.http;
|
|||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import org.bouncycastle.crypto.engines.AESEngine;
|
||||
import org.bouncycastle.crypto.io.CipherOutputStream;
|
||||
import org.bouncycastle.crypto.modes.AEADBlockCipher;
|
||||
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
||||
import org.bouncycastle.crypto.params.AEADParameters;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
|
@ -206,7 +213,7 @@ public class HttpDownloadConnection implements Transferable {
|
|||
}
|
||||
}
|
||||
|
||||
private void download() throws SSLHandshakeException, IOException {
|
||||
private void download() throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection();
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive);
|
||||
|
@ -215,9 +222,13 @@ public class HttpDownloadConnection implements Transferable {
|
|||
BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
OutputStream os = file.createOutputStream();
|
||||
if (os == null) {
|
||||
throw new IOException();
|
||||
OutputStream os;
|
||||
if (file.getKey() != null) {
|
||||
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
|
||||
cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
|
||||
os = new CipherOutputStream(new FileOutputStream(file), cipher);
|
||||
} else {
|
||||
os = new FileOutputStream(file);
|
||||
}
|
||||
long transmitted = 0;
|
||||
long expected = file.getExpectedSize();
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
package eu.siacs.conversations.http;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import org.bouncycastle.crypto.engines.AESEngine;
|
||||
import org.bouncycastle.crypto.io.CipherInputStream;
|
||||
import org.bouncycastle.crypto.modes.AEADBlockCipher;
|
||||
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
||||
import org.bouncycastle.crypto.params.AEADParameters;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -43,7 +53,7 @@ public class HttpUploadConnection implements Transferable {
|
|||
private byte[] key = null;
|
||||
|
||||
private long transmitted = 0;
|
||||
private long expected = 1;
|
||||
private int expected = 1;
|
||||
|
||||
public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
|
||||
this.mHttpConnectionManager = httpConnectionManager;
|
||||
|
@ -142,14 +152,21 @@ public class HttpUploadConnection implements Transferable {
|
|||
if (connection instanceof HttpsURLConnection) {
|
||||
mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
|
||||
}
|
||||
if (file.getKey() != null) {
|
||||
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
|
||||
cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
|
||||
expected = cipher.getOutputSize((int) file.getSize());
|
||||
is = new CipherInputStream(new FileInputStream(file), cipher);
|
||||
} else {
|
||||
expected = (int) file.getSize();
|
||||
is = new FileInputStream(file);
|
||||
}
|
||||
connection.setRequestMethod("PUT");
|
||||
connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
|
||||
connection.setFixedLengthStreamingMode(expected);
|
||||
connection.setDoOutput(true);
|
||||
connection.connect();
|
||||
os = connection.getOutputStream();
|
||||
is = file.createInputStream();
|
||||
transmitted = 0;
|
||||
expected = file.getExpectedSize();
|
||||
int count = -1;
|
||||
byte[] buffer = new byte[4096];
|
||||
while (((count = is.read(buffer)) != -1) && !canceled) {
|
||||
|
@ -163,11 +180,13 @@ public class HttpUploadConnection implements Transferable {
|
|||
int code = connection.getResponseCode();
|
||||
if (code == 200 || code == 201) {
|
||||
Log.d(Config.LOGTAG, "finished uploading file");
|
||||
Message.FileParams params = message.getFileParams();
|
||||
if (key != null) {
|
||||
mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
|
||||
}
|
||||
mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl);
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
intent.setData(Uri.fromFile(file));
|
||||
mXmppConnectionService.sendBroadcast(intent);
|
||||
message.setTransferable(null);
|
||||
message.setCounterpart(message.getConversation().getJid().toBareJid());
|
||||
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
|
||||
|
@ -188,12 +207,13 @@ public class HttpUploadConnection implements Transferable {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
mXmppConnectionService.resendMessage(message,delayed);
|
||||
mXmppConnectionService.resendMessage(message, delayed);
|
||||
}
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.d(Config.LOGTAG,"http upload failed "+e.getMessage());
|
||||
fail();
|
||||
} finally {
|
||||
|
|
|
@ -93,7 +93,7 @@ public class JingleInbandTransport extends JingleTransport {
|
|||
digest.reset();
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
this.fileOutputStream = file.createOutputStream();
|
||||
this.fileOutputStream = createOutputStream(file);
|
||||
if (this.fileOutputStream == null) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
|
||||
callback.onFileTransferAborted();
|
||||
|
@ -120,7 +120,7 @@ public class JingleInbandTransport extends JingleTransport {
|
|||
this.fileSize = this.remainingSize;
|
||||
this.digest = MessageDigest.getInstance("SHA-1");
|
||||
this.digest.reset();
|
||||
fileInputStream = this.file.createInputStream();
|
||||
fileInputStream = createInputStream(this.file);
|
||||
if (fileInputStream == null) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
|
||||
callback.onFileTransferAborted();
|
||||
|
|
|
@ -106,7 +106,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
digest.reset();
|
||||
fileInputStream = file.createInputStream();
|
||||
fileInputStream = createInputStream(file); //file.createInputStream();
|
||||
if (fileInputStream == null) {
|
||||
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
|
||||
callback.onFileTransferAborted();
|
||||
|
@ -157,7 +157,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||
socket.setSoTimeout(30000);
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
fileOutputStream = file.createOutputStream();
|
||||
fileOutputStream = createOutputStream(file);
|
||||
if (fileOutputStream == null) {
|
||||
callback.onFileTransferAborted();
|
||||
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
package eu.siacs.conversations.xmpp.jingle;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.entities.DownloadableFile;
|
||||
|
||||
public abstract class JingleTransport {
|
||||
|
@ -12,4 +31,58 @@ public abstract class JingleTransport {
|
|||
final OnFileTransmissionStatusChanged callback);
|
||||
|
||||
public abstract void disconnect();
|
||||
|
||||
protected InputStream createInputStream(DownloadableFile file) {
|
||||
FileInputStream is;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
if (file.getKey() == null) {
|
||||
return is;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
IvParameterSpec ips = new IvParameterSpec(file.getIv());
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
|
||||
Log.d(Config.LOGTAG, "opening encrypted input stream");
|
||||
return new CipherInputStream(is, cipher);
|
||||
} catch (InvalidKeyException e) {
|
||||
return null;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
} catch (NoSuchPaddingException e) {
|
||||
return null;
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected OutputStream createOutputStream(DownloadableFile file) {
|
||||
FileOutputStream os;
|
||||
try {
|
||||
os = new FileOutputStream(file);
|
||||
if (file.getKey() == null) {
|
||||
return os;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
IvParameterSpec ips = new IvParameterSpec(file.getIv());
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
|
||||
Log.d(Config.LOGTAG, "opening encrypted output stream");
|
||||
return new CipherOutputStream(os, cipher);
|
||||
} catch (InvalidKeyException e) {
|
||||
return null;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
} catch (NoSuchPaddingException e) {
|
||||
return null;
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue