I am upgrading from 5.3 to 6.5 and getting errors using the Jose4J package to validate the JWTs CAS is generating. From what I can tell after jose4j's JWE decrypts the JWT the content type header “cty” is “JWT” indicating that the JWT is still in a nested state and needs to again be decrypted or unsigned. The code works fine with CAS version 5.3.15. I'm using the cas overlay project and last tried version 6.5.7.
It appears that the inner JWT's content type header "cty" is set to "JWT". From my reading only the outer JWT should have it set. Is this a bug in CAS's JWT implementation?
// Step 1: signature validation
JsonWebSignature jws = new JsonWebSignature();
jws.setCompactSerialization(jwtString);
jws.setKey(new AesKey(jwtConfig.getSigningKey().getBytes(StandardCharsets.UTF_8)));
jws.setAlgorithmConstraints(AlgorithmConstraints.DISALLOW_NONE);
if (!jws.verifySignature()) {
logger.error(String.format("jwt have invalid signature:%s", jwtString));
return new ValidationDTO(false, false);
}
// Step 2: check if encryption is fine, but possibly a expired token
final byte[] decodedBytes = Base64.decodeBase64(jws.getEncodedPayload().getBytes(StandardCharsets.UTF_8));
final String decodedPayload = new String(decodedBytes, StandardCharsets.UTF_8);
final JsonWebKey jsonWebKey = JsonWebKey.Factory
.newJwk("\n" + "{\"kty\":\"oct\",\n" + " \"k\":\"" + jwtConfig.getEncriptionKey() + "\"\n" + "}");
JwtConsumer consumer = new JwtConsumerBuilder()
.setSkipAllValidators()
.setDisableRequireSignature()
.setSkipSignatureVerification()
.setDecryptionKey(new AesKey(jsonWebKey.getKey().getEncoded()))
.setJweAlgorithmConstraints(
new AlgorithmConstraints(ConstraintType.WHITELIST,
KeyManagementAlgorithmIdentifiers.DIRECT))
.setJweContentEncryptionAlgorithmConstraints(
new AlgorithmConstraints(ConstraintType.WHITELIST,
ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256)) //this have to match CAS configuration
.build();
JwtContext context = consumer.process(decodedPayload); // <<<<< Exception thrown here. “Invalid JOSE Compact Serialization"
Invalid JWT:JWT processing failed. Additional details: [[17] Unable to process nested JOSE object (cause: org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 14.): {"clientIpAddress":"127.0.0.1","sub":"test#test2121.com","authenticationDate":1659977730,"successfulAuthenticationHandlers":"careerAuthenticationHandler","iss":"https:\/\/jason.crengland.com\/cas","userAgent":"PostmanRuntime\/7.29.2","credentialType":"UsernamePasswordCredential","aud":"https:\/\/jason.crengland.com\/cas","authenticationMethod":"careerAuthenticationHandler","geoLocation":"unknown","serverIpAddress":"127.0.0.1","exp":1660006530,"iat":1659977730,"jti":"TGT-2-xxxxxxxxx-CREJDR-MBP2022"}]
org.jose4j.jwt.consumer.InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process nested JOSE object (cause: org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 14.): {"clientIpAddress":"127.0.0.1","sub":"test#test2121.com","authenticationDate":1659977730,"successfulAuthenticationHandlers":"careerAuthenticationHandler","iss":"https:\/\/jason.crengland.com\/cas","userAgent":"PostmanRuntime\/7.29.2","credentialType":"UsernamePasswordCredential","aud":"https:\/\/jason.crengland.com\/cas","authenticationMethod":"careerAuthenticationHandler","geoLocation":"unknown","serverIpAddress":"127.0.0.1","exp":1660006530,"iat":1659977730,"jti":"TGT-2-xxxxxxxx-CREJDR-MBP2022"}]
at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:406) ~[jose4j-0.7.12.jar:na]
at com.cre.web.security.service.JWTValidationServiceImpl.validate(JWTValidationServiceImpl.java:93) ~[classes/:na]
at com.cre.web.security.service.JWTValidationServiceImpl$$FastClassBySpringCGLIB$$c0ab6de1.invoke(<generated>) [classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) [spring-core-5.3.2.jar:5.3.2]
…...
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_332]
Caused by: org.jose4j.lang.JoseException: Invalid JOSE Compact Serialization. Expecting either 3 or 5 parts for JWS or JWE respectively but was 14.
at org.jose4j.jwx.JsonWebStructure.fromCompactSerialization(JsonWebStructure.java:90) ~[jose4j-0.7.12.jar:na]
at org.jose4j.jwt.consumer.JwtConsumer.process(JwtConsumer.java:320) ~[jose4j-0.7.12.jar:na]
... 70 common frames omitted
Best I can tell, you are correct that the inner JWT has a content type header with "JWT". Which is incorrect and likely a problem with CAS's JWT implementation. The JwtConsumer sees that cty and tries to process the payload as a JWT (JWS or JWE) and that fails b/c the payload is JSON.
Side note that won't help with the cty issue but JwtConsumer can deal with (properly) nested JWTs so you shouldn't have to do any of the JsonWebSignature stuff. Rather build the JwtConsumer with the HMAC key and let it do that work. https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples#markdown-header-producing-and-consuming-a-nested-signed-and-encrypted-jwt shows an example of similar usage.
Related
I use resteasy 2.3.6.Final
And met strange issue with JSON parsing.
I send to server the JSON like:
{
"prop1": "val1",
"prop2": "val2",
"#Class": "come.home.CommonDTO"
}
And have the response:
javax.xml.bind.UnmarshalException - with linked exception: [com.sun.istack.internal.SAXParseException2; columnNumber: 0; unexpected element (uri:"", local:"prop1"). Expected elements are <{}commonDTO>]
So seems that that Resteasy doesn't see JSON properties.
The most strange thing, that it works it my local PC. Bur is not working at remote tesing server.
I was debuged the code and find out, that for parsing JSON, resteasy uploads the list of MessageBodyReader, like:
class: ResteasyProviderFactory
method: getMessageBodyReader
List<ResteasyProviderFactory.SortedKey> readers =
this.messageBodyReaders.getPossible(mediaType, type);
And I have different order of those MessageBodyReaders.
In my local PC is
org.jboss.resteasy.plugins.providers.jackson.ResteasyJacksonProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JsonCollectionProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JsonMapProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonXmlRootElementProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonXmlSeeAlsoProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonXmlTypeProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonElementProvider
org.jboss.resteasy.plugins.providers.DataSourceProvider
org.jboss.resteasy.plugins.providers.StringTextStar
org.jboss.resteasy.plugins.providers.InputStreamProvider
org.jboss.resteasy.plugins.providers.ByteArrayProvider
org.jboss.resteasy.plugins.providers.FileProvider
But in remote server the order is different:
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonXmlRootElementProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JsonCollectionProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JsonMapProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonXmlSeeAlsoProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonXmlTypeProvider
org.jboss.resteasy.plugins.providers.jaxb.json.JettisonElementProvider
org.jboss.resteasy.plugins.providers.jackson.ResteasyJacksonProvider
org.jboss.resteasy.plugins.providers.DataSourceProvider
org.jboss.resteasy.plugins.providers.StringTextStar
org.jboss.resteasy.plugins.providers.InputStreamProvider
org.jboss.resteasy.plugins.providers.ByteArrayProvider
org.jboss.resteasy.plugins.providers.FileProvider
Somehow this JettisonXmlRootElementProvider comes at fist position and breaks the JSON parsing.
I came deeper and foud that Providers are filling in org.jboss.resteasy.core.MediaTypeMap.
They are uploading and sorting:
Collections.sort(matches, new MediaTypeMap.TypedEntryComparator(type));
cached = convert(matches);
if (useCache) {
this.classCache.put(cacheEntry, cached);
}
But! sorting works correctly, and seems like the registration of Providers is incorrect:
class: RegisterBuiltin
method: registerProviders
Enumeration en = Thread.currentThread().getContextClassLoader().getResources("META-
INF/services/" + Providers.class.getName());
And the order of this resources is different in different environments!
So that's the case.
But I have no idea why this order is different...
PS.
In all enviromnents openjdk version "1.8.0_265"
I have a Qt program that stores all my small (tiny) company information on a sql database and I have over the years tailored it to do all my accounting stuff, invoices, BOMs etc.
At the push of a button I can get all of the necessary sql data to calculate a quarterly VAT return, but we're going to have to electronically submit all the data now, not just calculate it. I have all the data needed, it's just a case of submitting over HTTP using json (of which I know a little/nothing about respectively).
I'm small enough so that I don't have to do this submission at the moment, but the time will likely come, so I'm trying the most basic of requests in the HMRC's sandbox as a starting point.
On this page it shows you how to do an hello world request in Java, so I'm trying to do the same with Qt with C++.
I've tried the following which responds to the push of a button and I have of course, set up a slot to deal with a response:
void MainWindow::hello()
{
QJsonObject json;
QString rs("https://test-api.service.hmrc.gov.uk/hello/world");
QNetworkRequest request
{
QUrl(rs)
};
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/vnd.hmrc.1.0+json");
request.setUrl(QUrl(rs));
manager->get(request);
}
and the main window init:
manager = new QNetworkAccessManager();
QObject::connect
(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply)
{
if (reply->error())
{
ui->debugText->appendHtml(reply->errorString());
return;
}
QString answer = reply->readAll();
ui->debugText->appendHtml(answer);
}
);
To which I get the reply:
Error transferring https://test-api.service.hmrc.gov.uk/hello/world -
server replied: Not Acceptable
I assume that means I am communicating with the sever now, but I do not know what this terse error message means!
The Java on the HMRC web page is as follows:
// construct the GET request for our Hello World endpoint
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(
"https://test-api.service.hmrc.gov.uk/hello/world");
request.addHeader("Accept", "application/vnd.hmrc.1.0+json");
// execute the request
HttpResponse response = client.execute(request);
// extract the HTTP status code and response body
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
Is that enough information for someone to point me in the right direction of what I'm doing wrong please? Suspect I am missing a fundamental point here.
In your Java example, you are setting the HTTP header "Accept". In your C++/Qt snippet, your are setting the "Content-Type" header.
You may want to adapt your code like this to match your Java working example:
QNetworkRequest request { QUrl(rs) };
request.setRawHeader(QByteArray("Accept"), QByteArray("application/vnd.hmrc.1.0+json"));
manager->get(request);
I've been hitting the wall and haven't came up with any reasonable solution, so maybe someone will give it a try. I wrote simple service integrating with github, and having hard time to understand how should I work with exceptions in reactive word properly. Once I got expected 404 status error from Github I would like to throw my custom exception and present it to the client instead of valid response, I'm checking code statuses of response from github and the only thing I receive on my site is:
2018-06-26 21:45:08.286 WARN 8336 --- [ctor-http-nio-2]
.a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request
[GET http://localhost:8080/repositories/sh1nen/no-exist]: Response
status 404
Here is my simple method responsible for making requests and handling error codes appropriately.
fun findSpecificOwnerRepository(owner: String, repositoryName: String) = webClient
.get()
.uri("/repos/$owner/$repositoryName")
.retrieve()
.onStatus({ httpStatus -> HttpStatus.NOT_FOUND == httpStatus }, { Mono.error(RepositoryNotFoundException(reason = "Repository $repositoryName not found.")) })
.onStatus({ httpStatus -> HttpStatus.SERVICE_UNAVAILABLE == httpStatus }, { Mono.error(RepositoryNotFoundException(reason = "Service unavailable.")) })
.bodyToMono(GithubRepositoryResponse::class.java)
Here is my custom exception which basically represents no resources on my site to represent:
internal class RepositoryNotFoundException(
status: HttpStatus = HttpStatus.NOT_FOUND,
reason: String? = null,
throwable: Throwable? = null) : ResponseStatusException(status, reason, throwable)
And the endpoint itself which I'm hitting to get the response:
#GetMapping("{owner}/{repositoryName}")
fun findSpecificOwnerRepository(#PathVariable owner: String, #PathVariable repositoryName: String) = githubClient
.findSpecificOwnerRepository(owner, repositoryName)
I would like to get 404 with a message which is hardcoded. Do I need any special #ExceptionHandler in controller to handle my custom exception ?
Is there any chance of implementing situation when for example github is not able to keep up with requests I am serving and throw in that case also some exception? How could it be implemented?
I'm not sure if you are actually missing anything for point 1), as the exception you extend should naturally result in 404 to your clients, if I recall correctly.
About point 2, it all depends on how your source handles rate limiting. In the case of GitHub, it will return a 403 once you hit rate limits, but you can be extra careful and check the custom headers as well. See https://developer.github.com/v3/#rate-limiting
So the simplest way it would be implemented is with onStatus. Alternatively, you can inspect the whole response and act accordingly by using exchange instead of retrieve, and flatMaping on the resulting Mono (that emits the whole server response).
I am very new to bitcoin and this is my first experiment with bitcoind.
We have been trying to develop an Java based application on BTC using bitcoind (using testnet). We are using simple HTTP Post using Jersey client with basic authentication like given below. We already have jersey client as part of project dependencies. We are running on Mac OS. The bitcoind and java client are hosted in the same system.
Client client = Client.create();
String url = "http://"+username+':'+password+"#localhost:18333";
//String url = "http://localhost:18333";
System.out.println("URL is : "+url);
WebResource webResource = client.resource(url);
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication (username, password.toCharArray());
}
});
String input = "{\"method\":\"getblockcount\",\"params\":[],\"id\":\"1\"}";
ClientResponse response = webResource.type("application/json").post(ClientResponse.class, input);
When we execute this, we are getting
Caused by: java.net.SocketException: Unexpected end of file from server
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:772)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:633)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:769)
From the exception what I understand is, there are some server side errors but i am not able to see errors in the log files. The degug.log does not give any details.
The entries in the bitcoin.conf file is as follows:
rpcuser=bitcoinrpc
rpcpassword=5UKQTzWTf7EEJnkShZhr9tbjpDVNmLMgQkFfWsnZhLey
testnet=1
server=1
Also I tried integrating with bitcoind using json-rpc client as well which resulted in the same error.
Really appreciate any help in resolving this error. Thank you in advance. Let me know if you need any further details.
Regards,
Manjunath
====== EDIT ======
When I inspect the request and response, its giving "Remote server closed the connection before sending response header" error as part of HTTP failure scenario. Following is the request data content :
URL : http://192.168.2.111:18333/
Request Data:
{
"method": "getblockcount",
"params": [],
"id": "1"
}
Please help me in understanding where the mistake is.
================ EDIT =================
Added below entries to bitcoin.conf to allow connections from client. But still facing the same error:
rpcallowip=192.168.2.111
rpcallowip=127.0.0.1
Regards,
Manjunath
After all tweaking, I am able to get it working properly. For the benefit of others, here is the Java Code for making JSON-RPC calls to bitcoind (Using Jersey Client):
bitcoin.conf entries :
rpcuser=bitcoinrpc
rpcpassword=5UKQTzWTf7EEJnkShZhr9tbjpDVNmLMgQkFfWsnZhLey
testnet=1
server=1
#txindex=1
rpcallowip=192.168.2.*
rpcallowip=127.0.0.1
rpcport=8999
#rpctimeout=60000
Make sure you change the port number and dont forget to provide rpcallowip entry pointing to respective IP address.
Client Code:
DefaultClientConfig config = new DefaultClientConfig();
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING,
Boolean.TRUE);
Client client = Client.create(config);
client.addFilter(new HTTPBasicAuthFilter(username, password));
WebResource webResource = client.resource(url);
String input = "{\"id\":\"jsonrpc\",\"method\":\"listaccounts\",\"params\":[]}";
ClientResponse response = webResource.accept("application/json").type("application/json")
.post(ClientResponse.class, input);
Thats it. Your good to start with bitcoind integration.
Regards,
Manjunath
I am in the process of connecting to a custom RESTful API using Retrofit. I have testing communicating with the device/API via Curl and with some test Java code running on a non-Android system (mac os x). The curl and java commands return the expected response.
However, using retrofit, I am unsure if I am using an inadequate configuration in my RestAdapter (i.e. RestAdapter.Builder()) to talk to this device.
The error I am getting appears to be that the entire json contents is not returned and only the first 47 bytes or so. I expect this is a behavior of the device I'm talking to, and am curious if this means I need to implement an Asynchronous callback as described in the Retrofit API docs. Before I do this I wanted to get feedback from some who have more experience with Retrofit.
The error is as follows (sanitized for public consumption):
12-06 08:50:52.962 28267-1735/com.mycompany.project D/Retrofit? [ 12-06 08:50:52.972 28267: 1735 D/Retrofit ]
{"OBJECT1":{"#Version":1,"OBJECTARRAY1":[
12-06 08:50:52.972 28267-1735/com.mycompany.project D/Retrofit? <--- END HTTP (46-byte body)
12-06 08:50:52.982 28267-1735/com.mycompany.project W/System.err retrofit.RetrofitError: retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.io.EOFException: End of input at line 1 column 47
I am setting up the RestAdapter as such:
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer(serverUrl)
.setClient(new OkClient(RestUtils.getHttpClient(3000, 3000, username, password)))
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade requestFacade) {
requestFacade.addHeader("Accept", "application/json");
requestFacade.addHeader("Client-Id", "12345");
}
})
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
service = restAdapter.create(OBJECT1.class);
Any insight into this problem would be greatly appreciated. I understand this error is related to the java.io.EOFException, but have been unable to verify the contents I am getting returned from the device, except for the output shown above in the error. I am heavily leaning towards the async vs. sync being the issue, but am open to any recommendations.
Cheers!