src/core/fi/laverca/MSS_Formats.java
public class MSS_Formats {

    /**
     * http://uri.etsi.org/TS102204/v1.1.2#PKCS7
     */
    public final static String PKCS7 = "http://uri.etsi.org/TS102204/v1.1.2#PKCS7";

    /**
     * http://uri.etsi.org/TS102204/v1.1.2#CMS-Signature
     */
    public final static String CMS = "http://uri.etsi.org/TS102204/v1.1.2#CMS-Signature";

    /**
     * http://www.methics.fi/KiuruMSSP/v3.2.0#PKCS1
     */
    public final static String KIURU_PKCS1 = "http://www.methics.fi/KiuruMSSP/v3.2.0#PKCS1";
    
    /**
     * http://mss.ficom.fi/TS102204/v1.0.0#PKCS1
     */
    public final static String FICOM_PKCS1 = "http://mss.ficom.fi/TS102204/v1.0.0#PKCS1";
    
    /**
     * http://mss.ficom.fi/TS102204/v1.0.0#PKCS1
     * @deprecated Use {@link MSS_Formats#FICOM_PKCS1} instead.
     */
    @Deprecated
    public final static String PKCS1 = FICOM_PKCS1;

}
src/core/fi/laverca/mss/MssConf.java
public class MssConf {

    private static final Log log = LogFactory.getLog(MssConf.class);
    
    public static final String TRUSTSTORE_FILE = "ssl.truststore.file";
    public static final String TRUSTSTORE_PWD  = "ssl.truststore.password";
    public static final String TRUSTSTORE_TYPE = "ssl.truststore.type";
    
    public static final String KEYSTORE_FILE   = "ssl.keystore.file";
    public static final String KEYSTORE_PWD    = "ssl.keystore.password";
    public static final String KEYSTORE_TYPE   = "ssl.keystore.type";
    
    public static final String AP_ID           = "ap.id";
    public static final String AP_PWD          = "ap.password";
    
    public static final String SIGNATURE_URL       = "mssp.signature.url";
    public static final String STATUS_URL          = "mssp.status.url";
    public static final String RECEIPT_URL         = "mssp.receipt.url";
    public static final String PROFILE_URL         = "mssp.profile.url";
    public static final String REGISTRATION_URL    = "mssp.registration.url";
    public static final String HANDSHAKE_URL       = "mssp.handshake.url";
    
    private String apId;             // AP settings
    private String apPwd; 
    private String msspSignatureUrl; // AE connection settings
    private String msspStatusUrl;
    private String msspReceiptUrl;
    private String msspRegistrationUrl;
    private String msspProfileUrl;
    private String msspHandshakeUrl;
    
    private String keystoreFile;
    private String keystorePwd;
    private String keystoreType;
    
    private String truststoreFile;
    private String truststorePwd;
    private String truststoreType;

    public MssConf() {
        // Empty constructor
    }
    
    public MssConf(final String apId,             // AP settings
                   final String apPwd, 
                   final String msspSignatureUrl, // AE connection settings
                   final String msspStatusUrl,
                   final String msspReceiptUrl,
                   final String msspRegistrationUrl,
                   final String msspProfileUrl,
                   final String msspHandshakeUrl) 
    {
        this.apId             = apId;
        this.apPwd            = apPwd;
        this.msspSignatureUrl = msspSignatureUrl;
        this.msspStatusUrl    = msspStatusUrl;
        this.msspReceiptUrl   = msspReceiptUrl;
        this.msspRegistrationUrl = msspRegistrationUrl;
        this.msspProfileUrl   = msspProfileUrl;
        this.msspHandshakeUrl = msspHandshakeUrl;
    }
    
    public static MssConf fromProperties(final Properties p) {
        
        final MssConf conf = new MssConf();
        
        conf.setApId(p.getProperty(AP_ID));
        conf.setApPwd(p.getProperty(AP_PWD));
        
        conf.setSignatureUrl(p.getProperty(SIGNATURE_URL));
        conf.setStatusUrl(p.getProperty(STATUS_URL));
        conf.setReceiptUrl(p.getProperty(RECEIPT_URL));
        conf.setRegistrationUrl(p.getProperty(REGISTRATION_URL));
        conf.setProfileUrl(p.getProperty(PROFILE_URL));
        conf.setHandshakeUrl(p.getProperty(HANDSHAKE_URL));

        conf.setTruststore(p.getProperty(TRUSTSTORE_FILE),
                           p.getProperty(TRUSTSTORE_PWD),
                           p.getProperty(TRUSTSTORE_TYPE, "JKS"));
        
        conf.setKeystore(p.getProperty(KEYSTORE_FILE),
                         p.getProperty(KEYSTORE_PWD),
                         p.getProperty(KEYSTORE_TYPE, "JKS"));        
        return conf;
    }
    
    public static MssConf fromPropertyFile(final String fileName) {
        File f = new File(fileName);
        Properties p = new Properties();
        try (FileInputStream is = new FileInputStream(f)) {
            p.load(is);
        } catch (Exception e) {
            log.fatal("Could not load properties");
            throw new IllegalArgumentException(e);
        }
        return MssConf.fromProperties(p);
    }


