Skip to content

Instantly share code, notes, and snippets.

@kimathie
Last active December 9, 2020 08:09
Show Gist options
  • Save kimathie/8ac76f828ade4ec066bb99dbe20168ca to your computer and use it in GitHub Desktop.
Save kimathie/8ac76f828ade4ec066bb99dbe20168ca to your computer and use it in GitHub Desktop.
Web Services Security SOAP message generator
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Base64;
/**
* Web Services Security (WS-Security) enhancement to SOAP messaging to provide
* quality of protection through message integrity, message confidentiality, and
* single message authentication.
*
* @author kimathie
*/
public abstract class WSSEUtil {
private static final SecureRandom RANDOM;
/**
* SecureRandom object is initialized in this static initialization block to
* ensure there's only one instance of the SecureRandom object used in a
* multi-threaded environment.
*
* The static initialization block is only called once and is thread safe.
*/
static {
RANDOM = new SecureRandom();
}
/**
* Generates a 64 bit length random number which is then encoded to BASE64.
* The nonce is used to provide a unique value for each request sent to a
* server to counter against replay attacks.
*
* @return a String representation of the Base64 encoded nonce.
*/
public static String createNonce() {
BigInteger integer = new BigInteger(64, RANDOM);
return integer.toString();
}
/**
* Generates a timestamp to indicate the time the request was created.
*
* @return a String representation of UTC local time.
*/
public static String createTimeStamp() {
return LocalDateTime.now().toInstant(ZoneOffset.UTC).toString();
}
/**
* Generates a timestamp to indicate the time after which the request should
* be rejected.
*
* @param expire the minutes to be added to the current time.
* @return a String representation of UTC local time.
*/
public static String expireTimeStamp(long expire) {
return LocalDateTime.now().plusMinutes(expire).toInstant(ZoneOffset.UTC).toString();
}
/**
* This is a cryptographic hash of the password and timestamp and nonce.
*
* @param nonce a unique random value.
* @param createdTimeStamp indicates the time the request started.
* @param password a secret word or phrase used to gain admission
* @return a String representation of the hash
* @throws Exception
*/
public static String createPasswordDigest(String nonce, String createdTimeStamp, String password) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] bytes = digest.digest((nonce + createdTimeStamp + password).getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Generates an XML document containing details of the request to be sent to
* the server.
*
* @param userName a name that uniquely identifies an entity on the server.
* @param createTimeStamp indicates the time the request started.
* @param expireTimeStamp indicates the time the request should be rejected.
* @param nonce a unique random value.
* @param passwordDigest cryptographic hash of the password and timestamp
* and nonce.
* @param messageBody message to be included in the XML document request.
* @return an XML document request.
*/
public static String createXML(String userName, String createTimeStamp, String expireTimeStamp, String nonce, String passwordDigest, String messageBody) {
if (userName == null || userName.isEmpty()) {
throw new IllegalArgumentException("'userName' cannot be null or empty");
}
if (createTimeStamp == null || createTimeStamp.isEmpty()) {
throw new IllegalArgumentException("'createTimeStamp' cannot be null or empty");
}
if (expireTimeStamp == null || expireTimeStamp.isEmpty()) {
throw new IllegalArgumentException("'expireTimeStamp' cannot be null or empty");
}
if (nonce == null || nonce.isEmpty()) {
throw new IllegalArgumentException("'nonce' cannot be null or empty");
}
if (passwordDigest == null || passwordDigest.isEmpty()) {
throw new IllegalArgumentException("'passwordDigest' cannot be null or empty");
}
if (messageBody == null || messageBody.isEmpty()) {
throw new IllegalArgumentException("'messageBody' cannot be null or empty");
}
return "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" "
+ "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" "
+ "xmlns:main=\"http://main.dataexchange.billpaygw\">"
+ "<soapenv:Header>"
+ "<wsse:Security >"
+ "<wsu:Timestamp >"
+ "<wsu:Created>" + createTimeStamp + "</wsu:Created>"
+ "<wsu:Expires>" + expireTimeStamp + "</wsu:Expires>"
+ "</wsu:Timestamp>"
+ "<wsse:UsernameToken>"
+ "<wsse:Username>" + userName + "</wsse:Username>"
+ "<wsse:Password Type=\"wsse:PasswordDigest\">" + passwordDigest + "</wsse:Password>"
+ "<wsse:Nonce>" + Base64.getEncoder().encodeToString(nonce.getBytes()) + "</wsse:Nonce>"
+ "<wsu:Created>" + createTimeStamp + "</wsu:Created> "
+ "</wsse:UsernameToken>"
+ "</wsse:Security>"
+ "</soapenv:Header>"
+ "<soapenv:Body>"
+ messageBody
+ "</soapenv:Body>"
+ "</soapenv:Envelope>";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment