Skip to main content

This content has been archived and is no longer being updated. Links may not function; however, this content may be relevant to outdated versions of the product.

Support Article

Cannot open encrypted emails-Your digital ID cannot be found

SA-18689

Summary



Emails encrypted and sent by Pega cannot be viewed. The instructions given in the PDN article were followed. The link to the PDN article is given below.
https://pdn.pega.com/how-send-encrypted-email
The Email Recipient’s Certificate, needed to encrypt the outgoing message, is uploaded to Pega within a JKS keystore rule.
The Email Recipient has installed the corresponding Key Pair needed to decrypt the incoming email on Email Client.
The Certificate was referenced correctly in Data-Admin-Security-Keystore.pyEmailCertificateMap decision table rule.
However, there were no issues with non-encrypted emails sent by Pega.

Error Messages



The PegaRules logs display an error similar to this:
//
294 [http-bio-7170-exec-1] [  STANDARD] [                     ] [   GCSAppli:01.01.01] (Admin_Security_Keystore.Action) ERROR prpc_host|xx.xx.xx.xx Admin@gcs - getCertificate: Could not get certificate and key from keystore:
 
java.lang.NullPointerException
//

Microsoft Outlook displays the following error when the encrypted Email is opened:




Mozilla Thunderbird displays the following error when the encrypted Email is opened:




Steps to Reproduce




Assume that an encrypted email is sent using Pega from ‘alice@<mailhost>’ to ‘bob@<mailhost>’.
  1. Obtain Bob’s Certificate.
  2. Create a new Java Keystore File (JKS).
  3. Import Bob’s Certificate into the JKS file, and make a note of the Certificate Alias.
  4. Save the JKS, and set a password.
  5. Create a new Pega Keystore Rule by navigating to Create>Security>Keystore.
  6. Upload the JKS file to the Pega Keystore Rule, set the password, and save the rule.
  7. Create pyEmailCertificateMap decision table rule in Data-Admin-Security-Keystore class in a local ruleset by navigating to Create>Decision>Decision Table.
  • Enter Bob’s email address under Email Address column.
  • Enter the name of Pega Keystore rule under Keystore Instance column.
  • Enter the name of Certificate Alias under Keystore Alias column.
  1. Create a Wrapper Activity to call SendEmailNotification out-of-the-box activity. Setup requires parameters for sending email from Alice to Bob (see below), and ensure EncryptMessage is checked.
  2. Run the Activity to send a test email.
  3. Log into Email Client (as Bob). The received Email Message cannot be decrypted.
Note: It is assumed that the correct Private Key has been installed on the Email Client.

Example Rule Screenshots are given below.








Root Cause



There is an issue within the Activity “Data-Admin-Security-Keystore.getCertificate|Pega-IntegrationEngine:07-10-01” : wherein the required Certificate is not returned to the calling ‘SendEmailNotification’.
There is a secondary issue in that despite the failure of the ‘getCertificate’ to obtain the certificate, the ‘SendEmailNotification’ still sends out an email – but the end-user will not be able to decrypt it.

Resolution



Perform the following local-changes:
Copy getCertificate activity to a Local unlocked Ruleset and make the following changes.









1. Add in a Debug logging statement to call out the local-change. Add SendToTracer as well.




2. Add in a WHEN/JUMP step around a Log-Message step to exit the Activity if the Keystore Name cannot be found.






Ensure Enable conditions are checked for When and Jump.

3. Create a new page, OBJ-OPEN the Keystore:

Note the presence of a Jump here, ensure it is enabled (and set up the corresponding Get label later).



4. An error handler for bad keystore:

This is jumped over by Step 3 unless there is a problem with the OBJ-OPEN.

It is a Java Step comprised of the following code, and this will terminate the Activity if the keystore cannot be opened.

---cut here---
// need to wrap the Exception throw in order for the Activity to compile
if (true)
{
    throw new PRRuntimeException("Unable to open Keystore record called " + tools.getParamValue("KeystoreInstance"));
}
---cut here---

5. This step looks for the certificate in the keystore.

Ensure its label is set to Get.
The Java code is given below.

---cut here---
String keystoreFile = myStepPage.getString("pyFileSource");
String keystorePassword = myStepPage.getString("pyKeystorePassword");
final String keystoreRecordName = myStepPage.getString("pyKeystore");
final String aliasProvided = tools.getParamValue("KeystoreAlias");
String keyAlias = aliasProvided;
String certAlias = aliasProvided;
String type = myStepPage.getString("pyKeystoreType");

final ParameterPage pp = tools.getParameterPage();

// objects returned
java.security.Key myKey = null;
java.security.cert.Certificate [] certChain = null;

if (keystoreFile.length() < 1)
{
    throw new PRRuntimeException("getCertificate: No Keystore has been uploaded to " + keystoreRecordName + "Keystore.");
}

byte[] buffer = Base64Util.decodeToByteArray(keystoreFile);
java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(buffer);