    /**
     * Get the properties object of this configuration
     * @return Java Properties containing the configuration
     */
    public Properties getProperties() {
        Properties p = new Properties();
        p.put(AP_ID,  this.getApId());
        p.put(AP_PWD, this.getApPwd());
        
        p.put(SIGNATURE_URL,    this.getSignatureUrl());
        p.put(STATUS_URL,       this.getStatusUrl());
        p.put(RECEIPT_URL,      this.getReceiptUrl());
        p.put(REGISTRATION_URL, this.getRegistrationUrl());
        p.put(PROFILE_URL,      this.getProfileUrl());
        p.put(HANDSHAKE_URL,    this.getHandshakeUrl());
        
        p.put(TRUSTSTORE_FILE, this.getTruststore());
        p.put(TRUSTSTORE_PWD,  this.getTruststorePwd());
        p.put(TRUSTSTORE_TYPE, this.getTruststoreType());
        
        p.put(KEYSTORE_FILE, this.getKeystore());
        p.put(KEYSTORE_PWD,  this.getKeystorePwd());
        p.put(KEYSTORE_TYPE, this.getKeystoreType());

        return p;
    }

    public String getProfileUrl() {
        return this.msspProfileUrl;
    }

    public String getRegistrationUrl() {
        return this.msspRegistrationUrl;
    }

    public String getHandshakeUrl() {
        return this.msspHandshakeUrl;
    }

    public String getTruststore() {
        return this.truststoreFile;
    }
    
    public String getTruststorePwd() {
        return this.truststorePwd;
    }
    
    public String getTruststoreType() {
        return (this.truststoreType != null ? this.truststoreType : "JKS");
    }
    
    public String getKeystore() {
        return this.keystoreFile;
    }
    
    public String getKeystorePwd() {
        return this.keystorePwd;
    }
    
    public String getKeystoreType() {
        return (this.keystoreType != null ? this.keystoreType : "JKS");
    }
    
    public String getApId() {
        return this.apId;
    }
    
    public String getApPwd() {
        return this.apPwd;
    }
    
    public String getSignatureUrl() {
        return this.msspSignatureUrl;
    }
    
    public String getStatusUrl() {
        return this.msspStatusUrl;
    }
    
    public String getReceiptUrl() {
        return this.msspReceiptUrl;
    }

    public void setApId(String apId) {
        this.apId = apId;
    }

    public void setApPwd(String apPwd) {
        this.apPwd = apPwd;
    }

    public void setSignatureUrl(String msspSignatureUrl) {
        this.msspSignatureUrl = msspSignatureUrl;
    }

    public void setStatusUrl(String msspStatusUrl) {
        this.msspStatusUrl = msspStatusUrl;
    }

    public void setReceiptUrl(String msspReceiptUrl) {
        this.msspReceiptUrl = msspReceiptUrl;
    }

    public void setRegistrationUrl(String msspRegistrationUrl) {
        this.msspRegistrationUrl = msspRegistrationUrl;
    }

    public void setProfileUrl(String msspProfileUrl) {
        this.msspProfileUrl = msspProfileUrl;
    }

    public void setHandshakeUrl(String msspHandshakeUrl) {
        this.msspHandshakeUrl = msspHandshakeUrl;
    }
    
    public void setKeystore(final String file, final String pwd, final String type) {
        this.keystoreFile = file;
        this.keystorePwd  = pwd;
        this.keystoreType = type;
    }
    
    public void setTruststore(final String file, final String pwd, final String type) {
        this.truststoreFile = file;
        this.truststorePwd  = pwd;
        this.truststoreType = type;
    }
    
}
src/core/fi/laverca/util/X509CertificateChain.java
public class X509CertificateChain implements Collection<X509Certificate>, Serializable {

    private static final long serialVersionUID = 1L;
    private static final Log log = LogFactory.getLog(X509CertificateChain.class);

    private List<X509Certificate> certChain = new ArrayList<>();
    private List<String>          sigProfs  = new ArrayList<>();
    private String                signingMethod;
    private String                state;
    
    private Map<QName, String> attributes;
    
    /**
     * Construct an {@link X509CertificateChain} from SOAP {@link CertificateType} data.
     * @param certData SOAP data
     */
    public X509CertificateChain(CertificateType certData) {
        CertificateFactory certFactory = null;
        try {
            certFactory = CertificateFactory.getInstance("X.509");
        } catch (CertificateException e) {
            log.error(e.getMessage(), e);
            return;
        }

        this.attributes    = certData.getOtherAttributes();
        this.signingMethod = certData.getSigningMethod();
        this.state         = certData.getState();
        this.sigProfs.addAll(certData.getSignatureProfiles());
        
        for (Object o: certData.getX509IssuerSerialsAndX509SKISAndX509SubjectNames()) {
            if (o instanceof JAXBElement<?>) {
                JAXBElement<?> element = (JAXBElement<?>) o;
                Object val = element.getValue();
                
                if (val instanceof byte[]) {
                    try (InputStream in = new ByteArrayInputStream((byte[]) val)) {
                        final X509Certificate x509cert = (X509Certificate) certFactory.generateCertificate(in);
                        this.add(x509cert);                                
                    } catch (CertificateException e) {
                        log.error(e.getMessage(), e);
                    } catch (IOException ioe) {
                        log.error(ioe.getMessage(), ioe);
                    }
                }
            }
        }
    }

    /**
     * Empty default constructor
     */
    public X509CertificateChain() {
        // Do nothing. 
    }
    
    /**
     * Get SignatureProfiles for this certificate chain
     * @return SignatureProfiles (String list)
     */
    public List<String> getSignatureProfiles() {
        return this.sigProfs;
    }
    
    /**
     * Get certificate SigningMethod
     * @return e.g. APP or SIM
     */
    public String getSigningMethod() {
        return this.signingMethod;
    }
    
    /**
     * Get certificate state
     * @return one of: ACTIVE, INACTIVE
     */
    public String getCertState() {
        return this.state;
    }

    /**
     * Get nth certificate from this certificate chain
     * @param n
     * @return certificate if found
     * @throws IndexOutOfBoundsException if the index is out of range
     *         (<tt>index &lt; 0 || index &gt;= size()</tt>)
     */
    public X509Certificate get(int n) {
        return this.certChain.get(n);
    }
    
