How can I save MessageDigest internal state into database? - java

Is it possible, and if than how, to save the internal state of MessageDigest object? I want to save it in a database, so have to use only primitive data like String, int, byte[].
What I'm trying to achieve is to be able to receive a fragmented file (during a long period of time), save all the fragments in database, and after receiving last fragment verify the SHA512 digest of the file without getting back all the data previously saved in database.
So basically I want something like this:
MessageDigest md = MessageDigest.getInstance("SHA-512");
// restore previous internal state of md
md.update(dataSegment);
// save internal md state

you could serialize the object to String (XML format) and return it back.
check:
http://x-stream.github.io/tutorial.html
public class DigestTest {
private static final byte[] TEST_DATA = "Some test data for digest computations".getBytes();
#Test
public void shouldStoreAndRestoreDigest() throws Exception {
final MessageDigest referenceDigest = MessageDigest.getInstance("SHA-512");
MessageDigest testDigest = MessageDigest.getInstance("SHA-512");
referenceDigest.update(TEST_DATA);
testDigest.update(TEST_DATA);
// store state
final XStream xs = new XStream(new StaxDriver());
xs.alias("md", MessageDigest.class);
final String serializedMd = xs.toXML(testDigest);
System.out.println(serializedMd);
// restore state
testDigest = (MessageDigest)xs.fromXML(serializedMd);
// ---
referenceDigest.update(TEST_DATA);
testDigest.update(TEST_DATA);
Assert.assertArrayEquals(referenceDigest.digest(), testDigest.digest());
}
}

Related

How to update User Defined Variable in JMeter in JSR223 Sampler?

I'm new to JMeter & Java and now writing Authorization script for testing API.
I had some troubles with updating variable with vars.put(key,value)
Here is my code example:
import java.util.Arrays;
import java.security.MessageDigest;
import java.util.Base64;
public class StringToByte {
public void main(String[] args) {
String str_salt = "${salt}";
byte[] b_salt = str_salt.getBytes();
String str_pass = "c3000Hub";
byte[] b_pass = str_pass.getBytes();
byte[] b_pass_hash = new byte[b_salt.length + b_pass.length];
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(b_pass_hash);
String pass_hash = Base64.getEncoder().encodeToString(hash);
vars.put("passhash", pass_hash);
}
}
Variable in User Defined Variables just not updating and I've got no idea why?
You need to explicitly call this main() function in order to get it working, you declare it but I fail to see where it's being invoked
Change String str_salt = "${salt}"; to String str_salt = vars.get("salt");, as per JSR223 Sampler Documentation:
The JSR223 test elements have a feature (compilation) that can significantly increase performance. To benefit from this feature:
Use Script files instead of inlining them. This will make JMeter compile them if this feature is available on ScriptEngine and cache them.
Or Use Script Text and check Cache compiled script if available property.
When using this feature, ensure your script code does not use JMeter variables or JMeter function calls directly in script code as caching would only cache first replacement. Instead use script parameters.
Suggested code change (if you want to keep this class/method approach):
import org.apache.jmeter.threads.JMeterVariables
import java.security.MessageDigest
public class StringToByte {
public void main(JMeterVariables vars) {
String str_salt = vars.get("salt");
byte[] b_salt = str_salt.getBytes();
String str_pass = "c3000Hub";
byte[] b_pass = str_pass.getBytes();
byte[] b_pass_hash = new byte[b_salt.length + b_pass.length];
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(b_pass_hash);
String pass_hash = Base64.getEncoder().encodeToString(hash);
vars.put("passhash", pass_hash);
}
}
new StringToByte().main(vars)
or you can just do something like:
import java.security.MessageDigest
String str_salt = vars.get("salt");
byte[] b_salt = str_salt.getBytes();
String str_pass = "c3000Hub";
byte[] b_pass = str_pass.getBytes();
byte[] b_pass_hash = new byte[b_salt.length + b_pass.length];
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(b_pass_hash);
String pass_hash = Base64.getEncoder().encodeToString(hash);
vars.put("passhash", pass_hash);
More information on Groovy scripting in JMeter: Apache Groovy - Why and How You Should Use It

Java: how to calculate sha1 digest on-the-fly on a stream that is being saved?

