I have a client that I generate using the WSDL that I have. When I tried to connect to this WebService, I get the following error:
javax.xml.ws.soap.SOAPFaultException: An error occurred when verifying security for the message.
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
The actual server that hosts the WebService expects a SOAP message that contains the security header like:
<soapenv:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken u:Id="XXXX">
<o:Username>XXXX</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXX</o:Password>
</o:UsernameToken>
</o:Security>
</soapenv:Header>
How to get this along with my SOAP message that gets generated when the request is made? This is what I do in my code to call the WebService:
val factory = new org.apache.cxf.jaxws.JaxWsProxyFactoryBean()
factory.setServiceClass(classOf[MyWebService])
// if there is an address, then let's set it!
factory.setAddress("https://my/server/address/MyWebService.svc")
// log incoming and outgoing
factory.getInInterceptors.add(new org.apache.cxf.interceptor.LoggingInInterceptor())
factory.getOutInterceptors.add(new org.apache.cxf.interceptor.LoggingOutInterceptor())
val myServiceClient = factory.create().asInstanceOf[MyWebService]
myServiceClient.myMethod("param")
Any suggestions?
Here is what you have to do to get this working:
1. Add the following dependencies (if you have not added it already)
"org.apache.cxf" % "cxf-rt-ws-security" % "3.1.4",
"org.apache.ws.security" % "wss4j" % "1.6.13",
2. In the place where you set information in the client proxy, add the following:
val myWebService = factory.create().asInstanceOf[MyWebService]
val proxy = ClientProxy.getClient(myWebService)
val endpoint = proxy.getEndpoint
import collection.JavaConversions._
val callbackHandler = new CallbackHandler {
override def handle(callbacks: Array[Callback]): Unit = {
val pc = callbacks(0).asInstanceOf[WSPasswordCallback]
pc.setPassword("your password")
}
}
val outProps = Map(
WSHandlerConstants.ACTION -> WSHandlerConstants.USERNAME_TOKEN,
WSHandlerConstants.USER -> "Your user name",
WSHandlerConstants.PASSWORD_TYPE -> WSConstants.PW_TEXT
)
outProps.updated(WSHandlerConstants.PW_CALLBACK_REF, callbackHandler)
val wssOut = new WSS4JOutInterceptor(outProps)
endpoint.getOutInterceptors.add(wssOut)
// set the credentials, timeouts etc
proxy.getRequestContext().put("password", "your password")
Related
im new to Gatling and have been trying to setup a test where my users login, get an access token, then perform some simple get requests using that token. Having 1-2 users works fine, however once i start ramping up the users i start getting spammed with this error:
[ERROR] i.g.h.a.HttpRequestAction - 'httpRequest-2' failed to execute: No attribute named 'access_token' is defined
Im thinking it could have something to do with the way I am saving and using the access token ?
class GatlingTest extends Simulation {
val httpProtocol = http
.baseUrl("https://myurl.com/api/v1")
.inferHtmlResources(BlackList(""".*\.js""", """.*\.css""", """.*\.gif""", """.*\.jpeg""", """.*\.jpg""", """.*\.ico""", """.*\.woff""", """.*\.woff2""", """.*\.(t|o)tf""", """.*\.png""", """.*detectportal\.firefox\.com.*"""), WhiteList())
.acceptLanguageHeader("en-GB,en;q=0.5")
.upgradeInsecureRequestsHeader("1")
object GetUserData {
val userData = exec(http("Get_User_Data")
.get("/user")
.header("Authorization", "Bearer ${access_token}"))
.pause(1)
}
object GetUserInfo {
val userInfo = exec(http("Get_User_Info")
.get("/userInfo")
.header("Authorization", "Bearer ${access_token}")
.header("Accept", "application/json"))
.pause(1)
}
object Login {
val sentHeaders = Map("api_key" -> "nnxzv336wt2374h6zw5x24qd", "Content-Type" -> "application/x-www-form-urlencoded", "Accept" -> "application/json")
val login = exec(http("Login_User")
.post("/login")
.basicAuth("username", "password")
.headers(sentHeaders)
.body(StringBody("grant_type=password&username=username#username.local&password=12345"))
.check(jsonPath("$.access_token").saveAs("access_token"))
)
}
val user = scenario("User").exec(Login.login).exec(GetUserData.userData, GetUserInfo.userInfo)
setUp(
user.inject(
rampUsers(5).during(2.seconds),
).protocols(httpProtocol)
)
}
I have added Authorization Bearer to the get requests, like i mentioned it does work, but as soon as 3+ users are involved i get the error.
It means the login request failed and hence, the user wasn't able to capture the access_token there.
I created a mock service in SoapUI. I am using Groovy in this mock service so I can mock some requests, as well as forward other requests to the actual web service I am mocking.
When the web service returns one of three possible fault messages, I am unable to retrieve that actual fault from the soap response.
The mock service Groovy script just replies with the response herebelow (IOException, http status 500).
But when sending a request to the actual web service directly, I get the response I actually would like to get.
Groovy code which forwards the request and retrieve a response:
def soapUrl = new URL("[actual web service]");
def connection = soapUrl.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type" ,"text/html");
connection.setRequestProperty("SOAPAction", "");
connection.doOutput = true;
Writer writer = new OutputStreamWriter(connection.outputStream);
writer.write(soapRequest);
writer.flush();
writer.close();
connection.connect();
def soapResponse = connection.content.text;
// alert.showInfoMessage(soapResponse);
requestContext.responseMessage = soapResponse;
Response using the Groovy scripted mock service:
<soapenv:Body>
<soapenv:Fault>
<faultcode>Server</faultcode>
<faultstring>Failed to dispatch using script; java.io.IOException: Server returned HTTP response code: 500 for URL: [the endpoint url]</faultstring>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
Response when accessing the web service directly (with the same request):
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server</faultcode>
<faultstring> [actual fault message] </faultstring>
<detail> [useful details about the fault] </detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
When using the script, why is the response not the same as if I would retrieve it directly?
Ok I found out I can use the connection (URLConnection) in a different way.
I made some changes based on the accepted answer here.
Now, the actual response, happy or error, is retrieved. So in both cases the web service response is being forwarded to the mock service output. And now I can see the fault info in the response.
...
connection.connect();
// Get the response
HttpURLConnection httpConnection = (HttpURLConnection) connection;
InputStream is;
if (httpConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
is = httpConnection.getInputStream();
} else {
// Http error
is = httpConnection.getErrorStream();
}
// Read from input stream
StringBuilder builder = new StringBuilder();
BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = buffer.readLine()) != null) {
builder.append(line);
}
buffer.close();
// Forward the response to mock service output
requestContext.responseMessage = builder.toString();
Edit the Web socket header response sent from server to client.
I am creating a websocket server application using playframework. Right now the websocket response from the server is
taken care by Play. Following is the response header,
Request Header:
(UpgradeToWebSocket,),
(Host,localhost:8083),
(Connection,Upgrade),
(Upgrade,websocket),
(Sec-WebSocket-Version,13),
(Accept-Encoding,gzip, deflate, br),
(Accept-Language,en-US,en;q=0.9),
(Sec-WebSocket-Key,ZvfzpVo3EX4DFA4BRcgRIA==)
def chatSystem(): WebSocket = WebSocket.acceptOrResult[String, String] { request =>
Future.successful{
AuthenticationService.doBasicAuthentication(request.headers) match {
case Results.Ok => Right(ActorFlow.actorRef { out => ChatServiceActor.props(out) })
case _ => Left(Unauthorized)
}
}
}
I want to validate Sec-WebSocket-Protocol if it is present in the request header or add the same with value in the server response if it is not present.
I used the following code:
// Defined at http://tools.ietf.org/html/rfc6455#section-4.2.2
val MagicGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
def websocketAcceptForKey(key: String): String = {
val sha1 = MessageDigest.getInstance("sha1")
val salted = key + MagicGuid
val hash = sha1.digest(salted.asciiBytes)
val acceptKey: String = Base64.rfc2045().encodeToString(hash, false)
acceptKey
}
Use it like the following:
val wsKey: Optional[HttpHeader] = request.getHeader("Sec-WebSocket-Key")
val wsAccept = if (wsKey.isPresent) Some(RawHeader("Sec-WebSocket-Accept", websocketAcceptForKey(wsKey.get.value()))) else None
I'm using Akka (version 2.5.18) to send JSON strings to a specific server via https. I have used a poolRouter (balancing-pool with 10 instances) in order to create a pool of actors that are going to send JSONs (generated from different customers) to a single server:
val router: ActorRef = system.actorOf(
FromConfig.props(Props(new SenderActor(configuration.getString("https://server.com"), this.self))),
"poolRouter"
)
The project specification says that the requests can also be sent using curl:
curl -X PUT --cert certificate.pem --key private.key -H 'Content-Type: application / json' -H 'cache-control: no-cache' -d '[{"id" : "test"}] 'https://server.com'
Where "certificate.pem" is the tls certificate of the customer and "private.key" is the private key used to generate the CSR of the customer.
I'm using a balancing-pool because I will have a very big set of certificates (one for each customer) and I need to send the requests concurrently.
My approach is to have a "SenderActor" class that will be created by the balancing pool. Each actor, upon the reception of a message with a "customerId" and the JSON data generated by this customer, will send a https request:
override def receive: Receive = {
case Data(customerId, jsonData) =>
send(customerId(cid, jsonData))
Each SenderActor will read the certificate (and the private key) based on a path using the customerId. For instance, the customerId: "cust1" will have their certificate and key stored in "/home/test/cust1". This way, the same actor class can be used for all the customers.
According to the documentation, I need to create a HttpsConnectionContext in order to send the different requests:
def send(customerId: String, dataToSend): Future[HttpResponse] = {
// Create the request
val req = HttpRequest(
PUT,
uri = "https://server.com",
entity = HttpEntity(`application/x-www-form-urlencoded` withCharset `UTF-8`, dataToSend),
protocol = `HTTP/1.0`)
val ctx: SSLContext = SSLContext.getInstance("TLS")
val permissiveTrustManager: TrustManager = new X509TrustManager() {
override def checkClientTrusted(chain: Array[X509Certificate], authType: String): Unit = {}
override def checkServerTrusted(chain: Array[X509Certificate], authType: String): Unit = {}
override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty
}
ctx.init(Array.empty, Array(permissiveTrustManager), new SecureRandom())
val httpsConnContext: HttpsConnectionContext = ConnectionContext.https(ctx)
// Send the request
Http(system).singleRequest(req, httpsConnContext)
}
The problem I have is that I don't have any clue about how to "set the certificate and the key" in the request, so that the server accepts them.
For instance, I can read the certificate using the following code:
import java.util.Base64
val certificate: String => String = (customer: String) => IO {
Source.fromInputStream(getClass.getClassLoader
.getResourceAsStream("/home/test/".concat(customer).concat("_cert.pem")))
.getLines().mkString
}.unsafeRunSync()
val decodedCertificate = Base64.getDecoder.decode(certificate(customerId)
.replaceAll(X509Factory.BEGIN_CERT, "").replaceAll(X509Factory.END_CERT, ""))
val cert: Certificate = CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(decodedCertificate))
But I don't know how to "set" this certificate and the private key in the request (which is protected by a passphrase), so that the server accepts it.
Any hint or help would be greatly appreciated.
The following allows making a https request and identifying yourself with a private key from a x.509 certificate.
The following libraries are used to manage ssl configuration and to make https calls:
ssl-config
akka-http
Convert your pem certificate to pks12 format as defined here
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt
Define key-store in your application.conf. It supports only pkcs12 and because of
this step 1 is required.
ssl-config {
keyManager {
stores = [
{
type = "pkcs12"
path = "/path/to/pkcs12/cetificate"
password = changeme //the password is set when using openssl
}
]
}
}
Load ssl config using special akka trait DefaultSSLContextCreation
import akka.actor.ActorSystem
import akka.actor.ExtendedActorSystem
import akka.http.scaladsl.DefaultSSLContextCreation
import com.typesafe.sslconfig.akka.AkkaSSLConfig
import com.typesafe.sslconfig.ssl.SSLConfigFactory
class TlsProvider(val actorSystem: ActorSystem) extends DefaultSSLContextCreation {
override protected def sslConfig: AkkaSSLConfig =
throw new RuntimeException("Unsupported behaviour when creating new sslConfig")
def httpsConnectionContext() = {
val akkaSslConfig =
new AkkaSSLConfig(system.asInstanceOf[ExtendedActorSystem], SSLConfigFactory.parse(system.settings.config))
createClientHttpsContext(akkaSslConfig)
}
}
Create a https context and use in http connection pool.
Http(actorSystem).cachedHostConnectionPoolHttps[RequestContext](
host = host,
port = portValue,
connectionContext = new TlsProvider(actorSystem).httpsConnectionContext()
)
Or set connection context to Http(actorSystem).singleRequest method.
In summary, I used ssl-config library to manage certificates instead of doing it programmatically yourself. By defining a keyManager in a ssl-config, any http request done with help of custom httpsConnectionContext will use the certificate to identify the caller/client.
I focused on describing how to establish a https connection using client certificate. Any dynamic behavior for managing multiple certificates is omitted. But I hope this code should be able give you understanding how to proceed.
I am trying to get some data from a REST web service. So far I can get the data correctly if I don't use HTTPS with this code working as expected -
val client = Http.client.newService(s"$host:80")
val r = http.Request(http.Method.Post, "/api/search/")
r.host(host)
r.content = queryBuf
r.headerMap.add(Fields.ContentLength, queryBuf.length.toString)
r.headerMap.add("Content-Type", "application/json;charset=UTF-8")
val response: Future[http.Response] = client(r)
But when I am trying to get the same data from https request (Following this link)
val client = Http.client.withTls(host).newService(s"$host:443")
val r = http.Request(http.Method.Post, "/api/search/")
r.headerMap.add("Cookie", s"_elfowl=${authToken.elfowlToken}; dc=$dc")
r.host(host)
r.content = queryBuf
r.headerMap.add(Fields.ContentLength, queryBuf.length.toString)
r.headerMap.add("Content-Type", "application/json;charset=UTF-8")
r.headerMap.add("User-Agent", authToken.userAgent)
val response: Future[http.Response] = client(r)
I get the error
Remote Info: Not Available at remote address: searchservice.com/10.59.201.29:443. Remote Info: Not Available, flags=0x08
I can curl the same endpoint with 443 port and it returns the right result. Can anyone please help me troubleshoot the issue ?
Few things to check:
withTls(host)
needs to be the host name that is in the certificate of server (as opposed to the the ip for instance)
you can try:
Http.client.withTlsWithoutValidation
to verify the above.
Also you might want to verify if the server checks that the host header is set, and if so, you might want to include it:
val withHeader = new SimpleFilter[http.Request, http.Response] {
override def apply(request: http.Request, service: HttpService): Future[http.Response] = {
request.host_=(host)
service(request)
}
}
withHeader.andThen(client)
more info on host header:
What is http host header?