Added support for S/MIME auth-enveloped-data

master
M66B 2 weeks ago
parent 3379062acf
commit 0662e46ffb

@ -68,7 +68,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@ -153,14 +152,21 @@ import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1SequenceParser;
import org.bouncycastle.asn1.ASN1StreamParser;
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.cms.ContentInfoParser;
import org.bouncycastle.asn1.cms.Time;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSAuthEnvelopedDataParser;
import org.bouncycastle.cms.CMSEnvelopedDataParser;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
@ -169,11 +175,13 @@ import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.PKIXRecipientId;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransAuthEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipient;
import org.bouncycastle.operator.DefaultAlgorithmNameFinder;
@ -10603,17 +10611,46 @@ public class FragmentMessages extends FragmentBase
if (input == null)
throw new IllegalArgumentException("Encrypted message missing");
// Do not trust only the smime-type MIME parameter: it is a hint and
// is not retained in EntityAttachment.type. Detect the actual CMS
// content type from the ContentInfo OID.
ASN1ObjectIdentifier contentType = getCmsContentType(input);
boolean authEnveloped = CMSObjectIdentifiers.authEnvelopedData.equals(contentType);
if (!authEnveloped && !CMSObjectIdentifiers.envelopedData.equals(contentType))
throw new CMSException("Unsupported encrypted CMS content type: " + contentType);
int count = -1;
boolean decoded = false;
Throwable last = null;
while (!decoded)
try (FileInputStream fis = new FileInputStream(input)) {
// Create parser
// AuthEnvelopedData uses an AEAD-capable parser and recipient.
RecipientInformationStore recipientStore;
AlgorithmIdentifier contentAlgorithm;
String contentAlgorithmOid;
String parserClass;
JceKeyTransRecipient recipient;
if (authEnveloped) {
CMSAuthEnvelopedDataParser authData = new CMSAuthEnvelopedDataParser(fis);
recipientStore = authData.getRecipientInfos();
contentAlgorithm = authData.getEncryptionAlgOID();
contentAlgorithmOid = authData.getEncAlgOID();
parserClass = authData.getClass().getName();
recipient = new JceKeyTransAuthEnvelopedRecipient(privkey);
} else {
CMSEnvelopedDataParser envelopedData = new CMSEnvelopedDataParser(fis);
recipientStore = envelopedData.getRecipientInfos();
contentAlgorithm = envelopedData.getContentEncryptionAlgorithm();
contentAlgorithmOid = envelopedData.getEncryptionAlgOID();
parserClass = envelopedData.getClass().getName();
recipient = new JceKeyTransEnvelopedRecipient(privkey);
}
// Get recipient info
JceKeyTransRecipient recipient = new JceKeyTransEnvelopedRecipient(privkey);
Collection<RecipientInformation> recipients = envelopedData.getRecipientInfos().getRecipients(); // KeyTransRecipientInformation
Collection<RecipientInformation> recipients =
recipientStore.getRecipients(); // KeyTransRecipientInformation
EntityLog.log(context, "s/mime private key" +
" algo=" + privkey.getAlgorithm() +
@ -10644,14 +10681,15 @@ public class FragmentMessages extends FragmentBase
String malgo;
try {
DefaultAlgorithmNameFinder af = new DefaultAlgorithmNameFinder();
malgo = af.getAlgorithmName(envelopedData.getContentEncryptionAlgorithm());
malgo = af.getAlgorithmName(contentAlgorithm);
} catch (Throwable ex) {
Log.e(ex);
malgo = envelopedData.getEncryptionAlgOID();
malgo = contentAlgorithmOid;
}
EntityLog.log(context, "s/mime message" +
" type=" + contentType +
" algo=" + malgo +
" class=" + envelopedData.getClass());
" class=" + parserClass);
// Find recipient
if (count < 0) {
@ -10938,6 +10976,20 @@ public class FragmentMessages extends FragmentBase
Log.unexpectedError(getParentFragmentManager(), ex);
}
private ASN1ObjectIdentifier getCmsContentType(File file)
throws IOException, CMSException {
try (InputStream is =
new BufferedInputStream(new FileInputStream(file))) {
ASN1Encodable object = new ASN1StreamParser(is).readObject();
if (!(object instanceof ASN1SequenceParser))
throw new CMSException("Invalid CMS ContentInfo");
ContentInfoParser contentInfo =
new ContentInfoParser((ASN1SequenceParser) object);
return contentInfo.getContentType();
}
}
private void decodeMessage(Context context, InputStream is, EntityMessage message, Bundle args) throws MessagingException, IOException {
int type = args.getInt("type");
String alias = args.getString("alias");

@ -5271,6 +5271,10 @@ public class MessageHelper {
if ("enveloped-data".equalsIgnoreCase(smimeType)) {
getMessageParts(null, part, parts, EntityAttachment.SMIME_MESSAGE);
return parts;
} else if ("authEnveloped-data".equalsIgnoreCase(smimeType) ||
"auth-enveloped-data".equalsIgnoreCase(smimeType)) {
getMessageParts(null, part, parts, EntityAttachment.SMIME_MESSAGE);
return parts;
} else if ("signed-data".equalsIgnoreCase(smimeType)) {
getMessageParts(null, part, parts, EntityAttachment.SMIME_SIGNED_DATA);
return parts;

Loading…
Cancel
Save