I have a servlet written in Java that accepts a multpart-form posted file that needs to be saved in MongoDb/GridFS. I already have the code working for this.
Here is a code fragment that shows how it is done using the org.apache.commons.fileupload package. It consumes almost no memory, because it does not keep too much data in memory.
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iter = upload.getItemIterator(req);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField()) {
toProcess.put(name, Streams.asString(stream));
} else {
String fileName = item.getName();
String contentType = item.getHeaders().getHeader("Content-Type");
GridFSUploadOptions options = new GridFSUploadOptions()
// .chunkSizeBytes(358400)
.metadata(new Document("content_type", contentType));
ObjectId fileId = gridFSFilesBucket.uploadFromStream(fileName, stream, options);
fileIds.add(fileId);
fileNames.add(fileName);
}
I also need to calculate sha1 hash values for all files. Apache digestutils could be used for this. It has a method that can calculate sha1 on a stream:
https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html#sha1-java.io.InputStream-
My problem is that this method consumes the stream entirely. I need to split the input stream into two parts. Feed one part into SHA-1 calculation and the other part into the GridFS bucket.
How can I do that? I was thinking about creating my own "pipe" that has an input and an output stream, forwards all data but updates the digest on the fly.
I just don't know how to start writting such a pipe.
You can use the Java API class DigestInputStream
As the Javadoc explains,
A transparent stream that updates the associated message digest using
the bits going through the stream.
To complete the message digest
computation, call one of the digest methods on the associated message
digest after your calls to one of this digest input stream's read
methods.
In your code you can do this:
InputStream stream = item.openStream();
MessageDigest digest = MessageDigest.getInstance("SHA-256");
stream = new DigestInputStream(stream, digest);
And at the end you can get the digest with:
byte[] hash = digest.digest();

Java StreamCorruptedException attempting deserialize the object read from SharedPreferences Android

I'm trying to serialize an object from within it and deserialize it using this answer: Reliably convert any object to String and then back again
But I get StreamCorruptedException while deserializing.
java.io.StreamCorruptedException
W/System.err: at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:2065)
W/System.err: at java.io.ObjectInputStream.<init>(ObjectInputStream.java:371)
W/System.err: at ShoppingCart.load(ShoppingCart.java:154)
Here is the Class :
public class ShoppingCart implements Serializable {
ArrayList<Item> items ;
String token ;
transient Context context ;
public ShoppingCart(Context cntx){
context = cntx ;
items = new ArrayList<Item>();
SharedPreferences preferences = context.getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
token = preferences.getString("login_token", null);
}
public void emptyCart(){
items = new ArrayList<Item>();
store();
System.gc();
}
public boolean addToCart(Item item){
boolean exists = false ;
for(int i = 0 ; i < items.size() ; i++){
if(items.get(i).productID.equals(item.productID)){
exists = true ;
return false ;
}
}
if(!exists)
items.add(item);
store();
return true ;
}
public void removeFromCart(Item item){
items.remove(item);
store();
}
public void store() {
SharedPreferences.Editor editor =
context.getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
// serialize the object
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream so = new ObjectOutputStream(bo);
so.writeObject(this);
so.flush();
String serializedObject = bo.toString();
editor.putString("stored_cart", serializedObject);
editor.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
public ShoppingCart load() {
SharedPreferences preferences = context.getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
String serializedObject = preferences.getString("stored_cart", null);
ShoppingCart newCart = null ;
// deserialize the object
try {
byte b[] = serializedObject.getBytes();
ByteArrayInputStream bi = new ByteArrayInputStream(b);
ObjectInputStream si = new ObjectInputStream(bi);
newCart = (ShoppingCart) si.readObject();
newCart.context = context ;
} catch (Exception e) {
e.printStackTrace();
}
return newCart ;
}
}
I'm calling the load() function like this:
cart = new ShoppingCart(getApplicationContext());
SharedPreferences preferences =
getApplicationContext().getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
if(preferences.getString("stored_cart", null) != null) {
cart = cart.load();
Log.d("AppController","cart loaded");
}
Since the Context is not serilizable, so I made it transient.
What am I doing wrong ?
Firstly, from what I see, you cannot put a serializable into sharedprefs.
I tried saving to a database using a string value and convert it back and to a byte array. (I can store byte arrays as a blob in a database). When I used a String, the byte array got the wrong format which triggered the same exception you have right there: StreamCorrupedException
When I got this error and did research, what I got from it was that a StreamCorruptedException means you do something "bad" with the stream, that screws up the format.
Now, for the solution.
When I tried this, saved as a string and loaded back as a byte array, the different bytes aren't necessarily loaded back in the same format as they are saved. This is when you get the exception. You try to load a byte array from a String, but when you apply the byte array to a class it ends up as a corrupted byte array.
Basically, DO NOT SAVE IT AS A STRING AND CONVERT IT TO A BYTE ARRAY! What I did to be able to save was to actually save it as a byte array, and I use a database and databases support this. But from what I see, shared prefs do not. So basically, you cannot use a String and convert it to a byte array to save and then load. You have to use internal/external storage(basically files) or use a database with mode set to blob. But Shared Prefs simply do not support byte arrays which means when you convert the string to a byte array, it changes the stream and corrupts the data.
So you have three options:
Store as file (internal/external)
Store in database as blob
Save each individual item in the cart as an individual item in the shared prefs.
(ANd make sure item is serializable as well, to prevent exceptions there later).
TL:DR; When you save the serializable class as a String, but convert it back to a byte array will give this error.
TEST CASE
The following test works on both Android and desktop Java. This testing was done using a Gradle project, but you can go to apache commons and find a different dependency depending on your project. The dependency is used for converting back and forth between byte array and class.
Required dependency for testing:
compile 'org.apache.commons:commons-lang3:3.5'
Sample class:
public class Something implements Serializable{
public static final long serialVersionUID = 0L;
int x = 12;
}
Sample test:
Something instance = new Something();
String class_ = SerializationUtils.serialize(instance).toString();
byte[] bytes = class_.getBytes();
instance = (Something) SerializationUtils.deserialize(bytes);
Output:
Exception in thread "main" org.apache.commons.lang3.SerializationException: java.io.StreamCorruptedException: invalid stream header: 5B424036
at org.apache.commons.lang3.SerializationUtils.deserialize(SerializationUtils.java:229)
at org.apache.commons.lang3.SerializationUtils.deserialize(SerializationUtils.java:265)
at com.core.Launcher.main(Launcher.java:22)
Caused by: java.io.StreamCorruptedException: invalid stream header: 5B424036
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:857)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:349)
at org.apache.commons.lang3.SerializationUtils.deserialize(SerializationUtils.java:221)
... 2 more
Process finished with exit code 1
In my testing, this throws StreamCorruptedException, which basically shows one thing: You cannot convert a class to byte a byte array, then to a String and back to byte array
Note: This test case was executed on a WIndows 10 computer and Android 7(S6 edge), and it threw the same error (stacktrace changes because of there being two different projects).
Summary:
Saving and loading a byte array works fine. Where saving and loading a byte array involves a String in the middle, it corrupts the stream.
Solution:
Don't save with a String part to be able to save on an unsupported platform. That platform in this case being Sharedprefs. Use files or a database (with a blob field) are the only ways to save a byte array locally on a device in Android. Transferring data over the internet is an entirely different topic I am not going to cover.
So in order to serialize and deserialize with bytes, you have to save it as a file, or in a database. Converting back and forth between a string is what gives you the problems.
And finally, this error has nothing to do with an unserializable field. That throws a different error(NotSerializableException).

