Discrepancy between Java's public key representation and RFC 8410 - java

RFC 8410 lists this as an example of a Ed25519 public key: MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
Decoding this with an ASN.1 decoder, this becomes:
30 2A
30 05
06 03 2B6570 // Algorithm Identifier
03 21 0019BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1
As expected, this matches the SubjectPublicKeyInfo definition in the RFC.
Using the Sun cryptography provider in Java 11+ I can use this code to generate an X25519 (not Ed25519 - which is the difference in the algorithm identifier below) public key:
import java.security.KeyPairGenerator;
import java.util.Base64;
public class PrintPublicKey {
public static void main(String args[]) throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("X25519");
byte[] encodedPublicKey = generator.generateKeyPair().getPublic().getEncoded();
System.out.println(Base64.getEncoder().encodeToString(encodedPublicKey));
}
}
Which will output something like: MCwwBwYDK2VuBQADIQDlXKI/cMoICnQRrV+4c//viHnXMoB190/z2MX/otJQQw==
Decoding this with an ASN.1 decoder, this becomes:
30 2C
30 07
06 03 2B656E // Algorithm Identifier
05 00 // Algorithm Parameters - NULL
03 21 00E55CA23F70CA080A7411AD5FB873FFEF8879D7328075F74FF3D8C5FFA2D25043
This has an explicit NULL after the object identifier. Is this valid according to the specification? It says:
In this document, we define four new OIDs for identifying the different curve/algorithm pairs: the curves being curve25519 and curve448 and the algorithms being ECDH and EdDSA in pure mode.
For all of the OIDs, the parameters MUST be absent.

The paragraph after the one you quoted says this:
It is possible to find systems that require the parameters to be
present. This can be due to either a defect in the original 1997
syntax or a programming error where developers never got input where
this was not true. The optimal solution is to fix these systems;
where this is not possible, the problem needs to be restricted to
that subsystem and not propagated to the Internet.
So a plausible explanation for the Oracle implementation's behavior is that they want to be interoperable with old systems that require parameters. It is the kind of thing that you do to prevent big customers with large support contracts from complaining loudly that "upgrading to Java 11 broke my infrastructure".

Related

How to get BasicConstraints extension from Java X509 certificate

I would like to read the extension BasicConstraints from Java X509Certificate (the certificate implementation comes from default JCE so it is sun.security.x509.X509CertImpl).
I wanted to get the BasicConstraint extension value to check if it is CA :
X509Certificate certificate = ...
byte[] basicConstraint = certificate.getExtensionValue("2.5.29.19");
But this gives me byte array that contains DEROctetString. And after unwrapping it I get byte array with 2 bytes.
However the extension BasicConstraint seems to be defined as :
BasicConstraints := SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL
}
I have already looked at X509Certificate::getBasicConstraints() method which returns an int. The problem is that it also returns -1 when the extension is not present.
That is why I am looking for a way to get this ASN1 sequence from X509 certificate to explicitly check this CA boolean flag.
The valid encodings of the BasicConstraints extension (within the OCTET STRING) are:
CA=false: 30 00
CA=true, pathlen omitted: 30 03 01 01 FF
CA=true, pathlen=0 to 127: 30 06 01 01 FF 02 01 xx
CA=true, pathlen >= 128: using such long paths is so silly I omit this case, but you can work it out using the DER rules if you really want
You most likely have case 1.

How to generate secret key to get TOTP for HMAC SHA512 comply with RFC6238 and RFC4086?

