Integrating Enterprise Protocols: LDAP, SFTP, SNMP, and JWS

Lightweight Directory Access Protocol (LDAP)

Service Provisioning via Docker

Deploying an OpenLDAP server alongside a phpLDAPadmin interface can be achieved using Docker Compose.

version: '3.8'

services:
  directory-server:
    image: osixia/openldap:latest
    container_name: openldap-host
    environment:
      - TZ=UTC
      - LDAP_ORGANISATION=myorg
      - LDAP_DOMAIN=myorg.io
      - LDAP_ADMIN_PASSWORD=SuperSecretAdmin123
    ports:
      - 389:389
      - 636:636
    networks:
      - directory-net

  admin-ui:
    image: osixia/phpldapadmin:latest
    container_name: ldap-admin-console
    privileged: true
    environment:
      - TZ=UTC
      - PHPLDAPADMIN_HTTPS=false
      - PHPLDAPADMIN_LDAP_HOSTS=directory-server
    ports:
      - 8081:80
    depends_on:
      - directory-server
    networks:
      - directory-net

networks:
  directory-net:
    driver: bridge

Spring Boot Integration

Add the required dependency to your project configuration:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

Implement a configuration manager to handle connections and directory operations dynamically:

import lombok.extern.slf4j.Slf4j;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.query.LdapQueryBuilder;

import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class DirectoryManager {

    private volatile LdapTemplate ldapOperations;

    private void initializeTemplate() {
        try {
            Map<String, String> configProps = fetchConfigFromStore();

            LdapContextSource contextSrc = new LdapContextSource();
            contextSrc.setUrl(configProps.get("url"));
            contextSrc.setBase(configProps.get("baseDn"));
            contextSrc.setUserDn(configProps.get("adminDn"));
            contextSrc.setPassword(configProps.get("adminPass"));
            contextSrc.setPooled(true);
            
            Map<String, Object> envProps = new HashMap<>();
            envProps.put("java.naming.ldap.attributes.binary", "objectGUID");
            contextSrc.setBaseEnvironmentProperties(envProps);
            contextSrc.afterPropertiesSet();

            this.ldapOperations = new LdapTemplate(contextSrc);
            this.ldapOperations.setIgnorePartialResultException(true);
        } catch (Exception ex) {
            log.error("Failed to establish LDAP connection", ex);
            throw new IllegalStateException("Directory connection error");
        }
    }

    public boolean authenticateUser(String uid, String pwd) {
        EqualsFilter filter = new EqualsFilter("uid", uid);
        try {
            return getTemplate().authenticate("", filter.toString(), pwd);
        } catch (Exception ex) {
            log.error("Authentication check failed", ex);
            throw new IllegalStateException("Directory auth error");
        }
    }

    public List<Map<String, Object>> searchEntries(String uid) {
        try {
            return getTemplate().search(
                LdapQueryBuilder.query().where("uid").is(uid),
                (AttributesMapper<Map<String, Object>>) attrs -> {
                    Map<String, Object> entryMap = new HashMap<>();
                    NamingEnumeration<? extends Attribute> attrEnum = attrs.getAll();
                    while (attrEnum.hasMore()) {
                        Attribute attr = attrEnum.next();
                        entryMap.put(attr.getID(), attr.get());
                    }
                    return entryMap;
                }
            );
        } catch (Exception ex) {
            log.error("Entry search failed", ex);
            throw new IllegalStateException("Directory search error");
        }
    }

    private LdapTemplate getTemplate() {
        if (ldapOperations == null) {
            synchronized (this) {
                if (ldapOperations == null) {
                    initializeTemplate();
                }
            }
        }
        return ldapOperations;
    }

    private Map<String, String> fetchConfigFromStore() {
        // Retrieve configuration from database or properties
        return Map.of(
            "url", "ldap://127.0.0.1:389",
            "baseDn", "dc=myorg,dc=io",
            "adminDn", "cn=admin,dc=myorg,dc=io",
            "adminPass", "SuperSecretAdmin123"
        );
    }
}

Secure File Transfer Protocol (SFTP)

Container Deployment

Deploy an SFTP server using Docker Compose:

version: '3.8'

services:
  sftp-host:
    image: atmoz/sftp
    volumes:
      - ./sftp-data/:/home/transfer_user/
    ports:
      - "2221:22"
    privileged: true
    command: transfer_user:S3cureP@ss:1002

The mapped root directory acts as a chroot jail and must lack write permissions for the user. Assign 755 to the root mount, then create a sub-directory with 777 permissions for actual file uploads:

mkdir ./sftp-data/upload_dir
chmod 777 ./sftp-data/upload_dir

Spring Boot Integration

Include the JSch library and Commons IO:

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

Create a utility class for SFTP operations:

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import java.util.Vector;

@Slf4j
public class SftpClient {