    /**
     * Get an attribute from the SOAP certificate element
     * @param qname QName of the attribute
     * @return attribute or null if not found
     */
    public String getAttribute(final QName qname) {
        return this.attributes.get(qname);
    }

    @Override
    public int size() {
        return this.certChain.size();
    }

    @Override
    public boolean isEmpty() {
        return this.certChain.isEmpty();
    }
    
    /**
     * Does the signing certificate have KeyUsage nonRepudiaiton?
     * @return true if the signing certificate have KeyUsage nonRepudiaiton
     */
    public boolean isNonRepudiation() {
        if (this.isEmpty()) return false;
        return this.certChain.get(0).getKeyUsage()[1];
    }

    /**
     * Does the signing certificate have KeyUsage digitalSignature?
     * @return true if the signing certificate have KeyUsage digitalSignature
     */
    public boolean isDigitalSignature() {
        if (this.isEmpty()) return false;
        return this.certChain.get(0).getKeyUsage()[0];
    }
    
    @Override
    public boolean contains(Object arg0) {
        return this.certChain.contains(arg0);
    }

    @Override
    public Iterator<X509Certificate> iterator() {
        return this.certChain.iterator();
    }

    @Override
    public Object[] toArray() {
        return this.certChain.toArray();
    }

    @Override
    public <T> T[] toArray(T[] arg0) {
        return this.certChain.toArray(arg0);
    }

    @Override
    public boolean add(X509Certificate arg0) {
        if (arg0 != null) {
            return this.certChain.add(arg0);            
        }
        return false;
    }

    @Override
    public boolean remove(Object arg0) {
        return this.certChain.remove(arg0);
    }

    @Override
    public boolean containsAll(Collection<?> arg0) {
        return this.certChain.containsAll(arg0);
    }

