WSO2 products, or more precise the carbon core, includes a repository where the certificates and private keys can be stored. This repository is composed by one or more Keystores.
In some products of WSO2, like the Identity Server (IS) or API Manager (APIM), there are situations were a Keystore must be added, modified or deleted in a programmatically way or without access to the user interface. This blog describes the steps to accomplish the mentioned objective and focuses the example on adding a new secondary store after the WSO2 product has been started.
Secondary Keystores
WSO2 products based on Carbon include one primary and, zero or more, secondary Keystores. The primary Keystore is added already out of the box and any replacement or change in the configuration must be done prior to starting the server. There is a self-signed certificate in the wso2carbon.jks which is the reason for giving the warning about ‘not secure’ when starting. This has less to do with WSO2 and more to do with a self-signed certificate which is less secure then one from a Certificate Authority (CA).
To create and configure the primary Keystore there are already some good documents at hand. For example, here is one were the setup is clearly explained: https://www.yenlo.com/blog/setting-up-wso2-with-a-valid-certificate
The available documentation from WSO2 is more vague when it comes to adding or modifying secondary Keystores.
After the Carbon server is started, sure, you can manually modify your Keystores from the Carbon GUI, but if you have a cluster of products which is being automatically spawned from Docker containers or similar technologies, you will probably not want to do that manually every time.
The KeyStoreAdminService
To allow the automatization of the manipulation of WSO2 Keystores, WSO2 Carbon includes the KeyStoreAdminService. This service contains all the add, get, and remove operations needed to cover all the common Keystore use cases.
This service is part of the WSO2 built-in SOAP Web-Services set and its endpoint descriptions can be found via WSDL. All the services’ WSDLs are hidden by default, so if you want to call it you should follow these steps:
- Set the <HideAdminServiceWSDLs> element to false in the [WSO2_HOME]/repository/conf/carbon.xml file
- Restart the Server
- Call the WSDL at https://[WSO2_HOST]:8243/services/KeyStoreAdminService?wsdl
After reviewing the WSDL the following list of supported operations can be noticed:
- addKeyStore
- addTrustStore
- deleteStore
- getKeystoreInfo
- getKeyStores
- getPaginatedKeystoreInfo
- getStoreEntries
- importCertToStore
- removeCertFromStore
Here we sent a SOAP message GetKeyStores, this is a response we get back on the default keystore from, in this case, API Manager 2.1.0.
The names of the operations are self-explanatory so, as a first conclusion, the exchange of SOAP messages with the WSO2 server will be a good option of manipulating the Keystore repository.
Keystore Service SOAP Messages
Sending a message to the WSO2 server is as easy as sending request with the SOAP envelope to the service at https://[WSO2_SERVER]:9443/services/KeyStoreAdminService (do not forget the HTTP authentication header).
For most of the KeyStoreAdminService operations, the envelope parameters are easy to understand and there are ones that don’t even need given values. A couple examples of these are the deleteStore operation where only the keyStoreName value must be given or the getKeyStores operation where the message envelope can be used the same as from the template.
- DeleteStore operation:
<soapenv:Envelope xmlns_soapenv=”http://schemas.xmlsoap.org/soap/envelope/” xmlns_ser=”http://service.keystore.security.carbon.wso2.org”>
<soapenv:Header/>
<soapenv:Body>
<ser:deleteStore>
<ser:keyStoreName>myKeyStore</ser:keyStoreName>
</ser:deleteStore>
</soapenv:Body>
</soapenv:Envelope>
In other types of operations, it is a bit more complicated to understand what parameters are expected, as in the case of addKeyStore Operation.
The following list shows a short list of the parameters requiring values to be set in the envelope of the addKeyStore operation:
- fileData: Base64 encoded Keystore file
- filename: Name of the file after decoding
- password: Keystore password
- provider: Keystore provider
- type: Keystore type
- pvtkeyPass: Keystore private key password
Let’s create a keystore from scratch to follow this example through:
We use the JAVA keytool to do this. To make it simple we are going to create the keystore conversationally, that means that we need to enter information during the creation rather than supplying it as parameters.
The minimum command is:
keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048
This creates a new keystore. During the creation, you need to enter details about the password used in both the keystore as well as the private key as well as the information about the Distinquished Name (the questions about first name etc.). We just enter some dummy values in this example. Because we created a self-signed certificate we do not need to import certificates (and the fact this is just an example, not a real-world setup).
We need to base64 encode the keystore in order to be able to add it to our WSO2 product using the SOAP based Admin Services. We use the upload file functionality and get a txt file back.
An example of how the filled envelope for the addKeyStore operation looks like this:
<soapenv:Envelope xmlns_soapenv=”http://schemas.xmlsoap.org/soap/envelope/” xmlns_ser=”http://service.keystore.security.carbon.wso2.org”>
<soapenv:Header/>
<soapenv:Body>
<ser:addKeyStore>
<ser:fileData>/u3+7QAAAAIAAAABAAAAAQAEbHVpcwAAAV…</ser:fileData>
<ser:filename>newKeyStoreName.jks</ser:filename>
<ser:password>password</ser:password>
<ser:provider>admin</ser:provider>
<ser:type>JKS</ser:type>
<ser:pvtkeyPass>password</ser:pvtkeyPass>
</ser:addKeyStore>
</soapenv:Body>
</soapenv:Envelope>
Encoding the Keystore
If you noticed in the previous section, to add a new Keystore with the addKeyStore SOAP operation it is necessary to encode the Keystore in base64 and add the result string as a parameter value in the envelope.
We need to base64 encode the keystore in order to be able to add it to our WSO2 product using the SOAP based Admin Services. We use the https://www.base64encode.org/ upload file functionality and get a txt file back.
A second option can be programming the encoder yourself.
The following piece of Java8 code shows how to encode the Keystore to a valid string which can be used to call the service.
public static void writeToBase64String(){
Path path = Paths.get("newKeyStoreName.jks");
try {
byte[] data = Files.readAllBytes(path);
String encodedBytes = Base64.getEncoder().encodeToString(data);
System.out.println("Base64 keystore = " + encodedBytes);
Files.write(Paths.get("encodedKeyStore.txt"), encodedBytes.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
After encoding the file just add the result string as the fileData parameter and the message can be sent.
As a final note, it is important to remember that the Keystore must already contain a private key or otherwise it will be rejected by the WSO2 server.
The operation importCertToStore must be also treated in a similar way when setting the fileData parameter.
Conclusion
As stated before, the exchange of SOAP messages with the WSO2 server will be a good option of manipulating the Keystore repository and the process to do it is composed by these steps.
- Generate the Keystore and include a private key
- Encode the Keystore in base64
- Fill the parameters of the required SOAP messages, including the encoded Keystore if required
- Send the SOAP message to https://[WSO2_SERVER]:9443/services/KeyStoreAdminService
That is it. Simple as can be.
If you have any questions about this blogpost contact us via the comments section of this blog. For more View also our WSO2 Tutorials, webinars or white papers for more technical information. Need support? We do deliver WSO2 Product Support, WSO2 Development Support, WSO2 Operational Support and WSO2 Training Programs.
Thanks to Rob Blaauboer for his contribution to this blog.