I have to make an HTTP POST request to the URL http://example.com/test which contains the JSON string as a body part, headers "Content-Type:application/json" and "Authorization: Basic userid:password". userid is abc#example.com and password must be 10-digit time-based one time password comply with RFC6238 TOTP using HMAC-SHA-512 for the hash function.
Token shared secret should be "abc#example.comTEXT5" without double quotations.
So, to achieve above I modified the Java code of RFC6238 RC6238 TOTP Algo
To get TOTP, I converted the shared secret "abc#example.comTEXT5" to HMAC-SHA512 using online converter tool as well some codes which generate the same 128 character length HEX code
Making the request always responses that "TOTP is wrong".
I noticed that I generated the wrong secret key, so there is the wrong TOTP. So, how can I generate the correct secret key that complies HMAC-SHA512 with Java code of RFC6238 algorithm?
There is default key as seed on the algorithm:
String seed64 = "3132333435363738393031323334353637383930" +
"3132333435363738393031323334353637383930" +
"3132333435363738393031323334353637383930" +
"31323334";
How can I get such seed64 for my shared secret "abc#example.comTEXT5"?
My modified code is 10 digit TOTP
I appreciate help from everyone!
The example 64 byte seed in Appendix A of RFC 6238 is the HEX encoded version of the ASCII secret 12345678901234567890 provided in Appendix B which contains the truth table.
ASCII 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
HEX 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30
If you want to convert your shared secret using the same pattern, you would convert abc#example.comTEXT5 to HEX in string format if you're using the example code provided in Appendix A.
This would come out to the following HEX string :
616263406578616D706C652E636F6D5445585435
To build the example 64 byte seed used for the SHA-512 hash the initial 20 bytes are repeated to make 64 bytes total to achieve an optimal key length for the SHA-512 hash.
Doing the same with your example string would produce the following seed:
String seed64 = "616263406578616D706C652E636F6D5445585435" +
"616263406578616D706C652E636F6D5445585435" +
"616263406578616D706C652E636F6D5445585435" +
"61626340";
If you use the rest of the example code to calculate the time step and request a 10 digit TOTP code, I assume it will work for you.
If you are using something like this in production, you may wish to use a more randomly generated secret.
For example, to generate a 64 byte secret for SHA-512, you could do something like:
public static String generateRawSecret(int length) {
byte[] buf = new byte[length];
new SecureRandom().nextBytes(buf);
String rawSecret = Base64.getEncoder().encodeToString(buf);
return rawSecret.substring(1, length + 1);
}
// Random 64 byte secret
String secret = generateRawSecret(64);
It looks like you've already got most of this coded, but if you're looking for some additional Java examples, the following link is a GitHub project that has a simple utility class with a bunch of tests. https://github.com/FusionAuth/fusionauth-2FA

How to get specific lines from the file ( between two sections)?

I'm trying to read specific lines in-between two sections using Java 8.
I need to get the information in between ~CURVE INFORMATION and ~PARAMETER INFORMATION
I was able to get it using by checking startsWith() or equals and start storing the lines in some stringbuilder or collection. But is there any method available to get some specific lines in-between some sections.
I was looking at below questions for reference.
How to read specific parts of a .txt file in JAVA
How to read specific parts of the text file using Java
Sample data from file:
~WELL INFORMATION
#MNEM.UNIT DATA TYPE INFORMATION
#---------- ------------ ------------------------------
STRT.FT 5560.0000: START DEPTH
STOP.FT 16769.5000: STOP DEPTH
STEP.FT 0.5000: STEP LENGTH
NULL. -999.2500: NULL VALUE
COMP. SHELL: COMPANY
~CURVE INFORMATION
#MNEM.UNIT API CODE CURVE DESCRIPTION
#---------- ------------ ------------------------------
DEPT.F :
SEWP.OHMM 99 000 00 00:
SEMP.OHMM 99 120 00 00:
SEDP.OHMM 99 120 00 00:
SESP.OHMM 99 220 01 00:
SGRC.GAPI 99 310 01 00:
SROP.FT/HR 99 000 00 00:
SBDC.G/C3 45 350 01 00:
SCOR.G/C3 99 365 01 00:
SPSF.DEC 99 890 03 00:
~PARAMETER INFORMATION
#MNEM.UNIT VALUE DESCRIPTION
#---------- ------------ ------------------------------
RMF .OHMM -: RMF
MFST.F -: RMF MEAS. TEMP.
RMC .OHMM -: RMC
MCST.F -: RMC MEAS. TEMP.
MFSS. -: SOURCE RMF.
MCSS. -: SOURCE RMC.
WITN. MILLER: WITNESSED BY
~OTHER INFORMATION
Using Java9 you can do it elegantly with streams
public static void main(String[] args) {
try (Stream<String> stream = Files.lines(Paths.get(args[0]))) {
System.out.println(stream.dropWhile(string -> !"~CURVE INFORMATION".equals(string)).takeWhile( string -> !"~PARAMETER INFORMATION".equals(string)).skip(1).collect(Collectors.joining("\n")));
} catch (IOException e) {
e.printStackTrace();
}
}
What makes it pleasing is the declarative nature of streams, your literally writing code that says drop elements until start mark then take elements until end mark and join them using "\n"! Java9 added takeWhile and dropWhile, I'm sure you can implement them or get their implementation from a library for java 8. Of course this is just another way to achieve the original goal.