    public static ChannelSftp createConnection(String host, String user, String pass, int port) throws IOException {
        JSch jsch = new JSch();
        int maxRetries = 3;
        int timeoutMs = 10000;

        for (int attempt = 0; attempt < maxRetries; attempt++) {
            try {
                Session session = jsch.getSession(user, host, port);
                session.setPassword(pass);
                
                Properties sshConfig = new Properties();
                sshConfig.put("StrictHostKeyChecking", "no");
                session.setConfig(sshConfig);
                session.connect(timeoutMs);
                
                ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
                sftpChannel.connect();
                return sftpChannel;
            } catch (Exception ex) {
                log.warn("SFTP connection attempt {} failed. Retrying...", attempt + 1, ex);
                try { Thread.sleep(3000); } catch (InterruptedException ignored) {}
            }
        }
        throw new IOException("Unable to establish SFTP connection after retries.");
    }

    public static void transferFile(ChannelSftp channel, String destPath, InputStream dataStream) {
        try {
            channel.put(dataStream, destPath);
        } catch (SftpException ex) {
            log.error("File upload failed to path {}", destPath, ex);
        } finally {
            try { dataStream.close(); } catch (IOException ignored) {}
        }
    }

    public static void retrieveFile(ChannelSftp channel, String remotePath, OutputStream outStream) {
        try {
            channel.get(remotePath, outStream);
        } catch (SftpException ex) {
            log.error("File download failed for path {}", remotePath, ex);
        }
    }

    public static void ensureDirectoryExists(ChannelSftp channel, String path) throws SftpException {
        String[] dirs = path.split("/");
        for (String dir : dirs) {
            if (StringUtils.isBlank(dir)) continue;
            try {
                channel.cd(dir);
            } catch (SftpException e) {
                channel.mkdir(dir);
                channel.cd(dir);
            }
        }
    }

    public static void disconnect(ChannelSftp channel) {
        if (channel != null && channel.isConnected()) {
            try {
                Session s = channel.getSession();
                if (s.isConnected()) s.disconnect();
            } catch (JSchException ignored) {}
            channel.disconnect();
        }
    }

    public static boolean deleteRemoteFile(ChannelSftp channel, String filePath) {
        try {
            channel.rm(filePath);
            return true;
        } catch (SftpException e) {
            return false;
        }
    }

    public static boolean removeRemoteDirectory(ChannelSftp channel, String dirPath) {
        try {
            Vector<ChannelSftp.LsEntry> entries = channel.ls(dirPath);
            if (entries != null) {
                for (ChannelSftp.LsEntry entry : entries) {
                    if (!entry.getFilename().equals(".") && !entry.getFilename().equals("..")) {
                        String fullPath = dirPath + "/" + entry.getFilename();
                        if (entry.getAttrs().isDir()) {
                            removeRemoteDirectory(channel, fullPath);
                        } else {
                            channel.rm(fullPath);
                        }
                    }
                }
            }
            channel.rmdir(dirPath);
            return true;
        } catch (SftpException e) {
            return false;
        }
    }
}

Simple Network Management Protocol (SNMP)

Service Installation and Configuration

Install the net-snmp utilities on a Linux host:

yum install net-snmp net-snmp-utils
systemctl enable snmpd
systemctl start snmpd

Update the SNMP daemon configuration to support v3 users:

# /etc/snmp/snmpd.conf
rwuser snmp_admin

Configure the trap daemon:

# /etc/snmp/snmptrapd.conf
authCommunity log,execute,net public
createUser -e 0x8000137001C0A842D64CE2B7CF snmp_admin MD5 "P@ssw0rd12" DES "P@ssw0rd12"
authUser log,execute,net snmp_admin

Launch the trap listener:

snmptrapd -df -C -c /etc/snmp/snmptrapd.conf -Lo

Spring Boot Integration

Include the SNMP4J dependency:

<dependency>
    <groupId>org.snmp4j</groupId>
    <artifactId>snmp4j</artifactId>
    <version>2.7.0</version>
</dependency>

Define the configuration DTO and service interface:

import lombok.Data;

@Data
public class SnmpProfile {
    private String targetIp;
    private String targetPort;
    private String communityStr = "public";
    private int retryCount = 0;
    private long timeoutMs = 1000;
    private String protocolVersion = "v2c";
    private String secName;
    private String authMethod = "MD5";
    private String authPass;
    private String privacyMethod = "DES";
    private String privacyPass;
}
import org.snmp4j.PDU;
import org.snmp4j.Target;
import org.snmp4j.smi.VariableBinding;
import java.util.List;

public interface SnmpManager {
    Target buildTarget(SnmpProfile profile);
    PDU buildPDU(List<VariableBinding> bindings, int pduType);
    void dispatchTrap(List<VariableBinding> bindings, SnmpProfile profile) throws Exception;
    void dispatchInform(List<VariableBinding> bindings, SnmpProfile profile) throws Exception;
}

Implement the base service:

