In this post, we are going to implement the WSS security in android using KSOAP2 library. Generally, most of the android application is created on the REST-based API but still, there are lots of android application which are created on the SOAP protocol and if you are here then obviously you have a project which required SOAP-based implementation directly from android app and implementing the security in this is very important. So let's start the implement WSS Security in Android using Ksoap2 SOAP Client.
Create SOAP Demo Android Project
To complete learn the implementation of WSS security, first of all, we have will create an android project. So Let's create the project using the Android Studio IDE File->New Project and select the Activity type and type the name of your project and click the finish button.
UI Design of the SOAP Android Application
Designing the Layout of any application is the first and most important part of any application. So we are going to design the layout of our application because I personally thought that if something looks good then it can be sold easily. But here we are not going to invest much time on the UI design we are just taking the username and password as input and sending them to the API server after creating the required header and body, which most difficult part of the calling the WSS secure web service API from android.
Creating a Header of the SOAP Request
Header creation of the SOAP request is the most important part of the implementation of the WSS security in the Android application. Its include the below some important part on which we will come step by step.
- Username token creation
- Nonce creation
- Creating the Password Digest for the soap call
Below is our code to generate our WSS header along with the nonce, created and digest.
import android.util.Base64
import android.util.Log
import org.kxml2.kdom.Element
import org.kxml2.kdom.Node
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.text.SimpleDateFormat
import java.util.Date
import java.util.TimeZone
class WsseToken(
private val user: User?) {
private val nonce: String
private val createdAt: String
private val digest: String?
val wsseHeader: String
get() {
val header = StringBuilder()
header.append("UsernameToken Username=\"")
header.append(this.user?.username)
header.append("\", PasswordDigest=\"")
header.append(this.digest)
header.append("\", Nonce=\"")
header.append(Base64.encodeToString(this.nonce.toByteArray(), Base64.NO_WRAP))
header.append("\", Created=\"")
header.append(this.createdAt)
header.append("\"")
return header.toString()
}
init {
this.createdAt = generateTimestamp()
this.nonce = generateNonce()
this.digest = generateDigest()
}//we need the user object because we need his username
private fun generateNonce(): String {
val random = SecureRandom()
val seed = random.generateSeed(10)
return bytesToHex(seed)
}
private fun generateTimestamp(): String {
sdf.timeZone = TimeZone.getTimeZone("UTC")
return sdf.format(Date())
}
private fun generateDigest(): String? {
var digest: String? = null
try {
val md = MessageDigest.getInstance("SHA-1")
val sb = StringBuilder()
sb.append(this.nonce)
sb.append(this.createdAt)
sb.append(this.user?.password)
val sha = md.digest(sb.toString().toByteArray())
digest = Base64.encodeToString(sha, Base64.NO_WRAP)
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
}
return digest
}
companion object {
val HEADER_AUTHORIZATION = "Authorization"
val HEADER_WSSE = "X-WSSE"
private val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
fun bytesToHex(bytes: ByteArray): String {
val hexArray = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
val hexChars = CharArray(bytes.size * 2)
var v: Int
for (j in bytes.indices) {
v = bytes[j].toInt() and 0xFF
hexChars[j * 2] = hexArray[v.ushr(4)]
hexChars[j * 2 + 1] = hexArray[v and 0x0F]
}
return String(hexChars)
}
}
}
For this, you need to download the KSOAP2 library which you can download from the below here and add it as the library to your project.
Download and add KSOAP2 jar as a library to your project
There are two methods to add the KSOAP2 library to your project. You can choose the method which is based suited for your project.
Add KSOAP2 library using the Gradle package
First, add the maven repository https://oss.sonatype.org/content/repositories/ksoap2-android-releases/ to your project level gradle.properties files as given below.
allprojects {
repositories {
google()
jcenter()
maven { url 'https://oss.sonatype.org/content/repositories/ksoap2-android-releases/' }
}
}
Then add the library to your app level build.gradle properties and sync the project.
implementation 'com.google.code.ksoap2-android:ksoap2-android:3.6.4'
Adding KSOAP2 jar to your project
If the first method does not work due to any reason then you can add the KSOAP2 jar directly to your project. Download the KSOAP2 library from the repository here.
After downloading the jar copy it to the project library folder and by right-clicking on the lib folder.
Once the file is copied in the lib folder of your project just right click on the jar and click on add as library.
If somehow you are not able to add as library option after right click then you can add it by adding the below line into your build.gradle properties files of the project.
implementation files('libs/ksoap2-android-assembly-3.6.4-jar-with-dependencies.jar')
Create the Request Envelope of the SOAP request
For creating the envelope of the request we need to create the instance of the previously created class WsseToken.
private fun soapSerializationEnvelope(user: User?): SoapSerializationEnvelope {
val rawEnvelope = SoapSerializationEnvelope(SoapEnvelope.VER11)
rawEnvelope.implicitTypes = true
rawEnvelope.isAddAdornments = false
rawEnvelope.dotNet = false
val token = WsseToken(user)
rawEnvelope.headerOut = token.xmlHeader
return rawEnvelope
}
In the above code block, we are creating the token component by passing the user as an object. This user object is nothing just a POJO with the username and password that will be used by the WsseToken class to creating the header (nonce, token, created, extra) and taken as input from the end user as described in the start of this post. This method will provide us a raw Envelope that means we need to add the body of our request to this envelope. Our generated header will look like as given below.
<v:Header>
<wsse:Security 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">
<wsse:UsernameToken wsu:Id="UsernameToken-DA34AE4918361575D615616385228214">
<wsse:Username>myusername</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">v9cBHtgupGarwmkE21ORHjgzMkA=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">QjFEREY0REE0NEQxMDlCNTkyRjQ=</wsse:Nonce>
<wsu:Created>2019-07-13T09:01:21Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</v:Header>
Create the SOAP body and add the WS SOAP method to it
We are done with the header creating the header of SOAP request. Now, we need to create the body part of our soap request. So let's create the body of the request. Our body will look like as given below.
<v:Body>
<n0:getUserInfoRequest>
<n0:username>myusername</n0:username>
<n0:usercode>myusercode</n0:userpin>
</n0:getUserInfoRequest>
</v:Body>
So for creating the body, we need to create an instance of the SoapObject as given below.
val request = SoapObject("namespace", "getUserInfoRequest")
After this, we need to add the properties like username and usercode to the body of the request. Both filed are symbolic here and you can put the value as per your requirement in your request body.
request.addProperty("n0:username", "myusername")
request.addProperty("n0:usercode", "myusercode")
It will create the soap body as given in the above example. Ater this we need to add the soap body to our envelope which we have created earlier.
val requestEnvelope = getRawSoapSerializationEnvelope(user)
requestEnvelope.bodyOut = request
requestEnvelope.setOutputSoapObject(request)
Call the SOAP URL endpoint using KSOAP2
So far we have created our request header and body part of the request which is the main task of the Implement WSS Security in Android using Ksoap2 SOAP Client. Now its time to call our SOAP endpoint using our created request. So, create an instance of HttpTransportSE and call the endpoint URL.
val t = HttpTransportSE("Here is your exact end point url")
t.call("", requestEnvelope)
If required we can enable the debug mode of the request using below method of the Http call.
t.debug = true
Make sure to call the above method from some background jobs like because network operators are not allowed on the main threads.
Reading the Request and Response of the HTTP call in Android
If you want to read the request and response that was sent over the HTTP, then you can do as given below.
val requestdump=t.requestDump
val responsedump=t.t.responseDump
Parsing the response of the HTTP call in android
The default parser of the HTTP response does not work due to the complexity of the response so in that case, you can create your own simple parser.
fun getFromXmlResponse(XMLResponse: String): Response {
val response = Response()
val `is` = ByteArrayInputStream(StandardCharsets.UTF_8.encode(XMLResponse).array())
val dbFactory = DocumentBuilderFactory.newInstance()
var dBuilder: DocumentBuilder? = null
try {
dBuilder = dbFactory.newDocumentBuilder()
} catch (e: ParserConfigurationException) {
e.printStackTrace()
}
var doc: Document? = null
try {
assert(dBuilder != null)
doc = dBuilder!!.parse(`is`)
} catch (e: IOException) {
e.printStackTrace()
} catch (e: SAXException) {
e.printStackTrace()
}
var element: Element? = null
if (doc != null) {
element = doc.documentElement
}
element?.normalize()
assert(element != null)
val nList2 = element!!.getElementsByTagName("*")
for (i in 0 until nList2.length) {
val node = nList2.item(i)
val element2 = node as Element
if (element2.tagName == response.tagName) // hear response.tagName is tag that you want capture
response.name = element2.textContent
else if (element2.tagName == response.tagLocation)
response.location =element2.textContent
// and so one you can add the value of all tag as per requirment
}
return response
}
In the above XML parser is a very simple method to parse the response you can create your own parser by taking this as reference depending on the requirement. In the above method, the Response is a simple POJO with the required filed to store the value. If we consider the above two filed then it looks like as given below.
class BalanceResponse {
var name: String? = null
var location: String? = null
companion object {
var tagName = "name"
varocation = "location"
}
}
Wrapping up Implement WSS Security in Android using Ksoap2 SOAP Client
If you go through the post step by step then you will find that it's not so much hard, but finding all these stuff is difficult for me and it takes a lot of my time. So Let's Summary what we have done so far.
Create an Android Project if you don't have a working project. After that download the KSOAP2 library and add it as a library in your build.gradle file, create the header, create the body of the request, call the HTTP endpoint and at last parse and show the result. Below is some reference that you can refer get deeper about the WSS security details.
Comments
Post a Comment