    @Override
    public boolean addAll(Collection<? extends X509Certificate> arg0) {
        for (X509Certificate cert: arg0) {
            if (!this.add(cert)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean removeAll(Collection<?> arg0) {
        return this.certChain.removeAll(arg0);
    }

    @Override
    public boolean retainAll(Collection<?> arg0) {
        return this.certChain.retainAll(arg0);
    }

    @Override
    public void clear() {
        this.certChain.clear();
    }

    public X509Certificate getSigningCert() {
        if (this.size() > 0) {
            return this.get(0);            
        } 
        
        log.error("Certificate chain does not contain a singing certificate.");
        return null;
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for (X509Certificate cert: this.certChain) {
            sb.append("Cert " + i + "= " + cert.toString() +"\n");
            i++;
        }
        return sb.toString();
    }

}
src/core/fi/laverca/etsi/EtsiResponse.java
public class EtsiResponse extends MssResponse {

    public EtsiResponse(final MSSSignatureReq originalSigReq,
                        final MSSSignatureResp originalSigResp, 
                        final MSSStatusResp finalStatusResp) {
        super(originalSigReq, originalSigResp, finalStatusResp);
    }
}
src/core/fi/laverca/etsi/EtsiClient.java
public class EtsiClient extends ClientBase<EtsiRequest, EtsiResponse> {
    
    
    /**
     * Preferred form of initialize a EtsiClient
     * 
     * @param conf Configuration data collection
     */
    public EtsiClient(MssConf conf) {
        super(conf);
    }

    /**
     * Initialize an EtsiClient
     * 
     * @param apId Your identifier; MessageAbstractType/AP_Info/AP_ID. Not null.
     * @param apPwd Your password; MessageAbstractType/AP_Info/AP_PWD. Not null.
     * @param msspSignatureUrl    Connection URL to the AE for signature requests. 
     * @param msspStatusUrl       Connection URL to the AE for status query requests. 
     * @param msspReceiptUrl      Connection URL to the AE for receipt requests. 
     * 
     * @throws IllegalArgumentException if AP ID or AP PWD is missing or invalid.
     */
    public EtsiClient(final String apId,             // AP settings
                      final String apPwd, 
                      final String msspSignatureUrl, // AE connection settings
                      final String msspStatusUrl,
                      final String msspReceiptUrl)
        throws IllegalArgumentException
    {
        super(apId, apPwd, msspSignatureUrl, msspStatusUrl, msspReceiptUrl);
    }
    
    /**
     * Initialize an EtsiClient
     *
     * @param apId Your identifier; MessageAbstractType/AP_Info/AP_ID. Not null.
     * @param apPwd Your password; MessageAbstractType/AP_Info/AP_PWD. Not null.
     * @param msspSignatureUrl    Connection URL to the AE for signature requests. 
     * @param msspStatusUrl       Connection URL to the AE for status query requests. 
     * @param msspReceiptUrl      Connection URL to the AE for receipt requests. 
     * @param msspRegistrationUrl Connection URL to the AE for registration requests. 
     * @param msspProfileUrl      Connection URL to the AE for profile query requests. 
     * @param msspHandshakeUrl    Connection URL to the AE for handshake requests.
     * 
     * @throws IllegalArgumentException if AP ID or AP PWD is missing or invalid.
     */
    public EtsiClient(final String apId,             // AP settings
                      final String apPwd, 
                      final String msspSignatureUrl, // AE connection settings
                      final String msspStatusUrl,
                      final String msspReceiptUrl,
                      final String msspRegistrationUrl,
                      final String msspProfileUrl,
                      final String msspHandshakeUrl)
        throws IllegalArgumentException
    {
        super(apId, apPwd, msspSignatureUrl, msspStatusUrl, msspReceiptUrl, msspRegistrationUrl, msspProfileUrl, msspHandshakeUrl);
    }

    /**
     * Create an EtsiRequest instance
     * @param apTransId AP Transaction ID
     * @param dtbd Data to be displayed
     * @param dtbs Data to be signed
     * @param msisdn MSISDN of the target user
     * @param additionalServices List of FiCom additionalservices to add to the request
     * @param signatureProfile Signature profile to use
     * @param mssFormat MSS Format to use.
     * @param messagingMode Messaging mode to use
     * @return Created request
     */
    public EtsiRequest createRequest(final String apTransId, 
                                     final String msisdn,
                                     final DTBS   dtbs,
                                     final String dtbd,
                                     final List<AdditionalServiceType> additionalServices, 
                                     final String signatureProfile,
                                     final String mssFormat,
                                     final MessagingModeType messagingMode) {
        
        final EtsiRequest req = new EtsiRequest();

        final MSSSignatureReq sigReq = this.mssClient.createSignatureRequest(apTransId, 
                                                                             msisdn, 
                                                                             dtbs,
                                                                             dtbd, 
                                                                             signatureProfile, 
                                                                             mssFormat, 
                                                                             messagingMode);
        req.sigReq = sigReq;
        
        final List<AdditionalServiceType> as = sigReq.getAdditionalServices().getServices();

        if (additionalServices != null) {
            for (AdditionalServiceType s : additionalServices) {
                if (s != null) {
                    as.add(s);
                }
            }
        }
        
        return req;
    }

    @Override
    protected EtsiResponse createResp(final MSSSignatureReq sigReq,
                                      final MSSSignatureResp sigResp, 
                                      final MSSStatusResp statResp) {
        return new EtsiResponse(sigReq, sigResp, statResp);
    }

}
src/core/fi/laverca/util/DTBS.java
public class DTBS {

    private String encoding = null;
    private String text     = null;
    private byte[] data     = null;
    private String mimeType = null;
    
    final static public String ENCODING_UTF8   = "UTF-8";
    final static public String ENCODING_BASE64 = "base64";
    
    final static public String MIME_STREAM    = "application/octet-stream";
    final static public String MIME_SHA1      = "application/x-sha1";
    final static public String MIME_SHA256    = "application/x-sha256";
    final static public String MIME_UCS2      = "text/plain;ucs2";
    final static public String MIME_GSM       = "text/plain;gsm";
    final static public String MIME_UTF8      = "text/plain;UTF-8";
    final static public String MIME_TEXTPLAIN = "text/plain";

    
    /**
     * Initialize a DTBS with text.
     * 
     * @param text Text data
     * @param encoding Text encoding
     * @param mimeType Mime-type
     */
    public DTBS(final String text, final String encoding, final String mimeType) {
        this.text     = text;
        this.encoding = encoding;
        this.mimeType = mimeType;
    }
    
    /**
     * Initialize a DTBS with data.
     * 
     * @param data Binary data
     * @param encoding Text encoding
     * @param mimeType Mime-type
     */
    public DTBS(final byte[] data, final String encoding, final String mimeType) {
        this.data = data;
        this.encoding = encoding;
        this.mimeType = mimeType;
    }
    
    /**
     * Initialize a DTBS without a mime type for <code>toBytes()</code>
     * 
     * @param text Text data
     * @param encoding Text encoding
     */
    public DTBS(final String text, final String encoding) {
    	this(text, encoding, null);
    }

    /**
     * Initialize a DTBS without a mime type for <code>toBytes()</code>
     * This defaults encoding to UTF-8.
     * @param text Text data
     */
    public DTBS(final String text) {
    	this(text, ENCODING_UTF8);
    }

    public String  getText() { return this.text; }
    public byte[]  getData() { return this.data; }


    /**
     * Converter of incoming DTBS to byte-array, if the incoming
     * form happened to be a String, otherwise returning it as is.
     * 
     * @return byte[]
     * @throws RuntimeException when no text or data is found
     */
    public byte[] toBytes() {
        if (this.data != null) {
            return this.data;
        }
        if (this.text != null) {
            try {
                return this.text.getBytes(this.encoding);
            }
            catch (UnsupportedEncodingException uee) {
                throw new RuntimeException(uee);
            }
        }
        throw new RuntimeException("Illegal DTBS");
    }

    /**
     * Retrieve DTBS as a String - if it was a String at creation time.
     * If the DTBS was not a String, return either 
     * "-binary DTBS-" or 
     * "-llegal DTBS-" 
     * depending on whether the DTBS contains binary data.
     * 
     * @return String
     */
    @Override
    public String toString() {
        if (this.text != null) {
            return this.text;
        }
        else if (this.data != null) {
            return "-binary DTBS-";
        }
        else {
            return "-illegal DTBS-";
        }
    }

    /**
     * Convert to a DataToBeSigned object.
     * @return this object as org.etsi.uri.TS102204.v1_1_2.DataToBeSigned
     */
    public DataType toDataToBeSigned() {
        DataType rv = new DataType();
        rv.setEncoding(this.encoding);
        rv.setMimeType(this.mimeType);
        
        if(this.text == null) {
            rv.setValue(Base64.encode(this.data));
        } else {
        	rv.setValue(this.text);
        }
        
        return rv;
    }
    
    /**
     * Length of DTBS data, either the string, or the byte-array
     * 
     * @return int - length of data
     * @throws RuntimeException when no text or data is found
     */
    public int length() {
        if (this.text != null) {
            return this.text.length();
        }
        else if (this.data != null) {
            return this.data.length; 
        }
        else {
            throw new RuntimeException("Illegal DTBS");
        }
    }
}
src/core/fi/laverca/CmsSignature.java
public class CmsSignature implements Signature {
    
    private static final Log log = LogFactory.getLog(CmsSignature.class);

    private SignedData _sd;
    private byte[] rawSignature;

    /** 
     * 
     * @param bytes In general, you get this from an MSS_SignatureResp.getSignature() call.
     * @throws IllegalArgumentException if bytes is null or the amount of signer certificates found is not equal to one
     */
    public CmsSignature(byte[] bytes) throws IllegalArgumentException {
        if(bytes == null) {
            throw new IllegalArgumentException("Can't construct a PKCS7 SignedData element from null input.");
        }
        this.rawSignature = bytes;
        

        this._sd = bytesToPkcs7SignedData(bytes);

        if(this._sd.getSignerInfos() == null || this._sd.getSignerInfos().size() != 1) {
            throw new IllegalArgumentException("This only works with exactly one SignerInfo.");
        }
    }

    /**
     * Look up the certificate of the signer of this signature. 
     * <p>Note that this only looks up the <b>first signer</b>. In MSSP signatures,
     * there is only one, but in a general Pkcs7 case, there can be several.
     * 
     * @return X509 signer certificate
     * @throws MssException if the amount of signer certificates found is not equal to one
     */
    @Override
    public X509Certificate getSignerCert() throws MssException {
        List<X509Certificate> allSignerCerts = getSignerCerts(this._sd);
        int certsFound = allSignerCerts.size();
        
        if (certsFound < 1) {
            throw new MssException("Signer cert not found.");
        } else if (certsFound > 1) {
            throw new MssException("Expected a single signer cert but found " + certsFound + ".");
        }
        
        return allSignerCerts.get(0);
    }

    /**
     * Convenience method. Equivalent to calling getSignerCert and
     * then parsing out the CN from the certificate's Subject field.
     * @return Signer CN or null if there's a problem.
     */
    @Override
    public String getSignerCn() {
        try {
            X509Certificate signerCert = this.getSignerCert();
            String dn = signerCert.getSubjectX500Principal().getName();

            String cn = null;
            try {
                LdapName ldapDn = new LdapName(dn);
                List<Rdn> rdns = ldapDn.getRdns();
                for(Rdn r : rdns) {
                    if("CN".equals(r.getType())) {
                        cn = r.getValue().toString();
                    }
                }
            } catch(InvalidNameException e) {
                log.warn("Invalid name", e);
            }

            return cn;
        } catch(Throwable t) {
            log.error("Failed to get signer CN: " + t.getMessage());
            return null;
        }
    }

    @Override
    public byte[] getRawSignature() {
        return this.rawSignature;
    }

    @Override
    public String getBase64Signature() {
        return Base64.toBase64String(this.getRawSignature());
    }
    
    /**
     * Get the CMS SignatureValue as byte[]
     * @return CMS SignatureValue as byte[]
     */
    public byte[] getSignatureValue() {
        return readSignatureValue(this.getSignerInfo());
    }
    
    /**
     * Get the SignerInfo object if found
     * @return SignerInfo or null
     */
    public SignerInfo getSignerInfo() {
        for (SignerInfo si : readSignerInfos(this._sd)) {
            return si;
        }
        return null;
    }

    /**
     * Get the certificates
     * @return All certificates or an empty list
     */
    public List<X509Certificate> getCertificates() {
        return readCerts(this._sd);
    }
    
    /**
     * Get the certificate chain as {@link X509CertificateChain}
     * @return certificate chain
     */
    public X509CertificateChain getCertificateChain() {
        X509CertificateChain certChain = new  X509CertificateChain();
        for (X509Certificate cert : this.getCertificates()) {
            certChain.add(cert);
        }
        return certChain;
    }
    
    /**
     * Convert a byte array to a PKCS7 SignedData object
     * @param bytes byte array
     * @return PKCS7 SignedData object
     */
    public static SignedData bytesToPkcs7SignedData(byte[] bytes) {

        if(bytes == null) {
            throw new IllegalArgumentException("null bytes");
        }

        ASN1InputStream ais = new ASN1InputStream(bytes);
        ASN1Object     asn1 = null;
        try {
            asn1 = ais.readObject();
        } catch(IOException ioe) {
            throw new IllegalArgumentException("not a pkcs7 signature");
        } finally {
            try {
                ais.close();
            } catch (IOException e) {
                // Ignore
            }
        }

        ContentInfo ci = ContentInfo.getInstance(asn1);

        ASN1ObjectIdentifier typeId = ci.getContentType();
        if( ! typeId.equals(PKCSObjectIdentifiers.signedData)) {
            throw new IllegalArgumentException("not a pkcs7 signature");
        }

        return SignedData.getInstance(ci.getContent());
    }

    /**
     * Read the certificates used to sign a PKCS7 SignedData.
     * 
     * @param sd PKCS7 SignedData
     * @return List of X509 certificates
     * @throws MssException if no certificate or signer info is found from the data
     */
    public static List<X509Certificate> getSignerCerts(final SignedData sd) throws MssException {

        // 0. Setup. 
        // 1. Read PKCS7.Certificates to get all possible certs.
        // 2. Read PKCS7.SignerInfo to get all signers.
        // 3. Look up matching certificates.
        // 4. Return the list.

        // 0. Setup. 
        if(sd == null) {
            throw new IllegalArgumentException("null input");
        }
        List<X509Certificate> signerCerts = new ArrayList<X509Certificate>();

        // 1. Read PKCS7.Certificates to get all possible certs.
        log.debug("Read all certs");
        List<X509Certificate> certs = readCerts(sd);
        
        if (certs.isEmpty()) {
            throw new MssException("PKCS7 SignedData certificates not found");
        }

        // 2. Read PKCS7.SignerInfo to get all signers.
        log.debug("Read SignerInfo");
        List<SignerInfo> signerInfos = readSignerInfos(sd);
        
        if (signerInfos.isEmpty()) {
            throw new MssException("PKCS7 SignedData signerInfo not found");
        }

        // 3. Verify that signerInfo cert details match the cert on hand
        log.debug("Matching cert and SignerInfo details");
        for (SignerInfo si : signerInfos) {
            for (X509Certificate c : certs) {
                String siIssuer = readIssuer(si);
                String siSerial = readSerial(si);

                String cIssuer = c.getIssuerDN().toString();
                String cSerial = c.getSerialNumber().toString();
    
                if (dnsEqual(siIssuer, cIssuer) && siSerial.equals(cSerial)) {
                    signerCerts.add(c);
                    log.debug("Cert does match signerInfo");
                    log.debug("SignerInfo   issuer:serial = " + siIssuer + ":" + siSerial);
                    log.debug("Certificates issuer:serial = " + cIssuer  + ":" + cSerial);
                } else {
                    log.debug("Cert does not match signerInfo");
                    log.debug("SignerInfo   issuer:serial = " + siIssuer + ":" + siSerial);
                    log.debug("Certificates issuer:serial = " + cIssuer  + ":" + cSerial);
                }
            }
        }

        // 4. Return the list.
        log.debug("Returning " + signerCerts.size() + " certs");
        return signerCerts;
    }

    /**
     * Read all certificates from a SignedData
     * @param sd data
     * @return all X509 certificates or an empty list
     */
    public static List<X509Certificate> readCerts(final SignedData sd) {
        if (sd == null) {
            return Collections.emptyList();
        }

        List<X509Certificate> certs = new ArrayList<X509Certificate>();

        ASN1Set certSet = sd.getCertificates();
        Enumeration<?> en = certSet.getObjects();
        while(en.hasMoreElements()) {
            Object o = en.nextElement();
            try {
                byte[] certDer = ((ASN1Sequence)o).getEncoded();
                X509Certificate cert = X509Util.DERtoX509Certificate(certDer);
                if (cert != null) {
                    certs.add(cert);                    
                }
            } catch (IOException e) {
                log.debug("Failed to read cert", e);
            }
        }

        return certs;
    }

    /**
     * Read SignerInfo elements from a SignedData
     * @param sd data
     * @return SignerInfo element list or null
     */
    public static List<SignerInfo> readSignerInfos(final SignedData sd) {
        if (sd == null) {
            return null;
        }
        
        List<SignerInfo> signerInfos = new ArrayList<SignerInfo>();

        ASN1Set siSet = sd.getSignerInfos();
        Enumeration<?> e = siSet.getObjects();
        while(e.hasMoreElements()) {
            Object o = e.nextElement();
            try {
                SignerInfo si = SignerInfo.getInstance(o);
                signerInfos.add(si);
            } catch (RuntimeException ex) {
                log.trace("SignerInfo " + o + " not found");
            }
        }

        return signerInfos;
    }

    /**
     * Read the Serial element from a SignedInfo object
     * @param si data
     * @return Serial as String
     */
    public static String readSerial(final SignerInfo si) {
        if (si == null) {
            return null;
        }

        final IssuerAndSerialNumber ias = si.getIssuerAndSerialNumber();
        final ASN1Integer     serialDER = ias.getCertificateSerialNumber();

        return serialDER.getPositiveValue().toString();
    }

    /**
     * Read the Issuer from a SignedInfo object
     * @param si data
     * @return Issuer as String
     */
    public static String readIssuer(final SignerInfo si) {
        if (si == null) {
            return null;
        }

        final IssuerAndSerialNumber ias = si.getIssuerAndSerialNumber();
        final X500Name       issuerName = ias.getName();

        return issuerName.toString();
    }
    
    /**
     * Read the SignatureValue from a SignedInfo
     * @param si data
     * @return SignatureValue as byte[]
     */
    public static byte[] readSignatureValue(final SignerInfo si) {
        if (si == null) {
            return null;
        }
        return si.getEncryptedDigest().getOctets();
    }

    /** 
     * Return true if two Distinguished Names are equal, ignoring 
     * delimiters and order of elements.
     * 
     * @param dn1 First Distinguished name
     * @param dn2 Second Distinguished name
     * @return true if DNs are equal, false otherwise
     */
    public static boolean dnsEqual(final String dn1, final String dn2) {
        if (dn1 == null || dn2 == null) {
            return false;
        }

        final X500Name n1 = new X500Name(dn1);
        final X500Name n2 = new X500Name(dn2);

        return n1.equals(n2);
    }
}
src/core/fi/laverca/SignatureProfiles.java
public class SignatureProfiles {

    public static final String FICOM_AUTHENTICATION    = "http://mss.ficom.fi/TS102206/v1.0.0/authentication-profile.xml";
    public static final String FICOM_ANONYMOUS         = "http://mss.ficom.fi/TS102206/v1.0.0/anonymous-profile.xml";
    public static final String FICOM_SIGNATURE         = "http://mss.ficom.fi/TS102206/v1.0.0/signature-profile.xml";
    public static final String FICOM_DIGESTIVE         = "http://mss.ficom.fi/TS102206/v1.0.0/digestive-signature-profile.xml";
    public static final String FICOM_CONSENT           = "http://mss.ficom.fi/TS102206/v1.0.0/consent-profile.xml";
    
    public static final String SWISSCOM_AUTHENTICATION = "http://mid.swisscom.ch/MID/v1/AuthProfile1";
    public static final String SWISSCOM_APP_LOA4       = "http://mid.swisscom.ch/Device-LoA4";
    public static final String SWISSCOM_SIM_LOA4       = "http://mid.swisscom.ch/STK-LoA4";
    public static final String SWISSCOM_ANY_LOA4       = "http://mid.swisscom.ch/Any-LoA4";
    
    @Deprecated
    public static final String ALAUDA_SIGNATURE        = "http://alauda.mobi/digitalSignature";
    
    // See RFC 5280
    public static final String ALAUDA_AUTHENTICATION   = "http://alauda.mobi/digitalSignature";
    public static final String ALAUDA_SIGNING          = "http://alauda.mobi/nonRepudiation";
    
    public static final String MOBILECONNECT_LOA2      = "http://uri.gsma.com/mobileconnect/LoA2";
    public static final String MOBILECONNECT_LOA3      = "http://uri.gsma.com/mobileconnect/LoA3";
    
}
src/core/fi/laverca/etsi/EtsiRequest.java
public class EtsiRequest extends MssRequest<EtsiResponse> {

    public EtsiRequest() {
        // fields are written by the client class during execution
    }

}
src/core/fi/laverca/mss/ProfileQueryResponse.java
public class ProfileQueryResponse {

    public MSSProfileResp resp;
    
    public ProfileQueryResponse(final MSSProfileResp resp) {
        this.resp = resp;
    }
    
    /**
     * Return the Mobile User's Signature Profiles
     * @return Signature Profiles as a String list
     */
    public List<String> getSignatureProfiles() {
        
        List<String> profiles = new ArrayList<>();
        if (this.resp == null) return profiles;

        for (MssURIType prof : this.resp.getSignatureProfiles()) {
            profiles.add(prof.getMssURI());
        }
        
        return profiles;
    }
    
    /**
     * If the MSSP supports ProfileQueryExtension, return the Mobile User's nonRepudiation certificate.
     * @return Mobile User certificate chain for nonRepudiation certificate.
     * @deprecated Use {@link #getCertificate(String)} or {@link #getNonRepudiationCertificate()} instead
     */
    @Deprecated
    public X509CertificateChain getCertificates() {
        return this.getNonRepudiationCertificate();
    }
    
    /**
     * If the MSSP supports ProfileQueryExtension, return the Mobile User's nonRepudiation certificate.
     * @return Mobile User certificate chain for nonRepudiation certificate.
     */
    public X509CertificateChain getNonRepudiationCertificate() {
        for (X509CertificateChain chain : this.getAllCertificates()) {
            if (chain.isNonRepudiation()) return chain;
        }
        return new X509CertificateChain();
    }
    
    
    /**
     * If the MSSP supports ProfileQueryExtension, return the Mobile User's digitalSignature certificate.
     * @return Mobile User certificate chain for nonRepudiation certificate.
     */
    public X509CertificateChain getDigitalSignatureCertificate() {
        for (X509CertificateChain chain : this.getAllCertificates()) {
            if (chain.isDigitalSignature()) return chain;
        }
        return new X509CertificateChain();
    }
    
    /**
     * If the MSSP supports ProfileQueryExtension, return all of the the Mobile User's certificates.
     * @return Mobile User certificate chains.
     */
    public List<X509CertificateChain> getAllCertificates() {
        
        List<X509CertificateChain> certs = new ArrayList<>();
        
        if (this.resp             == null) return certs;
        if (this.resp.getStatus() == null) return certs;
        if (this.resp.getStatus().getStatusDetail() == null) return certs;

        ProfileQueryExtension ext = this.getProfileQueryExtension();
        for (CertificateType certType : ext.getMobileUserCertificates()) {
            certs.add(new X509CertificateChain(certType));
        }
        return certs;
    }
    
    /**
     * If the MSSP supports ProfileQueryExtension, return the Mobile User's certificates.
     * @param mssSigProf SignatureProfile the certificate is related to
     * @return Mobile User certificate chains.
     */
    public X509CertificateChain getCertificate(final String mssSigProf) {

        if (this.resp             == null) return new X509CertificateChain();
        if (this.resp.getStatus() == null) return new X509CertificateChain();
        if (this.resp.getStatus().getStatusDetail() == null) return new X509CertificateChain();

        // Filter certs by signature profile
        ProfileQueryExtension    extension = this.getProfileQueryExtension();
        List<X509CertificateChain> certs = extension.getMobileUserCertificates().stream()
                                                                                .map(X509CertificateChain::new)
                                                                                .filter(c -> c.getSignatureProfiles() != null)
                                                                                .filter(c -> c.getSignatureProfiles().contains(mssSigProf))
                                                                                .collect(Collectors.toList());
        
        // Find first acceptable cert
        Predicate<X509CertificateChain> wantedSigningMethod = (c) -> Objects.equals(c.getSigningMethod(), extension.getPreferredSigningMethod());
        return certs.stream()
                    .filter(wantedSigningMethod).findFirst() // find first cert matching our wanted signing method
                    .orElse(certs.stream().findFirst()       // or if none found, find any other cert
                    .orElse(new X509CertificateChain()));    // or if none found, return empty cert chain
    }
    
    /**
     * Get a ProfileQueryExtension from the response
     * @return ProfileQueryExtension or null if not found
     */
    private ProfileQueryExtension getProfileQueryExtension() {
        for (Object o : this.resp.getStatus().getStatusDetail().getAniesAndServiceResponsesAndReceiptRequestExtensions()) {
            if (o instanceof ProfileQueryExtension) {
                return (ProfileQueryExtension)o;
            }
        }
        return null;
    }
        
}


Question: import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import eu.europa.esig.dss.asic.cades.ASiCWithCAdESSignatureParameters;
import eu.europa.esig.dss.asic.cades.signature.ASiCWithCAdESService;
import eu.europa.esig.dss.enumerations.ASiCContainerType;
import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.enumerations.SignatureAlgorithm;
import eu.europa.esig.dss.enumerations.SignatureLevel;
import eu.europa.esig.dss.enumerations.SignaturePackaging;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.FileDocument;
import eu.europa.esig.dss.model.SignatureValue;
import eu.europa.esig.dss.model.ToBeSigned;
import eu.europa.esig.dss.model.x509.CertificateToken;
import eu.europa.esig.dss.validation.CommonCertificateVerifier;
import fi.laverca.CmsSignature;
import fi.laverca.MSS_Formats;
import fi.laverca.SignatureProfiles;
import fi.laverca.etsi.EtsiClient;
import fi.laverca.etsi.EtsiRequest;
import fi.laverca.etsi.EtsiResponse;
import fi.laverca.jaxb.mss.MessagingModeType;
import fi.laverca.mss.MssConf;
import fi.laverca.mss.ProfileQueryResponse;
import fi.laverca.util.DTBS;
import fi.laverca.util.X509CertificateChain;
/* ==========================================
 * Laverca Project
 * https://sourceforge.net/projects/laverca/
 * ==========================================
 * Copyright 2017 Laverca Project
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package fi.laverca.examples.etsi;



/**
 * ASiC signing example
 * 
 * <p>Performs the following steps
 * <ol>
 * <li>Gets the Signing Certificate either with a ProfileQuery, or a dummy SignatureRequest
 * <li>Constructs {@link DTBS} from the input documents
 * <li>Sends a SignatureRequest to the MSSP
 * <li>Constructs an ASiC file
 * </ol>
 *
 */
public class ASiC {
    
    private static final DigestAlgorithm    DIGEST_ALG    = DigestAlgorithm.SHA256;
    private static final SignatureAlgorithm SIG_ALG       = SignatureAlgorithm.RSA_SHA256;
    private static final String             DTBS_MIMETYPE = DTBS.MIME_SHA256;
    private static final String             RESULT_FILE   = "signed.asics";
    
    // MSS SignatureProfile
    private static final String MSS_SIG_PROF = SignatureProfiles.ALAUDA_SIGNING;
    
    private String msisdn;
    private List<String> filesToSign;
    private List<DSSDocument>   docs;
    
    // ASiC service
    private ASiCWithCAdESService             service;
    private ASiCWithCAdESSignatureParameters parameters;
    
    // MSS
    private MssConf    conf;
    private EtsiClient client;
    
    public static void main(final String[] args) {
        new ASiC(args).run();
    }
    
    /**
     * Create an ASiC example instance.
     * @param args command arguments to parse
     */
    public ASiC(final String[] args) {

        if (args.length < 2) {
            System.err.println("Usage: msisdn filename1 filename2 ...");
            return;
        }
        // Read args
        this.msisdn      = args[0];
        this.filesToSign = Arrays.stream(args).skip(1).collect(Collectors.toList());
        this.docs        = this.filesToSign.stream().map(FileDocument::new).collect(Collectors.toList());
        
        // Init ASiC service, parameters, and handler
        this.service    = new ASiCWithCAdESService(new CommonCertificateVerifier());
        this.parameters = new ASiCWithCAdESSignatureParameters();
        this.parameters.setSignatureLevel(SignatureLevel.CAdES_BASELINE_B);
        this.parameters.setSignaturePackaging(SignaturePackaging.ENVELOPING);
        this.parameters.setDigestAlgorithm(DIGEST_ALG);
        this.parameters.aSiC().setContainerType(ASiCContainerType.ASiC_S);
        this.parameters.aSiC().setZipComment(true);
        this.parameters.bLevel().setSigningDate(new Date());
        
        this.conf   = MssConf.fromPropertyFile("conf/examples.conf");
        this.client = new EtsiClient(this.conf);
    }
    
    /**
     * Run the example
     */
    public void run() {

        // Create AP_TransID to use. This can be any unique String. 
        String apTransId = "A" + System.currentTimeMillis();
      
        // 1. Get Signing Certificate
        X509CertificateChain chain = null;
        try {
            chain = this.getCertChain(this.msisdn, apTransId);
        } catch (IOException ioe) {
            System.out.println("Failed to get certs from MSSP.");
            ioe.printStackTrace();
            this.client.shutdown();
            return;
        }
        
        // 2. Fill certs to SignatureParameters
        final X509Certificate       signingCert = chain.getSigningCert();        
        final List<CertificateToken> tokenChain = chain.stream()
                                                       .map(CertificateToken::new)
                                                       .collect(Collectors.toList());

        this.parameters.setCertificateChain(tokenChain);
        this.parameters.setSigningCertificate(new CertificateToken(signingCert));
        
        // 3. Send the ASiC signature request
        try {            
            final ToBeSigned dataToSign   = this.service.getDataToSign(this.docs, this.parameters);
            final byte[] dataToSignBytes  = dataToSign.getBytes();
            final byte[] dataToSignDigest = digest(dataToSignBytes);
            
            DTBS dtbs = new DTBS(dataToSignDigest, DTBS.ENCODING_BASE64, DTBS_MIMETYPE);
            
            // Data to be displayed
            String dtbd = dtbs.toString();
            
            EtsiRequest req = this.client.createRequest(apTransId,               // AP Transaction ID
                                                        this.msisdn,             // MSISDN
                                                        dtbs,                    // Data to be signed
                                                        dtbd,                    // Data to be displayed
                                                        null,                    // Additional services
                                                        MSS_SIG_PROF,            // Signature profile
                                                        MSS_Formats.KIURU_PKCS1, // MSS Format
                                                        MessagingModeType.ASYNCH_CLIENT_SERVER);
    
            // 4. Send Signature request
Answer:             EtsiResponse resp = this.client.send(req);