Enterprise Resource Planning Blogs by Members
Gain new perspectives and knowledge about enterprise resource planning in blog posts from community members. Share your own comments and ERP insights today!
cancel
Showing results for 
Search instead for 
Did you mean: 
DenizZilyas
Participant
2,409

Introduction:

The purpose of this blog : To identify certificates in the keystore with less than 30 days remaining until expiration and dynamically update all expiring certificates and send the updated certificate via email.

Prerequired:

SAP BTP and Integration Suite capabilities Cloud Integration.

Cloud Integration Steps:

Firstly, I can trigger the process with a CPI service using POSTMAN or run it based on a time interval with a timer. I will proceed with the timer approach. If you prefer, you can set it to check every 15 days.

Under normal circumstances, we check for expired certificates either when an error occurs in the integration or when we feel the need to verify their expiration status. Our goal is to have one or multiple certificates loaded into the keystore automatically updated at a predetermined time, ensuring that our processes no longer encounter errors due to certificate expiration.

DenizZilyas_0-1722814113334.png

 

DenizZilyas_1-1722814132095.png

 

 

The API we will use: https://{Account Short Name}-tmn.{SSL Host}.{region}.hana.ondemand.com/api/v1/KeystoreEntries

AND

 https://{Account Short Name}-tmn.{SSL Host}.{region}.hana.ondemand.com/api/v1/CertificateResources

1-CM_Headers

Our service works with a CSRF token, so we need to obtain the token first. In the initial content modifier, we store the request headers of the service.

DenizZilyas_2-1722601912045.png

2-RR_FetchToken

A defined user must access CPI as an authorized user. After defining security materials, credentials are provided in the flow.

DenizZilyas_0-1722810045388.png

3-RR_KeystoreEntries

DenizZilyas_1-1722810078553.png

DenizZilyas_2-1722810099489.png

4-MM_KeystoreEntries

DenizZilyas_3-1722810241143.png

The left side of the mapping is the XSD we created from the KeystoreEntries OData service. I previously shared information about which fields should be selected. The right side of the mapping is the XSD structure I want to output.

 

 

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
	<xs:element name="RootExpiring">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="RootExpiring" maxOccurs="unbounded" minOccurs="0">
					<xs:complexType>
						<xs:sequence>
							<xs:element type="xs:string" name="CertificateNumber" maxOccurs="unbounded" minOccurs="0"/>
							<xs:element type="xs:string" name="CertificateName" maxOccurs="unbounded" minOccurs="0"/>
							<xs:element type="xs:short" name="Validation" maxOccurs="unbounded" minOccurs="0"/>
							<xs:element type="xs:string" name="Type" maxOccurs="unbounded" minOccurs="0"/>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

 

 

DenizZilyas_4-1722810284829.png

The Groovy script I use to identify certificates with less than 30 days remaining:

 

 

def String get30DaysAfter(String arg1){
    
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
        Date dateNow= new Date()+30

    	return dateFormat.format(dateNow)+"" 
}

 

 

5-Router

If there is no certificate that will expire after the mapping, the 'no update' will be observed and the flow will be escalated.

//RootExpiring = ''

DenizZilyas_5-1722810402129.png

6- CM_EscaleteProcess

DenizZilyas_6-1722810460248.png

7-Iterating Splitter

In order to update multiple certificates, we need to split them based on RootExpiring. We will send them to the service one by one.

 

 

<RootExpiring>
	<RootExpiring>
		<CertificateNumber>x1</CertificateNumber>
		<CertificateName>a</CertificateName>
		<Validation>2024-02-21</Validation>
		<Type>Certificate</Type>
	</RootExpiring>
</RootExpiring>
<RootExpiring>
	<CertificateNumber>x2</CertificateNumber>
	<CertificateName>b</CertificateName>
	<Validation>2024-02-21</Validation>
	<Type>Certificate</Type>
</RootExpiring>

 

 

DenizZilyas_7-1722810501739.png

8-CM_Cert_Inf

It will take the host address from the certificateName field. Therefore, when importing a certificate, we need to give the host address as the alias name.

DenizZilyas_8-1722810562437.png

9-GS_Encoded

 

 

import java.security.cert.X509Certificate
import java.util.Base64
import javax.net.ssl.SSLPeerUnverifiedException
import javax.net.ssl.SSLSession
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import com.sap.gateway.ip.core.customdev.util.Message

def processData(Message message) {
    try {
        // Get host and port from message properties
        def host = message.getProperty("host") as String
        def port = message.getProperty("port") as Integer

        def factory = SSLSocketFactory.getDefault() as SSLSocketFactory
        def socket = factory.createSocket(host, port) as SSLSocket

        def session = socket.getSession()
        X509Certificate cert = session.peerCertificates[0] as X509Certificate

        def systemDomainName = cert.issuerDN.name 
        def encodedDERValue = Base64.getEncoder().encodeToString(cert.encoded)

        // Set Properties
        message.setProperty("systemDomainName", systemDomainName)
        message.setProperty("encodedDERValue", encodedDERValue)

        return message
    } catch (SSLPeerUnverifiedException e) {
        throw new Exception("${host} did not present a valid cert.")
    }
}

 

 

