@ -109,11 +109,19 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar ;
import com.sun.mail.util.FolderClosedIOException ;
import org.bouncycastle.cert.X509CertificateHolder ;
import org.bouncycastle.cms.CMSEnvelopedData ;
import org.bouncycastle.cms.CMSProcessable ;
import org.bouncycastle.cms.CMSProcessableFile ;
import org.bouncycastle.cms.CMSSignedData ;
import org.bouncycastle.cms.KeyTransRecipientInformation ;
import org.bouncycastle.cms.RecipientInformation ;
import org.bouncycastle.cms.SignerInformation ;
import org.bouncycastle.cms.SignerInformationStore ;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder ;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient ;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipient ;
import org.bouncycastle.util.Store ;
import org.jsoup.nodes.Document ;
import org.jsoup.nodes.Element ;
import org.openintents.openpgp.OpenPgpError ;
@ -131,6 +139,9 @@ import java.io.InputStream;
import java.io.OutputStream ;
import java.nio.charset.StandardCharsets ;
import java.security.PrivateKey ;
import java.security.PublicKey ;
import java.security.cert.CertificateException ;
import java.security.cert.X509Certificate ;
import java.text.Collator ;
import java.text.DateFormat ;
import java.text.NumberFormat ;
@ -3904,7 +3915,8 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
boolean auto = intent . getBooleanExtra ( "auto" , false ) ;
int type = intent . getIntExtra ( "type" , EntityMessage . ENCRYPT_NONE ) ;
if ( EntityMessage . SMIME_SIGNENCRYPT . equals ( type ) ) {
if ( EntityMessage . SMIME_SIGNONLY . equals ( type ) | |
EntityMessage . SMIME_SIGNENCRYPT . equals ( type ) ) {
final Bundle args = new Bundle ( ) ;
args . putLong ( "id" , id ) ;
args . putInt ( "type" , type ) ;
@ -4170,6 +4182,9 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
out = new FileOutputStream ( plain ) ;
} else if ( EntityAttachment . PGP_SIGNATURE . equals ( attachment . encryption ) ) {
if ( ! attachment . available )
throw new IllegalArgumentException ( context . getString ( R . string . title_attachments_missing ) ) ;
File file = attachment . getFile ( context ) ;
byte [ ] signature = new byte [ ( int ) file . length ( ) ] ;
try ( FileInputStream fis = new FileInputStream ( file ) ) {
@ -4341,7 +4356,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
}
private void onSmime ( Bundle args ) {
new SimpleTask () {
new SimpleTask <Boolean > () {
@Override
protected Boolean onExecute ( Context context , Bundle args ) throws Throwable {
long id = args . getLong ( "id" ) ;
@ -4351,12 +4366,69 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
if ( alias = = null )
throw new IllegalArgumentException ( "Key alias missing" ) ;
PrivateKey pk = KeyChain . getPrivateKey ( context , alias ) ;
if ( pk = = null )
throw new IllegalArgumentException ( "Private key missing" ) ;
DB db = DB . getInstance ( context ) ;
if ( EntityMessage . SMIME_SIGNONLY . equals ( type ) ) {
// Check public key
X509Certificate [ ] chain = KeyChain . getCertificateChain ( context , alias ) ;
if ( chain = = null | | chain . length = = 0 )
throw new IllegalArgumentException ( "Public key missing" ) ;
// Get content/signature
File content = null ;
File signature = null ;
List < EntityAttachment > attachments = db . attachment ( ) . getAttachments ( id ) ;
for ( EntityAttachment attachment : attachments )
if ( EntityAttachment . SMIME_SIGNATURE . equals ( attachment . encryption ) )
signature = attachment . getFile ( context ) ;
else if ( EntityAttachment . SMIME_CONTENT . equals ( attachment . encryption ) )
content = attachment . getFile ( context ) ;
if ( content = = null )
throw new IllegalArgumentException ( "Signed content missing" ) ;
if ( signature = = null )
throw new IllegalArgumentException ( "Signature missing" ) ;
// Build signed data
CMSProcessable signedContent = new CMSProcessableFile ( content ) ;
FileInputStream fis = new FileInputStream ( signature ) ;
CMSSignedData signedData = new CMSSignedData ( signedContent , fis ) ;
// Check signature
Store store = signedData . getCertificates ( ) ;
SignerInformationStore signerInfos = signedData . getSignerInfos ( ) ;
for ( SignerInformation signer : signerInfos . getSigners ( ) )
for ( Object cert : store . getMatches ( signer . getSID ( ) ) ) {
X509CertificateHolder certHolder = ( X509CertificateHolder ) cert ;
if ( signer . verify ( new JcaSimpleSignerInfoVerifierBuilder ( ) . build ( certHolder ) ) ) {
// Check validity
Date now = new Date ( ) ;
boolean valid ;
try {
chain [ 0 ] . checkValidity ( now ) ;
valid = certHolder . isValidOn ( now ) ;
} catch ( CertificateException ignored ) {
valid = false ;
}
// Check public key
PublicKey pubkey = chain [ 0 ] . getPublicKey ( ) ;
if ( valid & &
signer . verify ( new JcaSimpleSignerInfoVerifierBuilder ( ) . build ( pubkey ) ) )
return true ;
else
return null ;
}
}
return false ;
} else {
// Check private key
PrivateKey privkey = KeyChain . getPrivateKey ( context , alias ) ;
if ( privkey = = null )
throw new IllegalArgumentException ( "Private key missing" ) ;
// Get encrypted message
File input = null ;
List < EntityAttachment > attachments = db . attachment ( ) . getAttachments ( id ) ;
for ( EntityAttachment attachment : attachments )
@ -4367,12 +4439,14 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
if ( input = = null )
throw new IllegalArgumentException ( "Encrypted message missing" ) ;
// Build enveloped data
FileInputStream fis = new FileInputStream ( input ) ;
CMSEnvelopedData envelopedData = new CMSEnvelopedData ( fis ) ;
// Decrypt message
Collection < RecipientInformation > recipients = envelopedData . getRecipientInfos ( ) . getRecipients ( ) ;
KeyTransRecipientInformation recipientInfo = ( KeyTransRecipientInformation ) recipients . iterator ( ) . next ( ) ;
JceKeyTransRecipient recipient = new JceKeyTransEnvelopedRecipient ( p k) ;
JceKeyTransRecipient recipient = new JceKeyTransEnvelopedRecipient ( p riv key ) ;
InputStream is = recipientInfo . getContentStream ( recipient ) . getContentStream ( ) ;
// Decode message
@ -4414,12 +4488,20 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
db . endTransaction ( ) ;
}
return true ;
return null ;
}
}
@Override
protected void onExecuted ( Bundle args , Object data ) {
protected void onExecuted ( Bundle args , Boolean result ) {
int type = args . getInt ( "type" ) ;
if ( EntityMessage . SMIME_SIGNONLY . equals ( type ) )
if ( result = = null )
Snackbar . make ( view , R . string . title_signature_unconfirmed , Snackbar . LENGTH_LONG ) . show ( ) ;
else if ( result )
Snackbar . make ( view , R . string . title_signature_valid , Snackbar . LENGTH_LONG ) . show ( ) ;
else
Snackbar . make ( view , R . string . title_signature_invalid , Snackbar . LENGTH_LONG ) . show ( ) ;
}
@Override