Spring Framework and encode/decode of public key - spring

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.

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 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?

TokensRegex: Tokens are null after retokenization

I'm experimenting with Stanford NLP's TokensRegex and try to find dimensions (e.g. 100x120) in a text. So my plan is to first retokenize the input to further split these tokens (using the example provided in retokenize.rules.txt) and then to search for the new pattern.
After doing the retokenization, however, only null-values are left that replace the original string:
The top level annotation
[Text=100x120 Tokens=[null-1, null-2, null-3] Sentences=[100x120]]
The retokenization seems to work fine (3 tokens in result), but the values are lost. What can I do to maintain the original values in the tokens list?
My retokenize.rules.txt file is (as in the demo):
tokens = { type: "CLASS", value:"edu.stanford.nlp.ling.CoreAnnotations$TokensAnnotation" }
options.matchedExpressionsAnnotationKey = tokens;
options.extractWithTokens = TRUE;
options.flatten = TRUE;
ENV.defaults["ruleType"] = "tokens"
ENV.defaultStringPatternFlags = 2
ENV.defaultResultAnnotationKey = tokens
{ pattern: ( /\d+(x|X)\d+/ ), result: Split($0[0], /x|X/, TRUE) }
The main method:
public static void main(String[] args) throws IOException {
//...
text = "100x120";
Properties properties = new Properties();
properties.setProperty("tokenize.language", "de");
properties.setProperty("annotators", tokenize,retokenize,ssplit,pos,lemma,ner");
properties.setProperty("customAnnotatorClass.retokenize", "edu.stanford.nlp.pipeline.TokensRegexAnnotator");
properties.setProperty("retokenize.rules", "retokenize.rules.txt");
StanfordCoreNLP stanfordPipeline = new StanfordCoreNLP(properties);
runPipeline(pipelineWithRetokenize, text);
}
And the pipeline:
public static void runPipeline(StanfordCoreNLP pipeline, String text) {
Annotation annotation = new Annotation(text);
pipeline.annotate(annotation);
out.println();
out.println("The top level annotation");
out.println(annotation.toShorterString());
//...
}
Thanks for letting us know. The CoreAnnotations.ValueAnnotation is not being populated and we'll update TokenRegex to populate the field.
Regardless, you should be able to use TokenRegex to retokenize as you have planned. Most of the pipeline does not depending on the ValueAnnotation and uses the CoreAnnotations.TextAnnotation instead. You can use the CoreAnnotations.TextAnnotation to get the text for the new tokens (each token is a CoreLabel so you can access it using token.word() as well).
See TokensRegexRetokenizeDemo for example code on how to get the different annotations out.

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

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.

How to set the default namespace or how to define the #key values when using google-api and using Atom serialization/parser

I'm having trouble with Atom parsing/serializing - clearly something related to the namespace and the default alias - but I can;t figure out what I'm doing wrong.
I have two methods - one that I'm trying to do a GET and see if an album is defined and what that tries to do a POST to create the album (if it does not exist).
The GET I managed to get working - although there too I'm pretty sure I am doing something wrong because it is different from the PicasaAndroidSample. Specifically, if I define:
public class EDAlbum {
#Key("atom:title")
public String title;
#Key("atom:summary")
public String summary;
#Key("atom:gphoto:access")
public String access;
#Key("atom:category")
public EDCategory category = EDCategory.newKind("album");
}
Then the following code does indeed get all the albums:
PicasaUrl url = PicasaUrl.relativeToRoot("feed/api/user/default");
HttpRequest request = EDApplication.getRequest(url);
HttpResponse res = request.execute();
EDAlbumFeed feed = res.parseAs(EDAlbumFeed.class);
boolean hasEDAlbum = false;
for (EDAlbum album : feed.items) {
if (album.title.equals(EDApplication.ED_ALBUM_NAME)) {
hasEDAlbum = true;
break;
}
}
But - if instead I have:
public class EDAlbum {
#Key("title")
public String title;
#Key("summary")
public String summary;
#Key("gphoto:access")
public String access;
#Key("category")
public EDCategory category = EDCategory.newKind("album");
}
Then the feed has an empty collection - i.e. the parser does not know that this is Atom (my guess).
I can live with the android:title in my classes - I don;t get it, but it works.
The problem is that I can't get the POST to wok (to create the album). This code is:
EDAlbum a = new EDAlbum();
a.access = "public";
a.title = EDApplication.ED_ALBUM_NAME;
a.summary = c.getString(R.string.ed_album_summary);
AtomContent content = new AtomContent();
content.entry = a;
content.namespaceDictionary = EDApplication.getNamespaceDictionary();
PicasaUrl url = PicasaUrl.relativeToRoot("feed/api/user/default");
HttpRequest request = EDApplication.postRequest(url, content);
HttpResponse res = request.execute();
The transport and namespace are:
private static final HttpTransport transport = new ApacheHttpTransport(); // my libraries don;t include GoogleTransport.
private static HttpRequestFactory createRequestFactory(final HttpTransport transport) {
return transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) {
AtomParser parser = new AtomParser();
parser.namespaceDictionary = getNamespaceDictionary();
request.addParser(parser);
}
});
}
public static XmlNamespaceDictionary getNamespaceDictionary() {
if (nsDictionary == null) {
nsDictionary = new XmlNamespaceDictionary();
nsDictionary.set("", "http://www.w3.org/2005/Atom");
nsDictionary.set("atom", "http://www.w3.org/2005/Atom");
nsDictionary.set("exif", "http://schemas.google.com/photos/exif/2007");
nsDictionary.set("gd", "http://schemas.google.com/g/2005");
nsDictionary.set("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#");
nsDictionary.set("georss", "http://www.georss.org/georss");
nsDictionary.set("gml", "http://www.opengis.net/gml");
nsDictionary.set("gphoto", "http://schemas.google.com/photos/2007");
nsDictionary.set("media", "http://search.yahoo.com/mrss/");
nsDictionary.set("openSearch", "http://a9.com/-/spec/opensearch/1.1/");
nsDictionary.set("xml", "http://www.w3.org/XML/1998/namespace");
}
return nsDictionary;
}
If I use
#Key("title")
public String title;
then I get an exception that it does not have a default namespace:
W/System.err( 1957): java.lang.IllegalArgumentException: unrecognized alias: (default)
W/System.err( 1957): at com.google.common.base.Preconditions.checkArgument(Preconditions.java:115)
W/System.err( 1957): at com.google.api.client.xml.XmlNamespaceDictionary.getNamespaceUriForAliasHandlingUnknown(XmlNamespaceDictionary.java:288)
W/System.err( 1957): at com.google.api.client.xml.XmlNamespaceDictionary.startDoc(XmlNamespaceDictionary.java:224)
and if I use
#Key("atom:title")
public String title;
then it does serialize but each element has the atom: prefix and the call fails - when I to a tcpdump on it I see something like
.`....<? xml vers
ion='1.0 ' encodi
ng='UTF- 8' ?><at
om:entry xmlns:a
tom="htt p://www.
w3.org/2 005/Atom
"><atom: category
scheme= "http://
schemas. google.c
om/g/200 5#kind"
term="ht tp://sch
emas.goo gle.com/
photos/2 007#albu
m" /><at om:gphot
o:access >public<
/atom:gp hoto:acc
....
What do I need to do different in order to use
#Key("title")
public String title;
and have both the GET and the POST manage the namespace?
It looks you are adding either duplicate dictonary keys or keys that are not understood by the serializer.
Use the following instead.
static final XmlNamespaceDictionary DICTIONARY = new XmlNamespaceDictionary()
.set("", "http://www.w3.org/2005/Atom")
.set("activity", "http://activitystrea.ms/spec/1.0/")
.set("georss", "http://www.georss.org/georss")
.set("media", "http://search.yahoo.com/mrss/")
.set("thr", "http://purl.org/syndication/thread/1.0");
Removing the explicit set for the "atom" item namespace solved this issue for me.

Resources