10-CM_EncodedBody

DenizZilyas_9-1722810939701.png

DenizZilyas_10-1722810964505.png

11-RR_CertificateResources

fingerprintVerified=true:

The true value indicates that the certificate fingerprint has been verified and is valid.

returnKeystoreEntries=false:

This setting determines whether keystore entries will be returned during a process or query. A false value means that the keystore records will not be returned as a result of the operation.

update=true:

if you want to update a certificate or configuration, this setting is set to true.

DenizZilyas_11-1722811085021.png

The CertificateNumber value we keep in the property was mapped with the hexalias  field in the mapping. This way, we can obtain the hexalias values resulting from the mapping.

In the next step, there is a classic logging Groovy script.

12-Iterating Splitter

In the next step, I will adjust the fields in the latest build to format my mail properly.

I don't want the RooxExpiring field, which contains unwanted tags in the XML produced from the process call and my mail format. Therefore, I used a splitter and converted the XML to JSON.

DenizZilyas_12-1722811676028.png

Input: 

 

 

<RootExpiring>
	<RootExpiring>
		<CertificateNumber>x1</CertificateNumber>
		<CertificateName>a</CertificateName>
		<Validation>2024-02-21</Validation>
		<Type>Certificate</Type>
	</RootExpiring>
</RootExpiring>
</RootExpiring>

 

 

Output After XML to JSON Converter:

 

 

{
	"CertificateNumber": "x1",
	"CertificateName": "a",
	"Validation": "2024-02-21",
	"Type": "Certificate"
}

 

 

13-GS_MailBody

 

 

import com.sap.gateway.ip.core.customdev.util.Message;
import groovy.json.JsonSlurper;

def Message processData(Message message) {
    // JSON body
    def body = message.getBody(java.lang.String) as String;
    
    // Parse JSON
    def jsonSlurper = new JsonSlurper();
    def jsonObj = jsonSlurper.parseText(body);

    // Check if JSON parsing is successful
    if (!jsonObj || !jsonObj.CertificateNumber || !jsonObj.CertificateName || !jsonObj.Validation || !jsonObj.Type) {
        message.setBody("Error: JSON parsing failed or JSON structure is incorrect.");
        return message;
    }

    // Build HTML table row
    def tableRow = """
        <tr>
            <td>${jsonObj.CertificateNumber}</td>
            <td>${jsonObj.CertificateName}</td>
            <td>${jsonObj.Validation}</td>
            <td>${jsonObj.Type}</td>
            
        </tr>
    """

    // Set the HTML body
    def htmlBody = """
        <html>
        <head>
        <style>
            table {
                width: 100%;
                border-collapse: collapse;
            }
            th, td {
                padding: 12px;
                text-align: left;
                border: 1px solid #ddd;
            }
            th {
                background-color: #f2f2f2;
            }
            tbody tr:nth-child(even) {
                background-color: #f9f9f9;
            }
            p {
                color: #000000;
                font-size: 16px;
            }
            h2 {
                color: orange;
            }
        </style>
        </head>
        <body>
            <p><i>Hi there!</i></p>
            <p><i>This email is a validity notification for the certificate details below. Please review the certificate information.</i></p>
            <br>
            <h2>Certificate Information</h2>
            <table>
                <thead>
                    <tr>
                        <th>Certificate Number</th>
                        <th>Certificate Name</th>
                        <th>Validation</th>
                        <th>Type</th>
                    </tr>
                </thead>
                <tbody>
                    ${tableRow}
                </tbody>
            </table>
            <br>
            <p><i>This mail is automatically generated. If you do not receive it properly, please contact your administrator.</i></p>
            <br>
            <i>Thanks & Regards,</i>
            <br>
        </body>
        </html>
    """

    // Set the transformed body
    message.setBody(htmlBody);
    return message;
}

 

 

14-Mail Adapter

DenizZilyas_13-1722811959167.png

DenizZilyas_14-1722811973946.png

Let’s give it a try.

There are two expired certificates.

Overview--> Manage Keystore

DenizZilyas_0-1722812264258.png

DenizZilyas_1-1722812297381.png

Deployed flow and Let's check the keystore again.

DenizZilyas_2-1722813593693.png

DenizZilyas_3-1722813620018.png

Mail:

I sent the emails based on the updated certificates. If you want, you can use gather to consolidate them into a single email format.

DenizZilyas_4-1722813681512.png

DenizZilyas_5-1722813702956.png

If no certificates to be updated are found, the flow is escalated.

DenizZilyas_8-1722813961801.png

 

 

 

 

 

 

 

 

Labels in this area