Read PDVInputStream dicomObject information on onCStoreRQ association request

I am trying to read (and then store to 3rd party local db) certain DICOM object tags "during" an incoming association request.
For accepting association requests and storing locally my dicom files i have used a modified version of dcmrcv() tool. More specifically i have overriden onCStoreRQ method like:
#Override
protected void onCStoreRQ(Association association, int pcid, DicomObject dcmReqObj,
PDVInputStream dataStream, String transferSyntaxUID,
DicomObject dcmRspObj)
throws DicomServiceException, IOException {
final String classUID = dcmReqObj.getString(Tag.AffectedSOPClassUID);
final String instanceUID = dcmReqObj.getString(Tag.AffectedSOPInstanceUID);
config = new GlobalConfig();
final File associationDir = config.getAssocDirFile();
final String prefixedFileName = instanceUID;
final String dicomFileBaseName = prefixedFileName + DICOM_FILE_EXTENSION;
File dicomFile = new File(associationDir, dicomFileBaseName);
assert !dicomFile.exists();
final BasicDicomObject fileMetaDcmObj = new BasicDicomObject();
fileMetaDcmObj.initFileMetaInformation(classUID, instanceUID, transferSyntaxUID);
final DicomOutputStream outStream = new DicomOutputStream(new BufferedOutputStream(new FileOutputStream(dicomFile), 600000));
//i would like somewhere here to extract some TAGS from incoming dicom object. By trying to do it using dataStream my dicom files
//are getting corrupted!
//System.out.println("StudyInstanceUID: " + dataStream.readDataset().getString(Tag.StudyInstanceUID));
try {
outStream.writeFileMetaInformation(fileMetaDcmObj);
dataStream.copyTo(outStream);
} finally {
outStream.close();
}
dicomFile.renameTo(new File(associationDir, dicomFileBaseName));
System.out.println("DICOM file name: " + dicomFile.getName());
}
#Override
public void associationAccepted(final AssociationAcceptEvent associationAcceptEvent) {
....
#Override
public void associationClosed(final AssociationCloseEvent associationCloseEvent) {
...
}
I would like somewhere between this code to intercept a method wich will read dataStream and will parse specific tags and store to a local database.
However wherever i try to put a piece of code that tries to manipulate (just read for start) dataStream then my dicom files get corrupted!
PDVInputStream is implementing java.io.InputStream ....
Even if i try to just put a:
System.out.println("StudyInstanceUID: " + dataStream.readDataset().getString(Tag.StudyInstanceUID));
before copying datastream to outStream ... then my dicom files are getting corrupted (1KB of size) ...
How am i supposed to use datastream in a CStoreRQ association request to extract some information?
I hope my question is clear ...
The PDVInputStream is probably a PDUDecoder class. You'll have to reset the position when using the input stream multiple times.
Maybe a better solution would be to store the DICOM object in memory and use that for both purposes. Something akin to:
DicomObject dcmobj = dataStream.readDataset();
String whatYouWant = dcmobj.get( Tag.whatever );
dcmobj.initFileMetaInformation( transferSyntaxUID );
outStream.writeDicomFile( dcmobj );

computing checksum for an input stream

I need to compute checksum for an inputstream(or a file) to check if the file contents are changed. I have this below code that generates a different value for each execution though I'm using the same stream. Can someone help me to do this right?
public class CreateChecksum {
public static void main(String args[]) {
String test = "Hello world";
ByteArrayInputStream bis = new ByteArrayInputStream(test.getBytes());
System.out.println("MD5 checksum for file using Java : " + checkSum(bis));
System.out.println("MD5 checksum for file using Java : " + checkSum(bis));
}
public static String checkSum(InputStream fis){
String checksum = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
//Using MessageDigest update() method to provide input
byte[] buffer = new byte[8192];
int numOfBytesRead;
while( (numOfBytesRead = fis.read(buffer)) > 0){
md.update(buffer, 0, numOfBytesRead);
}
byte[] hash = md.digest();
checksum = new BigInteger(1, hash).toString(16); //don't use this, truncates leading zero
} catch (Exception ex) {
}
return checksum;
}
}
You're using the same stream object for both calls - after you've called checkSum once, the stream will not have any more data to read, so the second call will be creating a hash of an empty stream. The simplest approach would be to create a new stream each time:
String test = "Hello world";
byte[] bytes = test.getBytes(StandardCharsets.UTF_8);
System.out.println("MD5 checksum for file using Java : "
+ checkSum(new ByteArrayInputStream(bytes)));
System.out.println("MD5 checksum for file using Java : "
+ checkSum(new ByteArrayInputStream(bytes)));
Note that your exception handling in checkSum really needs fixing, along with your hex conversion...
Check out the code in org/apache/commons/codec/digest/DigestUtils.html
Changes on a file are relatively easy to monitor, File.lastModified() changes each time a file is changed (and closed). There is even a build-in API to get notified of selected changes to the file system: http://docs.oracle.com/javase/tutorial/essential/io/notification.html
The hashCode of an InputStream is not suitable to detect changes (there is no definition how an InputStream should calculate its hashCode - quite likely its using Object.hashCode, meaning the hashCode doesn't depend on anything but object identity).
Building an MD5 like you try works, but requires reading the entire file every time. Quite a performance killer if the file is large and/or watching for multiple files.
You are confusing two related, but different responsibilities.
First you have a Stream which provides stuff to be read. Then you have a checksum on that stream; however, your implementation is a static method call, effectively divorcing it from a class, meaning that nobody has the responsibility for maintaining the checksum.
Try reworking your solution like so
public ChecksumInputStream implements InputStream {
private InputStream in;
public ChecksumInputStream(InputStream source) {
this.in = source;
}
public int read() {
int value = in.read();
updateChecksum(value);
return value;
}
// and repeat for all the other read methods.
}
Note that now you only do one read, with the checksum calculator decorating the original input stream.
The issue is after you first read the inputstream. The pos has reach the end. The quick way to resolve your issue is
ByteArrayInputStream bis = new ByteArrayInputStream(test.getBytes());
System.out.println("MD5 checksum for file using Java : " + checkSum(bis));
bis = new ByteArrayInputStream(test.getBytes());
System.out.println("MD5 checksum for file using Java : " + checkSum(bis));

Categories