import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public abstract class AbstractSnmpHandler implements SnmpManager {

    public static final String TRAP_OID_PREFIX = "1.3.6.1.4.1.9955.11.2.1.1.2.";
    public static final Map<String, Integer> TRAP_FIELD_MAP = new HashMap<>();

    static {
        TRAP_FIELD_MAP.put("seqId", 11);
        TRAP_FIELD_MAP.put("objId", 12);
        TRAP_FIELD_MAP.put("domainName", 13);
        TRAP_FIELD_MAP.put("alarmId", 14);
        TRAP_FIELD_MAP.put("raisedTime", 15);
    }

    protected static Snmp snmpSession = null;

    protected static Snmp initSession() throws Exception {
        if (snmpSession != null) return snmpSession;
        try {
            TransportMapping<?> transport = new DefaultUdpTransportMapping();
            snmpSession = new Snmp(transport);
            snmpSession.listen();
            return snmpSession;
        } catch (Exception ex) {
            log.error("SNMP session initialization failed", ex);
            throw new Exception("SNMP init error");
        }
    }

    @Override
    public PDU buildPDU(List<VariableBinding> bindings, int pduType) {
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OID("1.3.6.1.4.1")));
        bindings.forEach(pdu::add);
        pdu.setType(pduType);
        return pdu;
    }

    public VariableBinding createBinding(String key, String val) {
        Integer idx = TRAP_FIELD_MAP.get(key);
        if (idx == null) return null;
        return new VariableBinding(new OID(TRAP_OID_PREFIX + idx), new OctetString(val));
    }
}

Implement V2C handler logic:

import lombok.extern.slf4j.Slf4j;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.VariableBinding;

import java.util.List;

@Slf4j
public class SnmpV2cHandler extends AbstractSnmpHandler {

    static {
        try { initSession(); } catch (Exception e) { log.error("Init failed", e); }
    }

    @Override
    public Target buildTarget(SnmpProfile profile) {
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(profile.getCommunityStr()));
        target.setAddress(new UdpAddress(profile.getTargetIp() + "/" + profile.getTargetPort()));
        target.setRetries(profile.getRetryCount());
        target.setTimeout(profile.getTimeoutMs());
        target.setVersion(SnmpConstants.version2c);
        return target;
    }

    @Override
    public void dispatchTrap(List<VariableBinding> bindings, SnmpProfile profile) throws Exception {
        Target target = buildTarget(profile);
        PDU pdu = buildPDU(bindings, PDU.TRAP);
        try {
            snmpSession.send(pdu, target);
        } catch (Exception ex) {
            throw new Exception("Trap dispatch failed", ex);
        }
    }

    @Override
    public void dispatchInform(List<VariableBinding> bindings, SnmpProfile profile) throws Exception {
        Target target = buildTarget(profile);
        PDU pdu = buildPDU(bindings, PDU.INFORM);
        try {
            ResponseEvent response = snmpSession.inform(pdu, target);
            if (response == null || response.getResponse() == null || response.getResponse().getErrorStatus() != PDU.noError) {
                throw new Exception("Inform acknowledgment failed");
            }
        } catch (Exception ex) {
            throw new Exception("Inform dispatch failed", ex);
        }
    }
}

CLI Validation

snmptrap -v 3 -e 0x8000137001C0A842D64CE2B7CF00000000 -u snmp_admin -l authPriv -a MD5 -A P@ssw0rd12 -x DES -X P@ssw0rd12 127.0.0.1 0 linkUp.0

JSON Web Signature (JWS) RFC 7515 Validation

Verifying JWS structures according to RFC 7515 can be implemented as follows:

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTSignerUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.Base64;

public class JwsRfc7515Validator {

    public static void main(String[] args) {
        String headerB64 = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9";
        String payloadB64 = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ";
        
        // Decoding Base64URL components
        byte[] headerBytes = Base64.getUrlDecoder().decode(headerB64);
        byte[] payloadBytes = Base64.getUrlDecoder().decode(payloadB64);
        System.out.println("Header: " + new String(headerBytes));
        System.out.println("Payload: " + new String(payloadBytes));

        String signingKeyB64 = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow";
        byte[] keyBytes = Base64.getUrlDecoder().decode(signingKeyB64);

        // Verifying an existing token
        String existingToken = headerB64 + "." + payloadB64 + ".dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
        boolean isVerified = JWT.of(existingToken).setKey(keyBytes).verify();
        System.out.println("Token Valid: " + isVerified);

        // Generating a new HS256 token
        String hs256Token = JWT.create()
            .setKey(keyBytes)
            .setHeader("typ", "JWT")
            .setHeader("alg", "HS256")
            .setPayload("iss", "joe")
            .setPayload("exp", 1300819380)
            .sign();
        System.out.println("HS256 Token: " + hs256Token);

        // Generating ES256 signature
        String algId = "ES256";
        KeyPair ecKeyPair = KeyUtil.generateKeyPair("EC");
        PrivateKey ecPrivateKey = ecKeyPair.getPrivate();
        System.out.println("EC Private Key: " + ecPrivateKey.toString());
        
        String es256Signature = JWTSignerUtil.createSigner(algId, ecKeyPair).sign(headerB64, payloadB64);
        System.out.println("ES256 Signature: " + es256Signature);
    }
}

Tags: LDAP SFTP SNMP JWS Spring Boot

Posted on Tue, 14 Jul 2026 16:44:59 +0000 by flyersun