//get certificate
try

    /// Adds the BouncyCastle provider.
    // Doesn't matter if the provider has been added before, will not add duplicates
    try
    {
        java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }
    catch (Exception e) 
    {
        oLog.error("getCertificate: Failed adding BouncyCastle Security provider: \n" + e.toString());
        throw e;
    }
    
    /// Load keyStore file
    java.security.KeyStore keyStore = null;
    char keyStorePassArray [] = null;
    try
    {
        if (type.equals("jks") || type.equals("JKS"))
        {
            keyStore = java.security.KeyStore.getInstance("JKS");
        }else 
        {
            if (oLog.isDebugEnabled())
            {
                oLog.debug("assuming non-JKS keystore is PKCS12");
            }
            keyStore = java.security.KeyStore.getInstance("PKCS12", "BC");
        }
        
        if (keystorePassword.length() > 0)
        {
            if (oLog.isDebugEnabled())
            {
                oLog.debug("decrypting keystore password");
            }
            keyStorePassArray = tools.getPRCrypto().decrypt(keystorePassword).toCharArray();
        }
        keyStore.load(bis, keyStorePassArray);
    }catch (Exception e) 
    {
        oLog.error("getCertificate: Unable to instantiate Java Keystore from record: " + keystoreRecordName + ": \n" + e.toString());
        throw e;
    }

    if (keyStore == null)
    {
        throw new PRRuntimeException("getCertificate: Unable to instantiate Java Keystore from record: " + keystoreRecordName);
    }
    
    // Search for Key (Pair) first, certificate second.
    // Select the first available Key in the keystore if no alias provided
    java.util.Enumeration en = keyStore.aliases();
    if (! en.hasMoreElements() )
    {
        throw new PRRuntimeException("getCertificate: Keystore in record is empty: " + keystoreRecordName);
    }
    
    while(keyAlias.length() < 1 && en.hasMoreElements())
    {
        String elementName = en.nextElement().toString();
        if(! keyStore.isKeyEntry(elementName) )
        {  
            // is probably a cert entry - not a key
            continue;
        }
        
        if (oLog.isInfoEnabled())
        {
            oLog.info("Using Key with Alias: " + elementName);
        }
        keyAlias = elementName;
    }
    
    if(keyAlias.length() < 1)
    {
        oLog.warn("getCertificate: No suitable Key found in KeyStore: " + keystoreRecordName);
    }
    else
    {
        oLog.debug("Attempting to key with alias: " + keyAlias);
        
        // extract the key from the keystore
        try
        {
            myKey = keyStore.getKey(keyAlias, keyStorePassArray);
        }catch( Exception e)
        {
            oLog.error("Exception in getCertificate: Could not extract Key \"" + keyAlias + "\" from KeyStore \"" + keystoreRecordName + "\": \n" + e.toString());
            throw e;
        }
        
        if (myKey != null)
        {
            if (oLog.isInfoEnabled())
            {
                oLog.info("\tKey Algorithm: " + myKey.getAlgorithm());
                oLog.info("\tKey Format: " + myKey.getFormat());
            }
            
            certAlias = keyAlias;
        }
    }
    
    // make sure certAlias is non-empty
    java.util.Enumeration allAliases = keyStore.aliases();
    while(certAlias.length() < 1 && allAliases.hasMoreElements())
    {
        String elementName = allAliases.nextElement().toString();
        if(! keyStore.isCertificateEntry(elementName))
        {  
            // is probably a cert entry - not a key
            continue;
        }
        
        if (oLog.isInfoEnabled())
        {
            oLog.info("Using Cert with Alias: " + elementName);
        }
        certAlias = elementName;
    }
    
    //  get the certificate array out of the keystore
    certChain = keyStore.getCertificateChain(certAlias);
    
    if (certChain == null)
    {
        // get the single certificate with this alias, construct our own array
        java.security.cert.Certificate cert = keyStore.getCertificate(certAlias);
        if (cert != null)
        {
            certChain = new java.security.cert.Certificate[1];
            certChain[0] = cert;
        }
       else { throw new PRRuntimeException("No Certificate with an alias of '"+keyAlias+"' found in keystore; cannot encrypt" ); }
    }
    
    // add certificate chain and key to parameter page
    pp.putObject("CertificateKey", myKey);
    pp.putObject("CertificateChain", certChain);
    
}
catch (PRRuntimeException pegaException)
{
    throw pegaException;
}
catch (Exception e) 
{
    throw new PRRuntimeException("getCertificate: Could not get certificate and/or key from keystore: \n", e);
}
---cut here---


NOTE: In case Certificate cannot be found, the Activity does not terminate.
It will instead report a (WARN) message to the logs and return a NULL pointer for the CERT.
In this scenario, a corrupt email will still be sent out. This behavior cannot be modified with this local-change.

This issue is due to defects in Pegasystems’ code or rules.

BUG-230566 "getCertificate Activity NPEs instead of errors"
BUG-230715 "Pega attempts to encrypt email with bad keystore, sends corrupt"

These BUGS will be addressed in a future version of PRPC.

Published February 25, 2016 - Updated October 8, 2020

Was this useful?

0% found this useful

Have a question? Get answers now.

Visit the Collaboration Center to ask questions, engage in discussions, share ideas, and help others.

Did you find this content helpful?

Want to help us improve this content?

We'd prefer it if you saw us at our best.

Pega Community has detected you are using a browser which may prevent you from experiencing the site as intended. To improve your experience, please update your browser.

Close Deprecation Notice
Contact us