What's wrong with Spring's #RequestBody byte[] (length bigger than expected)? - spring

I tried to use #RequestBody byte[] data as automatic Base64 decoder, in order to receive some RSA encrypted data and afterwards decrypt it in my service:
Controller:
#RequestMapping
void doIt(#RequestBody byte[] data) {
service.doIt(data);
}
Service:
void doIt(byte[] encryptedData) {
String testDataString = "hgLGtzF4D8tlJbVivQgaXXwfI9TbQ//PrYHEez5k93sqJSi17eLCa+r/cGNGvoxDRaPYONvP4yxW0ajKDFrusQ4V4owfhkijS9KzBOTjBeAVmA+5jBsZCdoxwCA65DiP5lJ+GRbn8CjcjCr4DaEWFbWHsyvY4NGGAQLuYv+PyZipfU9pXTEDsBb15NwaHlD5m7Z4CHWdWTt1ARvRaQs56Bp63/IEmGR7w4brA1+iuKPv83FLh0rsxyoJ+F8TeqtuPhm2fHTh1FiHn0Bpaqqoyd/cBl0/utSzu4qoZhB3AiVgLjnT6Iy9p5nVoAozxQo/Es59LrpGZfjYJer073jNIg==";
byte[] testDataBytes = Base64.decodeBase64(testDataString);
System.out.println(encryptedData.length);
System.out.println(testDataBytes.length);
System.out.println(new String(encryptedData).length());
System.out.println(Base64.encodeBase64String(testDataBytes).length());
System.out.println(new String(encryptedData).equals(Base64.encodeBase64String(testDataBytes)));
}
Request:
Content-Type: application/octet-stream
Request Payload:
hgLGtzF4D8tlJbVivQgaXXwfI9TbQ//PrYHEez5k93sqJSi17eLCa+r/cGNGvoxDRaPYONvP4yxW0ajKDFrusQ4V4owfhkijS9KzBOTjBeAVmA+5jBsZCdoxwCA65DiP5lJ+GRbn8CjcjCr4DaEWFbWHsyvY4NGGAQLuYv+PyZipfU9pXTEDsBb15NwaHlD5m7Z4CHWdWTt1ARvRaQs56Bp63/IEmGR7w4brA1+iuKPv83FLh0rsxyoJ+F8TeqtuPhm2fHTh1FiHn0Bpaqqoyd/cBl0/utSzu4qoZhB3AiVgLjnT6Iy9p5nVoAozxQo/Es59LrpGZfjYJer073jNIg==
The Output:
(Spring) Byte length: 344
(test) Byte length: 256
(Spring) Base64 String length: 344
(test) Base64 String length: 344
Base64 String equals: true
As you can see:
The request contains exactly the same data as the test data I put into the source code.
After doing Base64 encoding both have the same length and are `equal``
BUT: The length of the byte arrays are different.
Question:
Why is the byte[] that was generated by Spring bigger? (It's useless when trying to decode it using some RSA private key, because RSA expects a length of 256.)
Workaround:
Controller:
#RequestMapping
void doIt(#RequestBody String data) {
service.doIt(Base64.decodeBase64(data));
}

In Spring's ByteArrayHttpMessageConverter, you can find the method readInternal which converts the contents of a request into the desired format (in this case byte[]).
The code (Spring 4.0.0.RELEASE):
long contentLength = inputMessage.getHeaders().getContentLength();
ByteArrayOutputStream bos = new ByteArrayOutputStream(contentLength >= 0 ? (int) contentLength : StreamUtils.BUFFER_SIZE);
StreamUtils.copy(inputMessage.getBody(), bos);
return bos.toByteArray();
The second line says: *If the request payload isn't empty take it's content length, and set the capacity of the ByteArrayOutputStream with that. Else if the content length is < 0, then set it to StreamUtils.BUFFER_SIZE (= 4096).*
Im not really sure why it is implemented this way, but that's how it actually works.

Related

Check if redis key is hash or string using restTemplate

I have Redis DB consisting of hashes and strings.
I got all keys from DB using this code:
Set<byte[]> keys = redisTemplate.getConnectionFactory().getConnection().keys("*".getBytes());
Iterator<byte[]> it = keys.iterator();
while(it.hasNext()){
byte[] data = (byte[])it.next();
String key = (new String(data, 0, data.length));
System.out.println(key);
}
from here: How to get all Keys from Redis using redis template
Since the key can be hash or string, how to determine when I can use opsForHash and opsForValue i.e. how to check if it is hash or it is a string in spring boot using restTemplate.
The Redis command to get the type of a key is TYPE: https://redis.io/commands/type
You can use RedisTemplate's public DataType type(K key) method to execute this: https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/RedisTemplate.html#type-K-
Here's an example:
Set<byte[]> keys = redisTemplate.getConnectionFactory().getConnection().keys("*".getBytes());
Iterator<byte[]> it = keys.iterator();
while(it.hasNext()){
byte[] data = (byte[])it.next();
String key = (new String(data, 0, data.length));
DataType type = redisTemplate.type(key);
if (type == DataType.HASH) {
// ...
} else if (type == DataType.STRING) {
// ...
}
}
Edit: one more piece of advice is that you may want to use SCAN instead of KEYS * (mentioned in one answer to the SO question you linked). Scan is generally better in production since it doesn't try to get and return all keys at once.

How to satisfy multiple byte ranges when using PushStreamContent in WebAPI?

I need to use PushStreamContent because of the source of my data (effectively have to concatenate blobs), but I also have to support requests for multiple byte ranges (arbitrary ranges not aligned to the stored blobs). What is not clear to me is if I can use PushStreamContent to generate a multipart/byteranges response, if each range needs to be separated in the response, and if so, how to do it, and how it relates to the chunked transfer encoding which PushStreamContent invokes.
You can do it using MultipartContent like this:
public class MyRangeController : ApiController
{
[HttpGet]
public HttpResponseMessage Get()
{
// Create a multi-part content object for the response; note that per RFC spec, subtype must be "byteranges"
// Note that the content type of the over-all response will be "multipart/byteranges"
// We choose to use a GUID string for the separator; it could be anything suitable.
var multipartContent = new MultipartContent("byteranges", Guid.NewGuid().ToString("D"));
// Create the response object and set its content
var response = new HttpResponseMessage(HttpStatusCode.PartialContent) { Content = multipartContent };
foreach (var rangeItemHeaderValue in Request.Headers.Range.Ranges)
{
// Create PushStreamContent object for our current byte range...
var pushStreamContent = new PushStreamContent((stream1, content, arg3) =>
{
// Write to stream1
stream1.Close();
});
// We need to add certain headers to each part of the response
pushStreamContent.Headers.ContentRange = new ContentRangeHeaderValue(rangeItemHeaderValue.From.Value, rangeItemHeaderValue.To.Value, /* total size of the resource */);
pushStreamContent.Headers.ContentType = new MediaTypeHeaderValue(/* Set a content type for each part of the response */);
// Add the part to the multi-part content response
multipartContent.Add(pushStreamContent);
}
return response;
}
}

Spring Framework and encode/decode of public key

I am trying to create a new RsaVerifier to check a public key:
JwtHelper.decodeAndVerify(token, verifier);
I do believe it's a valid public key. I'm copying it correctly from my browser. It does begin with a return character though. It actually has them in several places:
-----BEGIN PUBLIC KEY-----\nMIIBI.....
It fails when creating an RsaVerifier.
SignatureVerifier verifier = null;
if (key.startsWith("-----BEGIN PUBLIC KEY-----")) {
logger.log("Trying to get key verifier...");
verifier = new RsaVerifier(key); //FAILS HERE
...
Specifically, it's failing on the last line in this snippet from RsaKeyHelper:
private static Pattern PEM_DATA = Pattern.compile("-----BEGIN (.*)-----(.*)-----END (.*)-----", Pattern.DOTALL);
static KeyPair parseKeyPair(String pemData) {
Matcher m = PEM_DATA.matcher(pemData.trim());
if (!m.matches()) {
throw new IllegalArgumentException("String is not PEM encoded data");
}
String type = m.group(1);
final byte[] content = b64Decode(utf8Encode(m.group(2))); //FAILS HERE
with error:
Exception validating token :
org.springframework.security.jwt.codec.InvalidBase64CharacterException: Bad
Base64 input character decimal 92 in array position 0
I know decimal 92 is a forward slash and I have one in position 0, in fact. However, I've tried removing the return characters and it doesn't recognize it as valid. Here's a link to the RsaKeyHelper class:
https://www.programcreek.com/java-api-examples/index.php?source_dir=spring-security-oauth-master/spring-security-jwt/src/main/java/org/springframework/security/jwt/crypto/sign/RsaKeyHelper.java
I am struggling to figure out how I can pass this key as a string that is accepted.

How to Decode Unicode Characters from Cookie

I am saving a list in String format by encoding it to UTF-8. But I see that {, }, :... and more symbols are in cookie value.
%7B%22evt%22%3A%5B%5D%2C%22exc%22%3A%5B%5D%2C%22tourQuantity%22%3A%221%22%2C%22tourId%22%3A%22067e61623b6f4ae2a1712470b63dff00%22%2C%22room%22%3A%7B%22accId%22%3A%226%22%2C%22roomTypeId%22%3A%225%22%7D%7D
Above one is the stored value in the cookie.
public ResponseEntity < ModelAndView > saveReservation(#RequestBody String reservation, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Cookie cookie = new Cookie("tourReservation", URLEncoder.encode(reservation, "UTF-8"));
cookie.setMaxAge(24 * 60 * 60);
cookie.setPath("/tour/reservation");
response.addCookie(cookie);
List < ? > list = service.saveRes(reservation);
if (list.size() > 0) {
.........
return new ResponseEntity < ModelAndView > (model, HttpStatus.OK);
}
return new ResponseEntity < ModelAndView > (new ModelAndView("tour"), HttpStatus.BAD_REQUEST);
}
How can I get my list string in a good format? I also used StringEscapeUtils, I got an error java.lang.IllegalArgumentException: An invalid character [34] was present in the Cookie value.
org.apache.commons.lang.StringEscapeUtils.unescapeJava(reservation)
Leave as it is. Get the cookie value in JavaScript and use unescape(str) or
decodeURIComponent(str) function to decode it.
Note: unescape() is deprecated so you may use decodeURIComponent() instead.

How to cast "byte[] publicKey" to "Data Object" (to use ObjectHandle)

I'm extracting the Public key for encryption from the sender's Certificate as byte[] array
JObject o = JObject.Parse(reply);
string certResponse = o.GetValue("certificate").Value<string>();
byte[] certByteArray = Encoding.ASCII.GetBytes(certResponse);
//Extract Public key from Certificate
var certTest = new X509Certificate2(certByteArray);
var certPublicKey = certTest.GetPublicKey();
I wish to use PKCS11Interop Encrypt() function which takes ObjectHandle of the key to Encrypt message/data. The only way I find is to set ObjectAttributes and session.CreateObject(objectAttributes); and eventually DestroyObject.
However I get CKR_FUNCTION_NOT_SUPPORTED calling CreateObject(). As clarified by jariq in this post that OpenSC PKCS#11 library does not support/implement some functions defined in PKCS#11 specification, What is the alternative/workaround to use the byte array publicKey for encryption?

Resources