How to parse the response of a Solr Analysis request in java/solrj?

i have a java program to make requests in a solr server. I created a query that triggers the analysis service of solr :
HttpSolrClient server = new
HttpSolrClient("http://localhost:8983/solr/docs");
SolrQuery query = new SolrQuery();
query.setRequestHandler("/analysis/field");
query.set("analysis.fieldtype", "text_en");
query.set("analysis.fieldvalue", "TESTS");
query.set("wt", "json");
The response i get back is something like:
{responseHeader={status=0,QTime=2},analysis={field_types={text_en={index={org.apache.lucene.analysis.standard.StandardTokenizer=[{text=TESTS,raw_bytes=[54 45 53 54 53],start=0,end=5,org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute#positionLength=1,type=<ALPHANUM>,position=1,positionHistory=[1]}],org.apache.lucene.analysis.core.StopFilter=[{text=TESTS,raw_bytes=[54 45 53 54 53],start=0,end=5,org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute#positionLength=1,type=<ALPHANUM>,position=1,positionHistory=[1, 1]}],org.apache.lucene.analysis.core.LowerCaseFilter=[{text=tests,raw_bytes=[74 65 73 74 73],start=0,end=5,org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute#positionLength=1,type=<ALPHANUM>,position=1,positionHistory=[1, 1, 1]}],org.apache.lucene.analysis.en.EnglishPossessiveFilter=[{text=tests,raw_bytes=[74 65 73 74 73],start=0,end=5,org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute#positionLength=1,type=<ALPHANUM>,position=1,positionHistory=[1, 1, 1, 1]}],org.apache.lucene.analysis.miscellaneous.SetKeywordMarkerFilter=[{text=tests,raw_bytes=[74 65 73 74 73],start=0,end=5,org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute#positionLength=1,type=<ALPHANUM>,position=1,positionHistory=[1, 1, 1, 1, 1],org.apache.lucene.analysis.tokenattributes.KeywordAttribute#keyword=false}],org.apache.lucene.analysis.en.PorterStemFilter=[{text=test,raw_bytes=[74 65 73 74],start=0,end=5,org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute#positionLength=1,type=<ALPHANUM>,position=1,positionHistory=[1, 1, 1, 1, 1, 1],org.apache.lucene.analysis.tokenattributes.KeywordAttribute#keyword=false}]}}},field_names={}}}
Which is not a valid json. I want to parse it and get the texts i.e. "tests", "test".
I can only retrieve the analysis part by :
response.getResponse().get("analysis");
which is a class org.apache.solr.common.util.SimpleOrderedMap Object.
Any ideas? Thank you in advance.
I finally used another solution. With an http request in:
http://localhost:8983/solr/docs/analysis/field?wt=json&analysis.showmatch=true&analysis.fieldvalue={custom}&analysis.fieldtype={custom}
I get back the result in a valid json format.
Basically, it's a sin of Solr, as you mentioned it's not valid json, but it's rather string representation of what is called in Solr a NamedList (SimpleOrderedMap is a subclass of NamedList)
A simple container class for modeling an ordered list of name/value
pairs. Unlike Maps:
Names may be repeated
Order of elements is maintained
Elements may be accessed by numeric index
Names and Values can both be null
NamedList provides fast access by element number, but not by name.
Unfortunately, there are no built in support for transforming NamedList, so, you have to write custom Java code, that will extract needed properties out of NamedList
The other possibility is to use FieldAnalysisRequest, which will return it FieldAnalysisResponse, which have a methods like:
getFieldNameAnalysis(String fieldName)
which will give you FieldAnalysisResponse.Analysis
From two possible solutions, I will recommend the latter, since it will be much easier to grasp and maintain.

Java cannot retrieve Unicode (Lithuanian) letters from Access via JDBC-ODBC

i have DB where some names are written with Lithuanian letters, but when I try to get them using java it ignores Lithuanian letters
DbConnection();
zadanie=connect.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
sql="SELECT * FROM Clients;";
dane=zadanie.executeQuery(sql);
String kas="Imonė";
while(dane.next())
{
String var=dane.getString("Pavadinimas");
if (var!= null) {var =var.trim();}
String rus =dane.getString("Rusys");
System.out.println(kas+" "+rus);
}
void DbConnection() throws SQLException
{
String baza="jdbc:odbc:DatabaseDC";
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}catch(Exception e){System.out.println("Connection error");}
connect=DriverManager.getConnection(baza);
}
in DB type of field is TEXT, size 20, don't use any additional letter decoding or something like this.
it gives me " Imonė Imone " despite that in DB is written "Imonė" which equals rus.
Now that the JDBC-ODBC Bridge has been removed from Java 8 this particular question will increasingly become just an item of historical interest, but for the record:
The JDBC-ODBC Bridge has never worked correctly with the Access ODBC Drivers ("Jet" and "ACE") for Unicode characters above code point U+00FF. That is because Access stores such characters as Unicode but it does not use UTF-8 encoding. Instead, it uses a "compressed" variation of UTF-16LE where characters with code points U+00FF and below are stored as a single byte, while characters above U+00FF are stored as a null byte followed by their UTF-16LE byte pair(s).
If the string 'Imonė' is stored within the Access database so that it appears properly in Access itself
then it is stored as
I m o n ė
-- -- -- -- --------
49 6D 6F 6E 00 17 01
('ė' is U+0117).
The JDBC-ODBC Bridge does not understand what it receives from the Access ODBC driver for that final character, so it just returns
Imon?
On the other hand, if we try to store the string in the Access database with UTF-8 encoding, as would happen if the JDBC-ODBC Bridge attempted to insert the string itself
Statement s = con.createStatement();
s.executeUpdate("UPDATE vocabulary SET word='Imonė' WHERE ID=5");
the string would be UTF-8 encoded as
I m o n ė
-- -- -- -- -----
49 6D 6F 6E C4 97
and then the Access ODBC Driver will store it in the database as
I m o n Ä —
-- -- -- -- -- ---------
49 6D 6F 6E C4 00 14 20
C4 is 'Ä' in Windows-1252 which is U+00C4 so it is stored as just C4
97 is "em dash" in Windows-1252 which is U+2014 so it is stored as 00 14 20
Now the JDBC-ODBC Bridge can retrieve it okay (since the Access ODBC Driver "un-mangles" the character back to C4 97 on the way out), but if we open the database in Access we see
ImonÄ—
The JDBC-ODBC Bridge has never and will never be able to provide full native Unicode support for Access databases. Adding various properties to the JDBC connection will not solve the problem.
For full Unicode character support of Access databases without ODBC, consider using UCanAccess instead. (More details available in another question here.)
As you're using the JDBC-ODBC bridge, you can specify a charset in the connection details.
Try this:
Properties prop = new java.util.Properties();
prop.put("charSet", "UTF-8");
String baza="jdbc:odbc:DatabaseDC";
connect=DriverManager.getConnection(baza, prop);
Try to use this "Windows-1257" instead of UTF-8, this is for Baltic region.
java.util.Properties prop = new java.util.Properties();
prop.put("charSet", "Windows-1257");

Categories