diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java
index 702e06bb8b..333e92b4aa 100644
--- a/app/src/main/java/eu/faircode/email/FragmentCompose.java
+++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java
@@ -52,6 +52,8 @@ import android.os.Handler;
import android.os.OperationCanceledException;
import android.provider.ContactsContract;
import android.provider.MediaStore;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -97,6 +99,17 @@ import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomnavigation.LabelVisibilityMode;
import com.google.android.material.snackbar.Snackbar;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSProcessableFile;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.Store;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
@@ -114,6 +127,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.UnknownHostException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -1203,56 +1218,81 @@ public class FragmentCompose extends FragmentBase {
onAction(R.id.action_check, extras);
}
- private void onEncrypt(EntityMessage draft) {
- if (pgpService.isBound())
- try {
- List
recipients = new ArrayList<>();
- if (draft.to != null)
- recipients.addAll(Arrays.asList(draft.to));
- if (draft.cc != null)
- recipients.addAll(Arrays.asList(draft.cc));
- if (draft.bcc != null)
- recipients.addAll(Arrays.asList(draft.bcc));
-
- if (recipients.size() == 0)
- throw new IllegalArgumentException(getString(R.string.title_to_missing));
-
- pgpUserIds = new String[recipients.size()];
- for (int i = 0; i < recipients.size(); i++) {
- InternetAddress recipient = (InternetAddress) recipients.get(i);
- pgpUserIds[i] = recipient.getAddress().toLowerCase(Locale.ROOT);
- }
+ private void onEncrypt(final EntityMessage draft) {
+ if (EntityMessage.SMIME_SIGNONLY.equals(draft.encrypt) ||
+ EntityMessage.SMIME_SIGNENCRYPT.equals(draft.encrypt)) {
+ Handler handler = new Handler();
+ KeyChain.choosePrivateKeyAlias(getActivity(), new KeyChainAliasCallback() {
+ @Override
+ public void alias(@Nullable String alias) {
+ Log.i("Selected key alias=" + alias);
+ if (alias != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ onSmime(draft, alias);
+ } catch (Throwable ex) {
+ Log.e(ex);
+ }
+ }
+ });
+ }
+ }
+ },
+ null, null, null, -1, null);
- Intent intent;
- if (EntityMessage.PGP_SIGNONLY.equals(draft.encrypt)) {
- intent = new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
- intent.putExtra(BuildConfig.APPLICATION_ID, working);
- } else if (EntityMessage.PGP_SIGNENCRYPT.equals(draft.encrypt)) {
- intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
- intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, pgpUserIds);
- intent.putExtra(BuildConfig.APPLICATION_ID, working);
- } else
- throw new IllegalArgumentException("Invalid encrypt=" + draft.encrypt);
+ } else {
+ if (pgpService.isBound())
+ try {
+ List recipients = new ArrayList<>();
+ if (draft.to != null)
+ recipients.addAll(Arrays.asList(draft.to));
+ if (draft.cc != null)
+ recipients.addAll(Arrays.asList(draft.cc));
+ if (draft.bcc != null)
+ recipients.addAll(Arrays.asList(draft.bcc));
+
+ if (recipients.size() == 0)
+ throw new IllegalArgumentException(getString(R.string.title_to_missing));
+
+ pgpUserIds = new String[recipients.size()];
+ for (int i = 0; i < recipients.size(); i++) {
+ InternetAddress recipient = (InternetAddress) recipients.get(i);
+ pgpUserIds[i] = recipient.getAddress().toLowerCase(Locale.ROOT);
+ }
- onPgp(intent);
- } catch (Throwable ex) {
- if (ex instanceof IllegalArgumentException)
- Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
- else {
- Log.e(ex);
- Helper.unexpectedError(getParentFragmentManager(), ex);
+ Intent intent;
+ if (EntityMessage.PGP_SIGNONLY.equals(draft.encrypt)) {
+ intent = new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
+ intent.putExtra(BuildConfig.APPLICATION_ID, working);
+ } else if (EntityMessage.PGP_SIGNENCRYPT.equals(draft.encrypt)) {
+ intent = new Intent(OpenPgpApi.ACTION_GET_KEY_IDS);
+ intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, pgpUserIds);
+ intent.putExtra(BuildConfig.APPLICATION_ID, working);
+ } else
+ throw new IllegalArgumentException("Invalid encrypt=" + draft.encrypt);
+
+ onPgp(intent);
+ } catch (Throwable ex) {
+ if (ex instanceof IllegalArgumentException)
+ Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
+ else {
+ Log.e(ex);
+ Helper.unexpectedError(getParentFragmentManager(), ex);
+ }
}
+ else {
+ Snackbar snackbar = Snackbar.make(view, R.string.title_no_openpgp, Snackbar.LENGTH_LONG);
+ if (Helper.getIntentOpenKeychain().resolveActivity(getContext().getPackageManager()) != null)
+ snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(Helper.getIntentOpenKeychain());
+ }
+ });
+ snackbar.show();
}
- else {
- Snackbar snackbar = Snackbar.make(view, R.string.title_no_openpgp, Snackbar.LENGTH_LONG);
- if (Helper.getIntentOpenKeychain().resolveActivity(getContext().getPackageManager()) != null)
- snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- startActivity(Helper.getIntentOpenKeychain());
- }
- });
- snackbar.show();
}
}
@@ -1518,8 +1558,8 @@ public class FragmentCompose extends FragmentBase {
throw new IllegalArgumentException(getString(R.string.title_from_missing));
// Create files
- File input = new File(context.getCacheDir(), "pgp_input." + id);
- File output = new File(context.getCacheDir(), "pgp_output." + id);
+ File input = new File(context.getCacheDir(), "pgp_input." + draft.id);
+ File output = new File(context.getCacheDir(), "pgp_output." + draft.id);
// Serializing messages is NOT reproducible
if ((EntityMessage.PGP_SIGNONLY.equals(draft.encrypt) &&
@@ -1527,7 +1567,7 @@ public class FragmentCompose extends FragmentBase {
(EntityMessage.PGP_SIGNENCRYPT.equals(draft.encrypt) &&
OpenPgpApi.ACTION_GET_KEY_IDS.equals(data.getAction()))) {
// Get/clean attachments
- List attachments = db.attachment().getAttachments(id);
+ List attachments = db.attachment().getAttachments(draft.id);
for (EntityAttachment attachment : new ArrayList<>(attachments))
if (attachment.encryption != null) {
db.attachment().deleteAttachment(attachment.id);
@@ -1610,7 +1650,7 @@ public class FragmentCompose extends FragmentBase {
db.beginTransaction();
String name;
- String type = "application/octet-stream";
+ ContentType ct = new ContentType("application/octet-stream");
int encryption;
if (OpenPgpApi.ACTION_GET_KEY.equals(data.getAction())) {
name = "keydata.asc";
@@ -1618,8 +1658,8 @@ public class FragmentCompose extends FragmentBase {
} else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) {
name = "signature.asc";
encryption = EntityAttachment.PGP_SIGNATURE;
- type = "application/pgp-signature; micalg=\"" +
- result.getStringExtra(OpenPgpApi.RESULT_SIGNATURE_MICALG) + "\"";
+ ct = new ContentType("application/pgp-signature");
+ ct.setParameter("micalg", result.getStringExtra(OpenPgpApi.RESULT_SIGNATURE_MICALG));
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(data.getAction())) {
name = "encrypted.asc";
encryption = EntityAttachment.PGP_MESSAGE;
@@ -1627,10 +1667,10 @@ public class FragmentCompose extends FragmentBase {
throw new IllegalStateException(data.getAction());
EntityAttachment attachment = new EntityAttachment();
- attachment.message = id;
- attachment.sequence = db.attachment().getAttachmentSequence(id) + 1;
+ attachment.message = draft.id;
+ attachment.sequence = db.attachment().getAttachmentSequence(draft.id) + 1;
attachment.name = name;
- attachment.type = type;
+ attachment.type = ct.toString();
attachment.disposition = Part.INLINE;
attachment.encryption = encryption;
attachment.id = db.attachment().insertAttachment(attachment);
@@ -1666,7 +1706,7 @@ public class FragmentCompose extends FragmentBase {
Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY);
intent.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpKeyIds[0]);
intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- intent.putExtra(BuildConfig.APPLICATION_ID, id);
+ intent.putExtra(BuildConfig.APPLICATION_ID, draft.id);
return intent;
}
}
@@ -1678,7 +1718,7 @@ public class FragmentCompose extends FragmentBase {
Intent intent = new Intent(OpenPgpApi.ACTION_DETACHED_SIGN);
intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, pgpSignKeyId);
intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- intent.putExtra(BuildConfig.APPLICATION_ID, id);
+ intent.putExtra(BuildConfig.APPLICATION_ID, draft.id);
return intent;
} else {
if (identity.sign_key != null) {
@@ -1687,13 +1727,13 @@ public class FragmentCompose extends FragmentBase {
intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, pgpKeyIds);
intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, identity.sign_key);
intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- intent.putExtra(BuildConfig.APPLICATION_ID, id);
+ intent.putExtra(BuildConfig.APPLICATION_ID, draft.id);
return intent;
} else {
// Get sign key
Intent intent = new Intent(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, pgpUserIds);
- intent.putExtra(BuildConfig.APPLICATION_ID, id);
+ intent.putExtra(BuildConfig.APPLICATION_ID, draft.id);
return intent;
}
}
@@ -1706,7 +1746,7 @@ public class FragmentCompose extends FragmentBase {
Intent intent = new Intent(OpenPgpApi.ACTION_GET_KEY);
intent.putExtra(OpenPgpApi.EXTRA_KEY_ID, pgpSignKeyId);
intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- intent.putExtra(BuildConfig.APPLICATION_ID, id);
+ intent.putExtra(BuildConfig.APPLICATION_ID, draft.id);
return intent;
} else if (EntityMessage.PGP_SIGNENCRYPT.equals(draft.encrypt)) {
// Encrypt message
@@ -1714,14 +1754,14 @@ public class FragmentCompose extends FragmentBase {
intent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, pgpKeyIds);
intent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, pgpSignKeyId);
intent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- intent.putExtra(BuildConfig.APPLICATION_ID, id);
+ intent.putExtra(BuildConfig.APPLICATION_ID, draft.id);
return intent;
} else
throw new IllegalArgumentException("Invalid encrypt=" + draft.encrypt);
} else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(data.getAction())) {
EntityAttachment attachment = new EntityAttachment();
- attachment.message = id;
- attachment.sequence = db.attachment().getAttachmentSequence(id) + 1;
+ attachment.message = draft.id;
+ attachment.sequence = db.attachment().getAttachmentSequence(draft.id) + 1;
attachment.name = "content.asc";
attachment.type = "text/plain";
attachment.disposition = Part.INLINE;
@@ -1794,7 +1834,150 @@ public class FragmentCompose extends FragmentBase {
} else
Helper.unexpectedError(getParentFragmentManager(), ex);
}
- }.execute(this, args, "compose:encrypt");
+ }.execute(this, args, "compose:pgp");
+ }
+
+ private void onSmime(EntityMessage draft, String alias) {
+ Bundle args = new Bundle();
+ args.putLong("id", draft.id);
+ args.putInt("type", draft.encrypt);
+ args.putString("alias", alias);
+
+ new SimpleTask() {
+ @Override
+ protected Void onExecute(Context context, Bundle args) throws Throwable {
+ long id = args.getLong("id");
+ int type = args.getInt("type");
+ String alias = args.getString("alias");
+
+ if (!EntityMessage.SMIME_SIGNONLY.equals(type))
+ throw new UnsupportedOperationException("Not yet supported");
+
+ if (alias == null)
+ throw new IllegalArgumentException("Key alias missing");
+
+ // Get key
+ PrivateKey privkey = KeyChain.getPrivateKey(context, alias);
+ if (privkey == null)
+ throw new IllegalArgumentException("Private key missing");
+ X509Certificate[] chain = KeyChain.getCertificateChain(context, alias);
+ if (chain == null || chain.length == 0)
+ throw new IllegalArgumentException("Certificate missing");
+
+ DB db = DB.getInstance(context);
+
+ // Get data
+ EntityMessage draft = db.message().getMessage(id);
+ if (draft == null)
+ throw new MessageRemovedException("S/MIME");
+ EntityIdentity identity = db.identity().getIdentity(draft.identity);
+ if (identity == null)
+ throw new IllegalArgumentException(getString(R.string.title_from_missing));
+
+ // Get/clean attachments
+ List attachments = db.attachment().getAttachments(id);
+ for (EntityAttachment attachment : new ArrayList<>(attachments))
+ if (attachment.encryption != null) {
+ db.attachment().deleteAttachment(attachment.id);
+ attachments.remove(attachment);
+ }
+
+ // Build message
+ Properties props = MessageHelper.getSessionProperties();
+ Session isession = Session.getInstance(props, null);
+ MimeMessage imessage = new MimeMessage(isession);
+ MessageHelper.build(context, draft, attachments, identity, imessage);
+ imessage.saveChanges();
+ BodyPart bpContent = new MimeBodyPart() {
+ @Override
+ public void setContent(Object content, String type) throws MessagingException {
+ super.setContent(content, type);
+
+ // https://javaee.github.io/javamail/FAQ#howencode
+ updateHeaders();
+ if (content instanceof Multipart) {
+ try {
+ MessageHelper.overrideContentTransferEncoding((Multipart) content);
+ } catch (IOException ex) {
+ Log.e(ex);
+ }
+ } else
+ setHeader("Content-Transfer-Encoding", "base64");
+ }
+ };
+ bpContent.setContent(imessage.getContent(), imessage.getContentType());
+
+ // Build content
+ EntityAttachment cattachment = new EntityAttachment();
+ cattachment.message = draft.id;
+ cattachment.sequence = db.attachment().getAttachmentSequence(draft.id) + 1;
+ cattachment.name = "content.asc";
+ cattachment.type = "text/plain";
+ cattachment.disposition = Part.INLINE;
+ cattachment.encryption = EntityAttachment.SMIME_CONTENT;
+ cattachment.id = db.attachment().insertAttachment(cattachment);
+
+ File content = cattachment.getFile(context);
+ try (OutputStream os = new FileOutputStream(content)) {
+ bpContent.writeTo(os);
+ }
+
+ db.attachment().setDownloaded(cattachment.id, content.length());
+
+ // Build signature
+ CMSTypedData cmsData = new CMSProcessableFile(content);
+ List certList = new ArrayList<>();
+ certList.add(chain[0]);
+ Store certs = new JcaCertStore(certList);
+
+ CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator();
+ ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256withRSA").build(privkey);
+ cmsGenerator.addSignerInfoGenerator(
+ new JcaSignerInfoGeneratorBuilder(
+ new JcaDigestCalculatorProviderBuilder()
+ .setProvider(new BouncyCastleProvider()).build())
+ .build(contentSigner, chain[0]));
+ cmsGenerator.addCertificates(certs);
+
+ CMSSignedData cms = cmsGenerator.generate(cmsData, true);
+ byte[] signedMessage = cms.getEncoded();
+
+ ContentType ct = new ContentType("application/pkcs7-signature");
+ ct.setParameter("micalg", "sha-256");
+
+ EntityAttachment sattachment = new EntityAttachment();
+ sattachment.message = draft.id;
+ sattachment.sequence = db.attachment().getAttachmentSequence(draft.id) + 1;
+ sattachment.name = "smime.p7s";
+ sattachment.type = ct.toString();
+ sattachment.disposition = Part.INLINE;
+ sattachment.encryption = EntityAttachment.SMIME_SIGNATURE;
+ sattachment.id = db.attachment().insertAttachment(sattachment);
+
+ File file = sattachment.getFile(context);
+ try (OutputStream os = new FileOutputStream(file)) {
+ os.write(signedMessage);
+ }
+
+ db.attachment().setDownloaded(sattachment.id, file.length());
+
+ return null;
+ }
+
+ @Override
+ protected void onExecuted(Bundle args, Void result) {
+ onAction(R.id.action_send);
+ }
+
+ @Override
+ protected void onException(Bundle args, Throwable ex) {
+ if (ex instanceof IllegalArgumentException) {
+ Log.i(ex);
+ Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
+ } else
+ Helper.unexpectedError(getParentFragmentManager(), ex);
+ }
+ }.execute(this, args, "compose:s/mimem");
}
private void onContactGroupSelected(Bundle args) {
diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java
index 0536ce6006..3f570f8d4e 100644
--- a/app/src/main/java/eu/faircode/email/FragmentMessages.java
+++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java
@@ -4392,11 +4392,12 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
return null;
} else {
+ // Get alias
String alias = args.getString("alias");
if (alias == null)
throw new IllegalArgumentException("Key alias missing");
- // Check private key
+ // Get private key
PrivateKey privkey = KeyChain.getPrivateKey(context, alias);
if (privkey == null)
throw new IllegalArgumentException("Private key missing");
diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java
index 3024c39f76..a1442869c2 100644
--- a/app/src/main/java/eu/faircode/email/MessageHelper.java
+++ b/app/src/main/java/eu/faircode/email/MessageHelper.java
@@ -78,6 +78,7 @@ import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
+import javax.mail.internet.ParameterList;
import javax.mail.internet.ParseException;
import biweekly.Biweekly;
@@ -248,48 +249,56 @@ public class MessageHelper {
// https://tools.ietf.org/html/rfc3156
for (final EntityAttachment attachment : attachments)
if (EntityAttachment.PGP_SIGNATURE.equals(attachment.encryption)) {
- Log.i("Sending signed message");
+ Log.i("Sending PGP signed message");
for (final EntityAttachment content : attachments)
if (EntityAttachment.PGP_CONTENT.equals(content.encryption)) {
BodyPart bpContent = new MimeBodyPart(new FileInputStream(content.getFile(context)));
- // Build signature
final ContentType cts = new ContentType(attachment.type);
+ String micalg = cts.getParameter("micalg");
+ ParameterList params = cts.getParameterList();
+ params.remove("micalg");
+ cts.setParameterList(params);
+
+ // Build signature
BodyPart bpSignature = new MimeBodyPart();
bpSignature.setFileName(attachment.name);
FileDataSource dsSignature = new FileDataSource(attachment.getFile(context));
dsSignature.setFileTypeMap(new FileTypeMap() {
@Override
public String getContentType(File file) {
- return cts.getBaseType();
+ return cts.toString();
}
@Override
public String getContentType(String filename) {
- return cts.getBaseType();
+ return cts.toString();
}
});
bpSignature.setDataHandler(new DataHandler(dsSignature));
bpSignature.setDisposition(Part.INLINE);
// Build message
- Multipart multipart = new MimeMultipart("signed;" +
- " micalg=\"" + cts.getParameter("micalg") + "\";" +
- " protocol=\"application/pgp-signature\"");
+ ContentType ct = new ContentType("multipart/signed");
+ ct.setParameter("micalg", micalg);
+ ct.setParameter("protocol", "application/pgp-signature");
+ String ctx = ct.toString();
+ int slash = ctx.indexOf("/");
+ Multipart multipart = new MimeMultipart(ctx.substring(slash + 1));
multipart.addBodyPart(bpContent);
multipart.addBodyPart(bpSignature);
imessage.setContent(multipart);
return imessage;
}
- throw new IllegalStateException("Content not found");
+ throw new IllegalStateException("PGP content not found");
} else if (EntityAttachment.PGP_MESSAGE.equals(attachment.encryption)) {
- Log.i("Sending encrypted message");
+ Log.i("Sending PGP encrypted message");
// Build header
BodyPart bpHeader = new MimeBodyPart();
- bpHeader.setContent("Version: 1\r\n", "application/pgp-encrypted");
+ bpHeader.setContent("", "application/pgp-encrypted");
// Build content
BodyPart bpContent = new MimeBodyPart();
@@ -310,13 +319,64 @@ public class MessageHelper {
bpContent.setDisposition(Part.INLINE);
// Build message
- Multipart multipart = new MimeMultipart("encrypted; protocol=\"application/pgp-encrypted\"");
+ ContentType ct = new ContentType("multipart/encrypted");
+ ct.setParameter("protocol", "application/pgp-encrypted");
+ String ctx = ct.toString();
+ int slash = ctx.indexOf("/");
+ Multipart multipart = new MimeMultipart(ctx.substring(slash + 1));
multipart.addBodyPart(bpHeader);
multipart.addBodyPart(bpContent);
imessage.setContent(multipart);
return imessage;
- }
+ } else if (EntityAttachment.SMIME_SIGNATURE.equals(attachment.encryption)) {
+ Log.i("Sending S/MIME signed message");
+
+ for (final EntityAttachment content : attachments)
+ if (EntityAttachment.SMIME_CONTENT.equals(content.encryption)) {
+ BodyPart bpContent = new MimeBodyPart(new FileInputStream(content.getFile(context)));
+
+ final ContentType cts = new ContentType(attachment.type);
+ String micalg = cts.getParameter("micalg");
+ ParameterList params = cts.getParameterList();
+ params.remove("micalg");
+ cts.setParameterList(params);
+
+ // Build signature
+ BodyPart bpSignature = new MimeBodyPart();
+ bpSignature.setFileName(attachment.name);
+ FileDataSource dsSignature = new FileDataSource(attachment.getFile(context));
+ dsSignature.setFileTypeMap(new FileTypeMap() {
+ @Override
+ public String getContentType(File file) {
+ return cts.toString();
+ }
+
+ @Override
+ public String getContentType(String filename) {
+ return cts.toString();
+ }
+ });
+ bpSignature.setDataHandler(new DataHandler(dsSignature));
+ bpSignature.setDisposition(Part.INLINE);
+
+ // Build message
+ ContentType ct = new ContentType("multipart/signed");
+ ct.setParameter("micalg", micalg);
+ ct.setParameter("protocol", "application/pkcs7-signature");
+ ct.setParameter("smime-type", "signed-data");
+ String ctx = ct.toString();
+ int slash = ctx.indexOf("/");
+ Multipart multipart = new MimeMultipart(ctx.substring(slash + 1));
+ multipart.addBodyPart(bpContent);
+ multipart.addBodyPart(bpSignature);
+ imessage.setContent(multipart);
+
+ return imessage;
+ }
+ throw new IllegalStateException("S/MIME content not found");
+ } else if (EntityAttachment.SMIME_MESSAGE.equals(attachment.encryption))
+ throw new UnsupportedOperationException();
build(context, message, attachments, identity, imessage);
diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml
index 203ecc9f5b..c96cff9fea 100644
--- a/app/src/main/res/values-bg-rBG/strings.xml
+++ b/app/src/main/res/values-bg-rBG/strings.xml
@@ -960,5 +960,7 @@
- Няма
- PGP само подписване
- PGP подписване+криптиране
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 202fee5553..dd04eb8aa9 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -981,5 +981,7 @@
- Žádný
- Pouze PGP podpis
- PGP podpis+šifrovat
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml
index b90f9dda3f..5204ef57cf 100644
--- a/app/src/main/res/values-da-rDK/strings.xml
+++ b/app/src/main/res/values-da-rDK/strings.xml
@@ -959,5 +959,7 @@
- Ingen
- Kun PGP-signering
- PGP-signering + kryptering
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml
index 0b9374f4e7..cd9848e148 100644
--- a/app/src/main/res/values-de-rDE/strings.xml
+++ b/app/src/main/res/values-de-rDE/strings.xml
@@ -959,5 +959,7 @@ Konten und Identitäten (Aliase) können bei Bedarf auch manuell eingerichtet we
- Keine Verschlüsselung
- PGP nur unterschreiben
- PGP unterschreiben + verschlüsseln
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 7a55dd13c3..4eb7557522 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -961,5 +961,7 @@
- Ninguno
- Sólo firmar con PGP
- Firmar y cifrar con PGP
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-eu-rES/strings.xml b/app/src/main/res/values-eu-rES/strings.xml
index 1aff7718e7..070226b717 100644
--- a/app/src/main/res/values-eu-rES/strings.xml
+++ b/app/src/main/res/values-eu-rES/strings.xml
@@ -961,5 +961,7 @@
- Bat ere ez
- PGP sinadura soilik
- PGP sinadura eta enkriptatzea
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index 60db712a4c..9427ddf3f0 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -961,5 +961,7 @@
- Ei mitään
- PGP vain allekirjoita
- PGP allekirjoita+salaa
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml
index a7e3853e7c..b7dfa3cf5e 100644
--- a/app/src/main/res/values-fr-rFR/strings.xml
+++ b/app/src/main/res/values-fr-rFR/strings.xml
@@ -961,5 +961,7 @@
- Aucun
- Signer PGP uniquement
- Signer & chiffrer PGP
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml
index 0aefa04bd1..a623572f2c 100644
--- a/app/src/main/res/values-it-rIT/strings.xml
+++ b/app/src/main/res/values-it-rIT/strings.xml
@@ -960,5 +960,7 @@
- Nulla
- PGP solo firma
- PGP firma e crittografia
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
index 69309bb6e8..74c86dedeb 100644
--- a/app/src/main/res/values-nl-rNL/strings.xml
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -958,5 +958,7 @@
- Geen
- PGP teken-alleen
- PGP teken+versleutel
+ - S/MIME teken-alleen
+ - S/MIME teken+versleutel
diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml
index 374a70c4cc..9e7de4170b 100644
--- a/app/src/main/res/values-nn-rNO/strings.xml
+++ b/app/src/main/res/values-nn-rNO/strings.xml
@@ -961,5 +961,7 @@
- Ingen
- Bare PGP-signering
- PGP signer+krypter
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml
index 374a70c4cc..9e7de4170b 100644
--- a/app/src/main/res/values-no-rNO/strings.xml
+++ b/app/src/main/res/values-no-rNO/strings.xml
@@ -961,5 +961,7 @@
- Ingen
- Bare PGP-signering
- PGP signer+krypter
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml
index b2ceaaf84f..0a2f0b4a8d 100644
--- a/app/src/main/res/values-ro-rRO/strings.xml
+++ b/app/src/main/res/values-ro-rRO/strings.xml
@@ -970,5 +970,7 @@
- Fără
- Doar semnare PGP
- Semnare+criptare PGP
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml
index 3c1df79db7..fa924932e7 100644
--- a/app/src/main/res/values-ru-rRU/strings.xml
+++ b/app/src/main/res/values-ru-rRU/strings.xml
@@ -983,5 +983,7 @@
- Ничего
- Только подпись PGP
- Подпись+шифрование PGP
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml
index b551746f30..cf53a2a40c 100644
--- a/app/src/main/res/values-tr-rTR/strings.xml
+++ b/app/src/main/res/values-tr-rTR/strings.xml
@@ -961,5 +961,7 @@
- Hiçbiri
- PGP sadece imzala
- PGP imzala ve şifrele
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 05d6647a84..76f903d69a 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -948,5 +948,7 @@ FairEmail会显示一条消息来提醒您这一点,这条消息在您购买
- 无
- 仅PGP签名
- PGP 签名+加密
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e35cc036fd..1cdf10e74b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1159,12 +1159,16 @@
- None
- PGP sign-only
- PGP sign+encrypt
+ - S/MIME sign-only
+ - S/MIME sign+encrypt
- 0
- 2
- 1
+ - 4
+ - 3
17BA15C1AF55D925F98B99CEA4375D4CDF4C174B