Development
- Development
Development
This section describes the details of how to develop your own plug-ins for the SignServer API. It goes through most of the necessary interfaces to get going.
Building customized code
It is possible to have your own code in a separate code tree to avoid a mix of custom code with SignServer project code. This makes it easier to maintain and update the code for future versions. This is done by configuring one or more of the 'custom.' parameters in the build configuration file. The are each described here:
custom.src.java = Should point to an external directory containing the package base of the Java code. These are then included in the compilation at build time.
custom.src.web = Should point to the base of a WAR source tree with WEB-INF/web.xml included. This will replace the default WAR deployed during the build.
custom.build.xml = This can point to a custom build.xml that will be imported from the main build.xml and lets the developer include his own ant tasks if necessary.
custom.commandfactory = Should point to a custom implementation of the interface org.signserver.cli.ISignServerCommandFactory. This gives to ability to extend (or replace) the default CLI with another one. The best way of extending the CLI is to look at how the DefaultSignServerCommandFactory is structured.
Plugin development
The main component in the SignServer is the Worker from which most other components inherits. To get a better overview of how the different component types relate to one and another see illustration 1 in the Overview section.
Most workers work in the same way but with different interfaces to implement but for all of them should the following steps be performed.
- Create a custom class implementing the specified interface. There usually exists a base class implementing the most basic function to simply the implementation even further. If it exists it's recommended to inherit it.
- You can define your own properties that the worker can use for its configuration.
- Make sure the custom class is available to the application server
- Redeploy the SignServer.
- Register the worker in the application by setting a property WORKER<id>.CLASSPATH with a global scope in the global configuration. (Also make sure to set it's crypto tokens class-path, see separate section).
- Reload the service with the CLI reload command.
Implementing Workers
*** The ISigner Interface ***
A Signer is a component used to perform some form of cryptographic processing of requested data and to create a custom signer class it should implement the org.signserver.server.signers.ISigner interface. There exists a BaseSigner that can be inherited taking care of some of the functionality. If the BaseSigner is inherited the only method that needs to be implemented is 'processData() '.
There exists a DummySigner implementation that is used for demonstration purposes.
*** The ITimedService Interface ***
There are two kinds of timed services, singleton or non-singleton. A singleton service is only run at one of the nodes at the time while non-singleton services are run at all nodes simultaneously. If a singleton service fails to run on one of the nodes will one of the other nodes take over the service automatically.
If a service should be singleton or not is determined by a standard property SINGLETON defined in the ServiceConfig class.
Other basic properties used to configure all services are: ACTIVE when set to "TRUE" means that the service is active and should be run. INTERVAL defining the interval in seconds of how often the service should be run. CRON used as a complement to INTERVAL to specify on a calendar basis.
To create a custom timed service class it should implement the org.signserver.server.timedservices.ITimedService interface. There exists a BaseTimedService that can be inherited taking care of most of the basic functionality. If the BaseTimedService is inherited the the only method that needs to be implemented is the 'work()' method.
The work method that needs to be implemented is described here:
/**
* Method that should do the actual work and should
* be implemented by all services. The method is run
* at a periodical interval defined in getNextInterval.
*
* @throws ServiceExecutionFailedException if execution of a service failed
*/
public void work() throws ServiceExecutionFailedException;
There exists a DummyTimedService implementation that is used for demonstration purposes.
*** IValidationService Interface ***
Just as the other worker plug-ins have the validator service a base class taking care of most of the common methods and the only method that needs to be implemented is the 'validate' method below. But for most applications should the DefaultValidationService work. What is probably more interesting is to develop a custom IValidator used to integrate the default validation service against different certificate status repositories. See section called 'Other Customizations' for details of how to implement a Validator.
/**
* Method used to check the validation of a certificate
*
* @param validationRequest
* @return a ValidateResponse
* @throws IllegalRequestException if data in the request didn't conform with the specification.
* @throws CryptoTokenOfflineException if the crypto token isn't online.
* @throws SignServerException for general failure exception during validation
* @see org.signserver.validationservice.common.ValidateRequest
* @see org.signserver.validationservice.common.ValidateResponse
*/
ValidateResponse validate(ValidateRequest validationRequest) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException;
*** IGroupKeyService Interface ***
To customize a group key service is slightly more work. Then need five methods be implemented: 'fetchGroupKey', 'pregenerateGroupKeys', 'swithEncryptionKey', 'removeGroupKeys' and 'getStatus'. The default implementation stores the group keys in database with a reference to the encryption key used, the encryption key is stored in the extended key store. See the JavaDoc and the code for the default group key service for more details of implementing a customized one.
/**
* Main method of a Group Key Service responsible for fetching keys from
* the database.
*
* @param fetchKeyRequest
* @return a FetchKeyReponse
* @throws IllegalRequestException if data in the request didn't conform with the specification.
* @throws CryptoTokenOfflineException if the crypto token isn't online.
* @throws SignServerException for general failure exception during key generation.
* @see org.signserver.groupkeyservice.common.FetchKeyRequest
* @see org.signserver.groupkeyservice.common.FetchKeyResponse
*/
FetchKeyResponse fetchGroupKey(FetchKeyRequest fetchKeyRequest) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException;
/**
* Method that instructs the group key service to pregenerate keys.
* This method is called at periods when the server is having
* a low load. This option is optional to implement, if the
* service doesn't support this method it should return null.
*
*
* @param pregenerateKeysRequest request data
* @return a response containing number of keys generated, etc
* @throws IllegalRequestException if requests contain unsupported data.
* @throws CryptoTokenOfflineException if the crypto token isn't online.
* @throws SignServerException for general failure exception during key generation.
* @see org.signserver.groupkeyservice.common.PregenerateKeysRequest
* @see org.signserver.groupkeyservice.common.PregenerateKeysResponse
*/
PregenerateKeysResponse pregenerateGroupKeys(PregenerateKeysRequest pregenerateKeysRequest) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException;
/**
* Method instructing the key service to switch the encryption key for
* storing the group keys in the database. This to ensure that one encryption
* key isn't exposed through to much data.
*
* This method is optional for the implementing service to implement, if
* it's not implemented it should return null.
*
* @param switchEncKeyRequest request data.
* @return a response containing the result of the operation such as new key index.
* @throws IllegalRequestException if requests contain unsupported data.
* @throws CryptoTokenOfflineException if the crypto token isn't online.
* @throws SignServerException for general failure exception during key generation.
* @see org.signserver.groupkeyservice.common.SwitchEncKeyRequest
* @see org.signserver.groupkeyservice.common.SwitchEncKeyResponse
*/
SwitchEncKeyResponse switchEncryptionKey(SwitchEncKeyRequest switchEncKeyRequest) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException;
/**
* Method instructing the key service to remove old group keys not used anymore
* it up to the caller to check that the implementing service supports the type
* of IRemoveGroupKeyRequest used. The request should contain data specifying which
* keys that should be removed.
*
* This method is optional for the implementing service to implement, if
* it's not implemented it should return null.
*
* @param removeGroupKeyRequests request data.
* @return a response containing the result of the operation such as number of keys actually removed.
* @throws IllegalRequestException if requests contain unsupported data.
* @throws CryptoTokenOfflineException if the crypto token isn't online.
* @throws SignServerException for general failure exception during key generation.
* @see org.signserver.groupkeyservice.common.RemoveGroupKeyResponse
* @see org.signserver.groupkeyservice.common.IRemoveGroupKeyRequest
*/
RemoveGroupKeyResponse removeGroupKeys(IRemoveGroupKeyRequest removeGroupKeyRequests) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException;
/**
* Should return the actual status of the worker, status could be if
* the signer is activated or not, or equivalent for a service.
* @return a WorkerStatus object.
*/
public WorkerStatus getStatus();
*** IMailProcessor Interface ***
Implementing a MailProcessor for the MailSigner is almost exactly the same as for the other components but here it's the 'service' method that needs to be implemented. There also exists a utility class called SMIMEHelper that contains methods for securing emails.
/**
* Main method used when processing mails
* @param mail the mail sent through the SMTP server
* @throws MessagingException if error occurred during processing of mail.
* @throws CryptoTokenOfflineException if the signing token not available at the time of the process.
*/
void service(Mail mail) throws MessagingException, CryptoTokenOfflineException;
Implementing Crypto Tokens
*** The ICryptoToken Interface ***
- A custom crypto token needs to implement the interface org.signserver.server.cryptotokens.ICryptoToken. See P12CryptoToken for an example implementation.
- You can define own properties for a crypto token in the same way as for workers. The properties are sent to the crypto token upon initialization.
- Make sure the custom class is available to the application server
- Redeploy the SignServer.
- Register the crypto token to a worker in the application by setting a property WORKER<id>.CRYPTOTOKEN.CLASSPATH with a global scope in the global configuration. (Also make sure to set it's crypto tokens class-path, see next section).
- Reload the service with the CLI reload command.
The ICryptoToken interface have the following methods that needs to be implemented:
public interface ICryptoToken {
public static final int PURPOSE_SIGN = 1;
public static final int PURPOSE_DECRYPT = 2;
public static final int PROVIDERUSAGE_SIGN = 1;
public static final int PROVIDERUSAGE_DECRYPT = 2;
/**
* Method called after creation of instance.
*
*/
public abstract void init(Properties props) throws CryptoTokenInitializationFailureException;
/**
* Method that returns the current status of the crypto token.
*
* Should return one of the SignerStatus.STATUS_.. values
*/
public abstract int getCryptoTokenStatus();
/**
* Method used to activate SignTokens when connected after being off-line.
*
* @param authenticationcode used to unlock crypto token, i.e PIN for smartcard HSMs
* @throws CryptoTokenOfflineException if SignToken is not available or connected.
* @throws CryptoTokenAuthenticationFailureException with error message if authentication to crypto token fail.
*/
public abstract void activate(String authenticationcode) throws CryptoTokenAuthenticationFailureException, CryptoTokenOfflineException;
/**
* Method used to deactivate crypto tokens.
* Used to set a crypto token too off-line status and to reset the HSMs authorization code.
*
* @return true if deactivation was successful.
*/
public abstract boolean deactivate();
/** Returns the private key (if possible) of token.
*
* @param purpose should one of the PURPOSE_... constants
* @throws CryptoTokenOfflineException if CryptoToken is not available or connected.
* @return PrivateKey object
*/
public abstract PrivateKey getPrivateKey(int purpose) throws CryptoTokenOfflineException;
/** Returns the public key (if possible) of token.
*
* @param purpose should one of the PURPOSE_... constants
* @throws CryptoTokenOfflineException if CryptoToken is not available or connected.
* @return PublicKey object
*/
public abstract PublicKey getPublicKey(int purpose) throws CryptoTokenOfflineException;
/** Returns the signature Provider that should be used to sign things with
* the PrivateKey object returned by this crypto device implementation.
* @param providerUsage should be one if the ICryptoToken.PROVIDERUSAGE_ constants
* specifying the usage of the private key.
* @return String the name of the Provider
*/
public abstract String getProvider(int providerUsage);
/**
* Method returning the crypto tokens certificate if it's included in the token.
* This method should only be implemented by soft crypto tokens which have the certificate
* included in the key store.
*
* All other crypto tokens should return 'null' and let the signer fetch the certificate from database.
*
*/
public abstract Certificate getCertificate(int purpose) throws CryptoTokenOfflineException;
/**
* Method returning the crypto tokens certificate chain if it's included in the token.
* This method should only be implemented by soft crypto tokens which have the certificates
* included in the key store.
*
* All other crypto tokens should return 'null' and let the signer fetch the certificate from database.
*
*/
public abstract Collection<Certificate> getCertificateChain(int purpose) throws CryptoTokenOfflineException;
/**
* Method used to tell the crypto token to create a certificate request using its crypto token.
*/
public ICertReqData genCertificateRequest(ISignerCertReqInfo info) throws CryptoTokenOfflineException;
/**
* Method used to remove a key in the signer that shouldn't be used any more
* @param purpose on of ICryptoToken.PURPOSE_ constants
* @return true if removal was successful.
*/
public boolean destroyKey(int purpose);
}
*** The Extended Crypto Token Interface ***
The default group key service need support for symmetric keys in addition the the functionality provided in the basic crypto token which mainly focuses on asymmetric key functionality.
The extended crypto token adds four more methods that need implementation used to generate exportable keys (symmetric or asymmetric) and to encrypt/decrypt data using symmetric keys.
public interface IExtendedCryptoToken extends ICryptoToken {
/**
* Method instructing the crypto token to generate a key that is returned
*
* @param keyAlg the key algorithm to generate, it's up to the caller to check that the crypto token
* used supports the given value.
* @param keySpec specification of the key, it's up to the caller to check that the crypto token
* used supports the given value.
* @return either a java.security.Key or a java.security.KeyPair depending on type of keyAlg sent to the the crypto token.
* @throws IllegalRequestException if the token doesn't support the given key alg or key spec.
* @throws CryptoTokenOfflineException if the token isn't online.
*/
Serializable genExportableKey(String keyAlg, String keySpec) throws IllegalRequestException, CryptoTokenOfflineException;
/**
* Instructs the crypto token to generate a key stored in the device returning only
* a alias reference to the key.
*
* @param keyAlg the key algorithm to generate, it's up to the caller to check that the crypto token
* @param keySpec keySpec specification of the key, it's up to the caller to check that the crypto token
* used supports the given value.
* @return a reference to the key in that can be used later for encryption/decryption.
*
* @throws IllegalRequestException if the token doesn't support the given key alg or key spec.
* @throws CryptoTokenOfflineException if the token isn't online.
*/
String genNonExportableKey(String keyAlg, String keySpec) throws IllegalRequestException, CryptoTokenOfflineException;
/**
* Method used to encrypt data using a key stored in the crypto token. This
* method should mainly be used for symmetric encryption.
* @param keyRef a alias reference to the key that should be used.
* @param data the data to encrypt.
* @return the encrypted data.
* @throws CryptoTokenOfflineException if the token isn't online.
*/
byte[] encryptData(String keyRef, byte[] data) throws CryptoTokenOfflineException;
/**
* Method used to decrypt data using a key stored in the crypto token. This
* method should mainly be used for symmetric encryption.
* @param keyRef a alias reference to the key that should be used.
* @param data the data to decrypt.
* @return the encrypted data.
* @throws CryptoTokenOfflineException if the token isn't online.
*/
byte[] decryptData(String keyRef, byte[] data) throws CryptoTokenOfflineException;
}
Other Customizations
The IValidator Interface
A Validator is used in the DefaultValidationService to connect to different kinds of certificate status repositories, such as CRL, OCSP, XKMS, database etc. It contains two methods 'validate' used for the actual certificate validation and 'testConnection' used by health check related functionality to check that the connection to the underlying validator resource is alright.
/**
* Main method of a Validation Service responsible for validating certificates.
*
* Important a validator also have to support to check the revocation status of the
* involved CA certificates and should only return Validation object with status REVOKED or VALID
* If the validator doesn't support the given issuer it must return null.
*
*
* @param cert the certificate to validate.
* @return a Validation object or null if the certificate couldn't be looked up in this validator.
* @throws IllegalRequestException if data in the request didn't conform with the specification.
* @throws CryptoTokenOfflineException if the crypto token isn't online.
* @throws SignServerException for general failure exception during validation.
*/
Validation validate(ICertificate cert) throws IllegalRequestException, CryptoTokenOfflineException, SignServerException;
/**
* Optional method used to test the connection to a specific underlying validator implementation.
*
* @throws ConnectException if connection to underlying validator implementation failed.
* @throws SignServerException for general failure exception during validation.
*/
void testConnection() throws ConnectException, SignServerException;
The IAuthorizer Interface
It's possible to integrate the authorization of processable requests with external authorizations applications. All that is needed is a class implementing the IAuthorizer interface containing two methods, 'init' and 'isAuthorized'.
To register that the customized authorizer should be used by a worker, all that's needed to be done is to set the property AUTHTYPE to the class path of the authorizer implementation.
public interface IAuthorizer {
/**
* Method called by the worker upon first call to the authenticator after instantiation.
*
* @param workerId id of worker.
* @param config active worker configuration of worker
* @param em the SignServer EntityManager
* @throws SignServerException if unexpected error occurred during initialization.
*/
void init(int workerId, WorkerConfig config, EntityManager em) throws SignServerException;
/**
*
* Main method determining if the requester is authorized to process the data in the request.
*
* @param request the request data sent to the worker to process.
* @param requestContext containing the optional clientCert client certificate or remote IP of the user, may also contain customly defined data.
* @throws SignServerException if unexpected error occurred during authorization.
* @throws IllegalRequestException if the requester isn't authorized or couldn't be authenticated for some other reason.
*/
void isAuthorized(ProcessRequest request, RequestContext requestContext) throws IllegalRequestException, SignServerException;
}
Using the Global Configuration Store
The global configuration store is a memory bank that workers can use to store data used in ongoing operations. The data can be either node (i.e. only read by the current node) or global scoped.
To access the global configuration store use the getGlobalConfigurationSession() method from the BaseWorker (inherited by most of the base component implementations). The returned GlobalConfigurationSession have the following methods that can be used (the other ones should be avoided)
/**
* Method setting a global configuration property. For node. prefix will the
node id be appended.
* @param scope, one of the GlobalConfiguration.SCOPE_ constants
* @param key of the property should not have any scope prefix, never null
* @param value the value, never null.
*/
public void setProperty( java.lang.String scope,java.lang.String
key,java.lang.String value ) ;
/**
* Method used to remove a property from the global configuration.
* @param scope, one of the GlobalConfiguration.SCOPE_ constants
* @param key of the property should start with either glob. or node., never
null
* @return true if removal was successful, othervise false.
*/
public boolean removeProperty( java.lang.String scope,java.lang.String key )
;
/**
* Method that returns all the global properties with Global Scope and Node
scopes properties for this node.
* @return A GlobalConfiguration Object, nevel null
*/
public org.signserver.common.GlobalConfiguration getGlobalConfiguration( ) ;
The getGlobalConfiguration returns a GlobalConfiguration and have a method String getProperty(String scope, String property) that can be used. The value of the property can be user-defined as long as it is guaranteed to be unique over the entire application. Reserved values are all property keys starting with "WORKER".
The Cluster Class Loader
Important, this feature is very new in version 3.1 and should still be considered experimental.
New to SignServer 3.1.x is the Cluster Class Loader. Simplifying the management of code in clusters. Instead of manually having to synchronize all the required JAR files to all nodes, it is possible to upload the code once and it will be accessible to all nodes in the cluster directly without having to restart the cluster. This is archived by storing the JAR resource data (such as class files) in database instead of in the application server class path.
Each worker will have their own Cluster Class Loader instance that will be reinitialized upon reload.
It is also possible to run multiple code versions of a worker simultaneously in a cluster, something that haven't been possible until now since only one version of class can exists in one JVM. This is a great feature for migration purposes where you have an old worker that is known to be stable, then it is possible to upload a new module beside the old one, with newer code, and migrate calling clients one by one. Under the hood is actually a version prefix appended on the fly to the class name by the Cluster Class Loader when converting the byte code into a class. For example a class called somepkg.SomeWorker with version 3 will be called v3.somepkg.SomeWorker internally in the JVM. A developer should have to thing about this when developing his module but it might cause problems when remotely debugging the SignServer since the IDE won't find the source class to the modified code. If remote debugging is going to be used should class versioning be turned off.
To improve the security of the code stored in database it is possible to enforce signature verification of all the uploaded code. This is a good feature if the database is in a different security zone or managed by different administrator than the SignServer nodes itself. If verification is turned on will the Cluster Class Loader check that the class is signed with a certificate issued by issuers stored in a specific trust key store for this purpose. Important, make sure the v the code signing certificate and it's issuers have a validity long enough to avoid production stop since the signature will not verify after the certificate have expired. The code is signed by a separate JKS key store when the module is uploaded to a cluster. The code signing certificate must have the extended key usage 'code signing' in order for the Cluster Class Loader to verify it.
Before code can be uploaded to the Cluster Class Loader must it's JAR files be packaged in something called a Module Archive (MAR). See section 15.2 for details about generating a MAR file.
Building Module Archives
A 'Module Archive' is a package containing all the jars required by the module along with a descriptor with information about the name of the module and it's version. A module archive is uploaded once and is then accessible for all the nodes in the cluster. A module archive can also have multiple parts indicating where in the system the code should be uploaded. In 3.1.x is only one part called 'server' supported, but in the future will other parts such as 'admin' exist. In that case will the code that should be executed on the server, such as worker code, be sent to the cluster, while the administrative part of the code is only sent to the node with an administrative web interface.
A 'Module Archive' have the postfix '.mar' and is managed with the CLI interface using the 'module' commands.
The MAR Descriptor
Every Module archive should have a descriptor file in it's META-INF directory. It follows the JAVA Properties notation and is used to specify general information about the module such as version and name of the module. This file is generated automatically when using the ANT tasks.
The MAR descriptor supports the following properties:
| Property | Description |
|---|---|
| version | The version of the module archive, can only contain digits. If not set "1" will be used. (Optional) |
| modulename | The name of the module, if not set will the MAR file name be used, but without '.mar'. The name will always be converted to upper case. (Optional) |
| default-description | The default description of all the resources in the module (Optional) |
| parts | The parts in the module archive, if not set will only the 'server' part be used. (Optional) |
Including Worker Configurations
It is also possible to include a set of worker configurations in the 'server' part of the module archive, these settings is configured directly after all the JAR files have been uploaded. These worker configurations should be property files that follows the same notation as if they where used with the 'setproperties' CLI command.
By default it should be named 'part-config.properties' and be in the 'server' part directory. It is also possible to have multiple configurations that is used depending on the environment the SignServer cluster is used. For instance it is possible to have one configuration file for test, another for production and a third for development. This makes it easy to manage the module since exactly the same file will be used in all environments. In case of multiple configurations should every property file be named '<environment>-part-config.properties', for instance test-part-config.properties. Later when the module is uploaded and the environment 'test' is specified will the test configuration be used.
Important, if a part-config.properties file is included in the MAR file will the worker properties MODULENAME and MODULEVERION automatically the be set to all workers defined in the configuration file and there is no need to manually define these settings.
Using ANT
The simplest way to build a Module Archive is by using ANT. The SignServer have provided two ant tasks to do this.
All that is required to use these tasks is to add the following two lines in your target:
<taskdef name="mar" classname="org.signserver.anttasks.MarAntTask"
classpathref="signserver.test.compile.classpath"/>
<taskdef name="part" classname="org.signserver.anttasks.PartAntTask"
classpathref="signserver.test.compile.classpath"/>
With these two lines included you will have access the the 'mar' and 'part' tasks. The tasks have the following properties.
MAR Task Properties
| Property | Description |
|---|---|
| version | The version of the module archive, can only contain digits. If not set will "1" be used. (Optional) |
| modulename | The name of the module, if not set will the destfile property be used, but only the file name without '.mar'. Will always be converted to upper case. (Optional) |
| description | The default description of the module that is set in the MAR descriptor.(Optional) |
| destfile | The path and file name of the mar file to generate. (Required) |
| verbose | Set to true if verbose output should be done during execution.(Optional) |
| part | One or more 'part' tasks is required in order to generate a MAR file. (Required) |
PART Task Properties
| Property | Description |
|---|---|
| name | The name of the part, if not set will the default 'server' (and the only part supported in 3.1.x) be used. (Optional) |
| fileset | One or more 'filesets' are required containing JAR file or worker configurations. (Required) |
Here is an example of a simple ANT task building a MAR file, it will have the module name of SOME, version of 3 and have a 'server' part with all the JARs in 'somejarlocation' and all the worker properties in the directory 'someworkerpropertieslocation' :
<target name="gensomemar" depends="signserver.ear">
<taskdef name="mar" classname="org.signserver.anttasks.MarAntTask"
classpathref="signserver.test.compile.classpath"/>
<taskdef name="part" classname="org.signserver.anttasks.PartAntTask"
classpathref="signserver.test.compile.classpath"/>
<mar version="3" destfile="dist/some.mar" verbose="true">
<part>
<fileset dir="${somelibrarylocation}">
<include name="*.jar"/>
</fileset>
<fileset dir="${somejarlocation}">
<include name="*.jar"/>
</fileset>
<fileset dir="${someworkerproperiteslocation}">
<include name="*.properties"/>
</fileset>
</part>
</mar>
</target>
Building Manually
A Module Archive is just like a JAR or EAR file basically a ZIP with a certain file structure. And it is quite easy to create one manually if the ANT task cannot be used. The following rules should be followed when creating a MAR file:
The module descriptor file should be in META-INF/mar-descriptor.properties. If other than just the 'server' part is used, then it is important that the 'parts' property is set correctly. Every part should have a directory with the same name as the part containing all the JAR files and worker configuration files. In 3.1.x is only one part supported and it should be named 'server'. No subdirectories should exists in the parts directory.
Managing Module Archives
Managing of MAR files is done using the CLI command 'signserver.sh/.cmd module' it have three subcommands: add, remove and list. See chapter 17.2.5 for more details.
Here is an example usage of adding a module to a SignServer:
'bin/signserver.sh module add dist-server/tsa.mar demo'
This will load the Time-Stamp Authority module with the demo environment, this requires that the mar file have a file called demo-part-config.properties in the server directory.
Changing the default configuration of the Cluster Class Loader
When enabled, the default configuration of the Cluster Class Loader is to support multiple versions but not requiring signature verification. This behaviour can be changed in the signserver_build.properties file under the 'Cluster Class Loader Configurations' section.
The following properties can be set:
| Property | Description |
|---|---|
| useclusterclassloader | Set to true to enable the use of the cluster class loader. Default is true. |
| clusterclassloader.useclassversions | Indicates if multiple versions of the same worker code should be supported. Default true. Turn this of if remote debugging should be used. |
| clusterclassloader.requiresignature | Set to true if signing of resource data should be required. Default false |
| clusterclassloader.pathtotruststore | Path to the JKS trust store containing all CA certificates that is trusted for signing of resource data. Only required if signature verification is used. |
| clusterclassloader.truststorepwd | Password to unlock the trust store. Only required if signature verification is used. |
Remember to do an 'ant clean' when changing settings in the signserver_build.properties
Testing
There exists some test scripts used to test that the SignServer functions correctly. They are described here.
Automatic Junit Tests
Automatic Junit tests lies in the directory 'src/tests'. There are two different test suites, one for the SignServer build and the other for the MailSigner. The same command applies for both.
Important: For the SignServer test suite to run successful through all the tests must both the Main WebService and validation service WebService API be enabled in the build configuration.
To run the test suite do the following:
- Set the environment variable SIGNSERVER_HOME
- Make sure the sign server is deployed and JBoss is running
- do 'ant test:run'
- A protocol is generated in the directory 'tmp/bin/junit'
Testing the TimeStamp Authority
*** The TSA Test Client ***
There exists a Time Stamp Authority test client that is built with the main distribution. For other workers it's recommended to test it with the WS CLI client described in the 'Main WebService' section.
It only works without client authentication requirements and through HTTP.
To run the client do
ant
cd dist-client
java -jar timeStampClient.jar "http://<hostname>:8080/signserver/tsa?signerId=1"
It will continuously make one request per second.
