The job to do:
I have got a signed SOAP request and I have to check if the signature is okay. The timestamp of the SOAP message is not of interest.
My solution so far:
I made a child class of org.apache.wss4j.dom.engine.WSSecurityEngine where in the method processSecurityHeader the check of TimestampProcessor is taken out of concern:
public class SignatureSecurityEngine extends WSSecurityEngine {
...
public WSHandlerResult processSecurityHeader(Element securityHeader, RequestData requestData) throws org.apache.wss4j.common.ext.WSSecurityException {
...
Processor p = cfg.getProcessor(el);
if (p != null) {
try {
results = p.handleToken((Element) node, requestData, wsDocInfo);
} catch (Exception e){
if (p instanceof TimestampProcessor) {
// it's okay if timestamp is too old
} else {
throw e;
}
}
}
...
In fact it's just a copy of WSSecurityEngine with the try/catch added for timestamp processor.
I older versions of wss4j and xmlsec this worked fine.
After a version upgrade of the components, I got the following strange issue:
The calculation of the signature digest fails in org.apache.jcp.xml.dsig.internal.dom.DOMReference.validate(...) if:
The programm runs on Windows (JRE)
I debug on Windows (JDK)
I debug on Linux (JDK)
BUT:
If the programm runs on Linux (JRE), everything works fine !
For both (Windows/Linux), the configuration is:
wss4j 2.1.9
xmlsec 2.0.8
Java version: 1.8.0_131 (build 1.8.0_131-b11)
Observation:
It seems that there remains a standard value ( 2jmj7l5rSw0yVb/vlWAYkK/YBwk= ) for the calculated digest.
Any idea?
Additional facts (2017-06-13):
After Maartens remark I (re)wrote some of the classes (in fact copy & paste) and added some System.out.println to have "debug information" at runtime. Really an odd old style and ugly thing...
But the result was quite interesting!
The stream for MessageDigest was never set. So this explains the 2jmj7l5rSw0yVb/vlWAYkK/YBwk= which is the digest for an empty string with SHA-1 (thanks Maarten!)
I managed then to fix - so the stream is now set in my copied "debug"-classes.
Result: If I debug now with my IDE, the calculation functions!
But: If I run in runtime the check fails :-((( Reason: The calculated value is not equal to the expected.
Further observations showed: ev. the wrong calculation depends on the length of the data the digest has to be calculated for (!?!?!?).
Let's have a look at my log:
*** Digest for Timestamp
VGDOMReference.validate -> transform:
Expected digest: LxfIdEUVsbyLaevptByfIf2L0PA=
Actual digest: LxfIdEUVsbyLaevptByfIf2L0PA=
Reference[#Timestamp-31b20235-a1e2-4ed0-9658-67611572108e]
*** Digest for Body
Expected digest: Yv+zLpkog+xzAdMlSjoIaZntZLs=
Actual digest: sj2Gb0GEyjWuxoCmnBzDY266aG8=
Reference[#Body-c9761a98-46bb-4175-8f8b-bfa9b5c75509]
As you can see the calculation for timestamp is correct. But the one for the body is wrong.
Perhaps some stream buffer that is not entirely written?
After some tests it turned out that there was an additional encoding problem... :-(((
The original signed files are in UTF-8, but as Windows uses ISO-xyz_whatever it did not match.
First fix is to put the encoding of JVM in the script that is calling the jar, so:
java -Dfile.encoding=UTF8 <programm>.jar
Related
As i am from testing background and have a very limited knowledge on Java kindly excuse me if the question i have asked is not good or repetitive.
I am trying to build an application using Java Swing. As per the requirement I have to display 32 bit Jre or 64 bit Jre installed on laptop based on the selection made. Frame which i developed for this
Unfortunately when I tested by installing a 32 bit JRE on Windows 64 bit machine and tried to run my application, when I choose the radio button "64 bit JRE" , in the drop down list its displaying me the 32 bit JRE which is installed on my system. (Kindly note i do not have any 64 bit JRE installed on my system). Ideally it should not display anything but the default value set in the JComboBox. :(
I understood the JRE was picked due to the Windows property. I tried with the API Advapi32.INSTANCE using the method RegGetValue but i am unable to get the value from Windows Registry.
32 bit JRE are under the path : HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node \JavaSoft\Java Runtime Environment
and 64 bit JRE are under the path : HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment
Please find the below code which i tried to retrieve 64 bit JRE :
String[] val64 = RegistryCheck.getRegistryDataForJRE(HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
public static String[] getRegistryDataForJRE(WinReg.HKEY root, String key) {
if (Advapi32Util.registryKeyExists(root, key)) {
return Advapi32Util.registryGetKeys(root, key);
} else {
return null;
}
}
Also below is the code which i tried with Advapi32.INSTANCE (just ttried to do a sysout to know whether the value returned is 0 or not. as from the Microsoft pages i see if its a non zero value returned its an error
public static void main(String[] args) {
byte[] b = new byte[50];
IntByReference pcbData = new IntByReference(50);
System.out.println(Advapi32.INSTANCE.RegGetValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment",
"" , 0x0200, pcbData, b, pcbData));
}
Also, I read the flag KEY_WOW64_32KEY is the responsible to pick up from the WOW6432Node , but i am not able to check the flag value or to set it to false in java.
Can anyone help me with this issue ?
I would be really grateful for the help.
Thank you in advance for the support.
try to use the method RegOpenKeyEx() of Advapi32 i.e. syntax
RegOpenKeyEx(WinReg.HKEY hKey,
String lpSubKey,
int ulOptions,
int samDesired,
WinReg.HKEYByReference phkResult)
refer to the link RegOpenKeyEx()
You can pass the parameters accordingly and then check if the return value is "0" then the path exists in 64 bit else it doesn't exists.
Note : when you pass "samDesired" into the RegOpenKeyEx() pass the combination of KEY_QUERY_VALUE and KEY_WOW64_64KEY
For example :
WinReg.HKEYByReference hKey = new HKEYByReference();
Advapi32.INSTANCE.RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment",0, 0x0001 | 0x0100, hKey);
Let me know if it worked for you
I am testing basic stuff in ESAPI, and I ran across this symmetric encryption tutorial and copied and pasted the code, (along with importing the ESAPI 2.1.0 jar file, ESAPI.properties and validation.properties in the 'src' directory in Eclipse)
Modified code from the tutorial:
import org.owasp.esapi.crypto.CipherText;
import org.owasp.esapi.crypto.PlainText;
import org.owasp.esapi.errors.EncryptionException;
import org.owasp.esapi.reference.crypto.JavaEncryptor;
public class ESAPIsymEncTester {
public static void main(String[] args) throws EncryptionException{
String myplaintext = "My plaintext";
CipherText ciphertext =
JavaEncryptor.getInstance().encrypt( new PlainText(myplaintext) );
PlainText recoveredPlaintext = JavaEncryptor.getInstance().decrypt(ciphertext);
assert myplaintext.equals( recoveredPlaintext.toString() );
System.out.println("recovered plaintext: " + recoveredPlaintext.toString());
}
}
However, when I run this in Eclipse Luna using Java 1.8, I get this stack trace:
Exception in thread "main" org.owasp.esapi.errors.EncryptionException: Encryption failure: Invalid key exception.
at org.owasp.esapi.reference.crypto.JavaEncryptor.encrypt(JavaEncryptor.java:526)
at org.owasp.esapi.reference.crypto.JavaEncryptor.encrypt(JavaEncryptor.java:338)
at com.fate.engine.test.ESAPIsymEncTester.main(ESAPIsymEncTester.java:15)
Caused by: java.security.InvalidKeyException: Invalid AES key length: 96 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:495)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1062)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1033)
at javax.crypto.Cipher.init(Cipher.java:1367)
at javax.crypto.Cipher.init(Cipher.java:1301)
at org.owasp.esapi.reference.crypto.JavaEncryptor.encrypt(JavaEncryptor.java:504)
... 2 more
I am not sure if this is a bug in the JavaEncryptor.java code, or if I am pulling something that I misconfigured from the ESAPI.properties file.
I replaced the master key and salt by running the JavaEncryptor and copy/pasting the resultant key/salt.
If it is a bug, I will email the ESAPI guys to get clarification on how I can fix it, since I looked through the JavaEncryptor code and am not entirely clear where all of the pieces are coming from.
Encryptor.MasterKey=WppLubGgsc/p6HhvcPf2LA==
Encryptor.MasterSalt=YokRN9mjMUTZspEbzBY90NA6EC8=
Encryptor.PreferredJCEProvider=
Encryptor.EncryptionAlgorithm=AES
Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
Encryptor.cipher_modes.additional_allowed=CBC
Encryptor.EncryptionKeyLength=128
Encryptor.ChooseIVMethod=random
Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
Encryptor.CipherText.useMAC=true
Encryptor.PlainText.overwrite=true
Encryptor.HashAlgorithm=SHA-512 *****
Encryptor.HashIterations=1024
Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
Encryptor.DigitalSignatureKeyLength=1024
Encryptor.RandomAlgorithm=SHA1PRNG
Encryptor.CharacterEncoding=UTF-8
Encryptor.KDF.PRF=HmacSHA1 *****
You forgot to place the most important part of your log into the question:
Dec 11, 2015 8:05:24 AM org.owasp.esapi.reference.JavaLogFactory$JavaLogger log
WARNING: [SECURITY FAILURE Anonymous:null#unknown -> /JavaEncryptor] Encryption key length mismatch. ESAPI.EncryptionKeyLength is 128 bits, but length of actual encryption key is 24 bits. Did you remember to regenerate your master key (if that is what you are using)???
This is a clue that there is something here that the library expects you to do.
It seems to me that you probably have the default encryptor properties set like this in esapi.properties:
Encryptor.MasterKey=owasp1
Encryptor.MasterSalt=testtest
The class JavaEncryptor has a main method that will generate valid properties for you. Run it in eclipse or via the command line. It will give you values to replace in esapi.properties, like this:
Dec 11, 2015 8:10:25 AM org.owasp.esapi.reference.JavaLogFactory$JavaLogger log
OFF: [SECURITY AUDIT Anonymous:null#unknown -> /SecurityProviderLoader] No Encryptor.PreferredJCEProvider specified.
SecurityConfiguration for Encryptor.EncryptionKeyLength not an integer in ESAPI.properties. Using default: 128
Generating a new secret master key
use '-print' to also show available crypto algorithms from all the security providers
SecurityConfiguration for Encryptor.EncryptionKeyLength not an integer in ESAPI.properties. Using default: 128
Copy and paste these lines into your ESAPI.properties
#==============================================================
Encryptor.MasterKey=qW0Qw+8eb1Zu1MBv5djwqA==
Encryptor.MasterSalt=b0VappFU1Hd6LjIt+TGYqQlfrdU=
#==============================================================
Once I did that, your code example runs just fine.
Here's what I'm going to suggest... grab the TEST version of ESAPI.properties from GitHub ("wget https://github.com/ESAPI/esapi-java-legacy/blob/master/src/test/resources/esapi/ESAPI.properties" should work, you use 'git' or save if from your browser), put it in place, and first use it AS-IS. If it fails, then there is a problem in you tweaked code. If it works, there was a problem in your ESAPI.properties file. Many people already suggested what to look for in terms of what might be wrong, but the differences should be minor enough that you should be able to spot them by diff'ing yours versus the TEST version in src/test/resources/esapi/ESAPI.properties. (The production version, incidentally, is under 'configuration/esapi/ESAPI.properties' and is not included with the jar because of some bug in the pom.xml which I don't know how to fix as I am not a Maven guru.)
If you have further questions, contact me at my Gmail account which you should be able to find easily enough via Google with my name and the term "OWASP". Once we figure out an answer that works for you, either you or I can post an answer back to Stack Overflow, but I don't frequent this forum enough to regularly monitor it. (Although, come to think of it, I probably do get notified of replies.)
Hope this helps,
-kevin w. wall / ESAPI crypto developer and co-project lead
Solved, In short: the problem was that I wrote to an already closed FileOutputStream
I noticed some strange semantics using the FileOutputStream class.
If I create a FileOutputStream using this code:
try {
File astDumpFile = new File(dumpASTPath);
if(!astDumpFile.exists()) {
astDumpFile.createNewFile();
}
astDumpStream = new FileOutputStream(dumpASTPath);
} catch( IOException e ) {
dumpAST = false;
//throw new IOException("Failed to open file for dumping AST: " + dumpASTPath);
System.out.println("Failed to open file for dumping AST: " + dumpASTPath);
}
at the beginning of the program (astDumpStream is a member variable). Then if I later (~3 seconds later) write string data to the file, i get an IOException: stream closed:
try {
String dotGraph = gpvisitor.getDotGraph();
astDumpStream.write(dotGraph.getBytes("UTF8"));
astDumpStream.flush();
astDumpStream.close();
} catch( IOException e ) {
System.out.println("Failed to dump AST to file: " + e.getMessage());
e.printStackTrace();
}
However if I copy the excact code which I use to create the FileOutputStream to directly before writing to it, it works as expected.
Now I wonder why do I get that exception if I create that object earlier, but not if I create it directly before I use it.
EDIT: The exception:
java.io.IOException: Stream Closed
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:305)
at MyClass.function(MyClass.java:208)
I just noticed, that even though I get an exception, still some data was written to the file. Interrestingly the first line is written completely, then all following lines except the last line are missing.
If I replace the written String dotGraph with something shorter everything is written correctly, however I still get that exception.
EDIT: Environment Information:
[~]> lsb_release -a
Distributor ID: Debian
Description: Debian GNU/Linux testing (wheezy)
Release: testing
Codename: wheezy
[~]> java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
The only reason to get an IOException complaining that the stream is closed is because the stream was closed. You'll have to trace through your code to find out where that's happening. Some not-so-obvious places include calls into other methods and finally blocks of try statements. Another thing to look for is reassignment of the variable astDumpStream to a different stream (that was closed before the IOException was raised—possibly even before the first assignment to astDumpStream).
The time doesn't seem relevant unless you have a separate thread that might close the stream after a delay.
The only way this can happen if is the close() function gets called more than once. My guess is that for some reason, the second block of code is being called more than once.
To prevent indentation errors, there are two good pieces of advice I've received:
Always indent consistently. Preferably use a tool that does this for you (like Eclipse).
Always use curly braces, even if you don't think you need them. This helps prevent quite a few minor bugs that take forever to find, so the extra half-second it takes to type each one is more than made up by the hours you don't spend looking for these bugs.
To Second Tedd - in case you happen to use a nested try with resource block and using stream outside tryblock - you could run in this situation as well because once the control comes out of nested try with resource block the stream will be closed.
I hava a Java program that needs to monitor a directory tree for changes. I have JNI code that uses ReadDirectoryChangesW(). The directory is opened like:
HANDLE dirHandle = CreateFile(
path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
);
and then I pass dirHandle to ReadDirectoryChangesW(). All of that works just fine.
The problem is that other parts of the code (on the Java side) use File.setLastModified() to "touch" files or directories (update their timestamps to be "now"). This generally works; however, it fails when it tried to "touch" the directory that was opened using CreateFile().
To see what Windows error is actually occurring, I looked at the JDK source for File.setLastModified() and reimplemented it in my own code with the addition of printing the error from GetLastError(); the error is:
ERROR_SHARING_VIOLATION (error 32)
"The process cannot access the file because it is being used by another process."
WTF? It's the same process. I even passed FILE_SHARE_READ and FILE_SHARE_WRITE to CreateFile().
Is there a way to make this work?
More Info
The native code implementation of File.setLastModified() in the JDK does a:
h = CreateFileW(pathbuf, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0);
If I change the first 0 to FILE_SHARE_READ | FILE_SHARE_WRITE, it all works. So it seems that the JDK implementation is a little broken. :(
So my question now becomes: Is there a way to make this work without having to use my own (re)implementation of File.setLastModified()?
Although the error message is a bit misleading in this case, what you're seeing is normal behaviour.
By opening the directory with dwShareMode set to zero, the JDK is, in effect, asking for exclusive access, which will cause any other access attempt to fail with a sharing violation error. This applies equally to accesses
from other processes and from within your own process.
The CreateFile documentation describes the dwShareMode parameter:
If this parameter is zero and CreateFile succeeds, the file or device cannot be shared and cannot be opened again until the handle to the file or device is closed.
You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION.
So, it seems you've answered your own question: you need a custom setLastModified function that specifies FILE_SHARE_READ | FILE_SHARE_WRITE when accessing the directory.
I have created a plain file which does not have execute permission but when I create a Java File object using this file's path/name and then call File.canExecute() I get true as the result, whereas I would expect this method call to return false. Can someone explain what I'm missing here?
Solaris:
$ touch /tmp/nonexecutable
$ ls -l /tmp/nonexecutable
-rw-r--r-- 1 root root 0 May 21 07:48 /tmp/nonexecutable
Java:
String pathName = "/tmp/nonexecutable";
File myFile = new File(pathName);
if (!myFile.canExecute())
{
String errorMessage = "The file is not executable.";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
Thanks in advance for your help.
--James
Nothing to do with Java - you're running as root, and root is allowed everything, not matter what the permissions say.
Though I'm not an expert, and this will not answer your question properly, I'd like to add that this behavior is not specific to Java. From the find (GNU findutils) 4.4.0 manpage on my Ubuntu 8.10 install, regarding the -executable flag:
Matches files which are
executable and directories which are
searchable (in a file name resolution
sense). This takes into account
access control lists and other
permissions artefacts which the -perm
test ignores. This test makes
use of the access(2) system call,
and so can be fooled by NFS servers
which do UID mapping (or
root-squashing), since many systems
implement access(2) in the client’s
kernel and so cannot make use of the
UID mapping information held on the
server. Because this test is
based only on the result of the
access(2) system call, there is no
guarantee that a file for which this
test succeeds can actually be
executed.
Here is a bug which was opened on JDK on this:
http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=8b833c54cb93d6c9cf416667dc02?bug_id=6379654
The conclusion is that File.canExecute() simply translates into a native posix call to access(path, X_OK). Linux returns false and solaris returns true for that call when run as root.
Finally, the bug was closed as Wont Fix! :)