S/MIME sign+encrypt

This commit is contained in:
M66B 2019-12-04 08:34:59 +01:00
parent 886ad067e4
commit c28a786777
1 changed files with 100 additions and 71 deletions

View File

@ -105,7 +105,6 @@ import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
@ -154,6 +153,7 @@ import java.util.Objects;
import java.util.Properties;
import java.util.regex.Pattern;
import javax.activation.DataHandler;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.MessageRemovedException;
@ -166,7 +166,9 @@ import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.ParseException;
import javax.mail.util.ByteArrayDataSource;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
@ -1881,7 +1883,7 @@ public class FragmentCompose extends FragmentBase {
attachments.remove(attachment);
}
// Build message
// Build message to sign
Properties props = MessageHelper.getSessionProperties();
Session isession = Session.getInstance(props, null);
MimeMessage imessage = new MimeMessage(isession);
@ -1906,7 +1908,6 @@ public class FragmentCompose extends FragmentBase {
};
bpContent.setContent(imessage.getContent(), imessage.getContentType());
if (EntityMessage.SMIME_SIGNONLY.equals(type)) {
if (alias == null)
throw new IllegalArgumentException("Key alias missing");
@ -1919,6 +1920,7 @@ public class FragmentCompose extends FragmentBase {
throw new IllegalArgumentException("Certificate missing");
// Build content
if (EntityMessage.SMIME_SIGNONLY.equals(type)) {
EntityAttachment cattachment = new EntityAttachment();
cattachment.message = draft.id;
cattachment.sequence = db.attachment().getAttachmentSequence(draft.id) + 1;
@ -1934,6 +1936,7 @@ public class FragmentCompose extends FragmentBase {
}
db.attachment().setDownloaded(cattachment.id, content.length());
}
// Sign
Store store = new JcaCertStore(Arrays.asList(chain[0]));
@ -1948,10 +1951,15 @@ public class FragmentCompose extends FragmentBase {
.build(contentSigner, chain[0]);
cmsGenerator.addSignerInfoGenerator(signerInfoGenerator);
CMSTypedData cmsData = new CMSProcessableFile(content);
ByteArrayOutputStream osContent = new ByteArrayOutputStream();
bpContent.writeTo(osContent);
CMSTypedData cmsData = new CMSProcessableByteArray(osContent.toByteArray());
CMSSignedData cmsSignedData = cmsGenerator.generate(cmsData, true);
byte[] signedMessage = cmsSignedData.getEncoded();
// Build signature
if (EntityMessage.SMIME_SIGNONLY.equals(type)) {
ContentType ct = new ContentType("application/pkcs7-signature");
ct.setParameter("micalg", "sha-256");
@ -1970,7 +1978,10 @@ public class FragmentCompose extends FragmentBase {
}
db.attachment().setDownloaded(sattachment.id, file.length());
} else if (EntityMessage.SMIME_SIGNENCRYPT.equals(draft.encrypt)) {
return null;
}
// Get recipient
if (draft.to == null || draft.to.length != 1)
throw new IllegalArgumentException(getString(R.string.title_to_missing));
@ -1985,14 +1996,33 @@ public class FragmentCompose extends FragmentBase {
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(encoded));
// Build signature
BodyPart bpSignature = new MimeBodyPart();
bpSignature.setFileName("smime.p7s");
bpSignature.setDataHandler(new DataHandler(new ByteArrayDataSource(signedMessage, "application/pkcs7-signature")));
bpSignature.setDisposition(Part.INLINE);
// Build message
ContentType ct = new ContentType("multipart/signed");
ct.setParameter("micalg", "sha-256");
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);
imessage.saveChanges();
// Encrypt
CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator = new CMSEnvelopedDataGenerator();
RecipientInfoGenerator gen = new JceKeyTransRecipientInfoGenerator(cert);
cmsEnvelopedDataGenerator.addRecipientInfoGenerator(gen);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bpContent.writeTo(bos);
CMSTypedData msg = new CMSProcessableByteArray(bos.toByteArray());
ByteArrayOutputStream osMessage = new ByteArrayOutputStream();
imessage.writeTo(osMessage);
CMSTypedData msg = new CMSProcessableByteArray(osMessage.toByteArray());
OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC)
.build();
@ -2008,13 +2038,12 @@ public class FragmentCompose extends FragmentBase {
attachment.encryption = EntityAttachment.SMIME_MESSAGE;
attachment.id = db.attachment().insertAttachment(attachment);
File file = attachment.getFile(context);
try (OutputStream os = new FileOutputStream(file)) {
File encrypted = attachment.getFile(context);
try (OutputStream os = new FileOutputStream(encrypted)) {
cmsEnvelopedData.toASN1Structure().encodeTo(os);
}
db.attachment().setDownloaded(attachment.id, file.length());
}
db.attachment().setDownloaded(attachment.id, encrypted.length());
return null;
}