welcome to xlongwei.com

欢迎大家一起学习、交流、分享


QQ群:162333776 邮箱:admin@xlongwei.com

Steganography 图片隐写术


分类 Java   关键字 分享   标签 java   web   algorithm   发布 hongwei  1474942611953
注意 转载须保留原文链接,译文链接,作者译者等信息。  
aTool.org上看到图片隐写术,觉得挺有意思,就想用java做个接口,因此有了下文。

aTool.org是使用js实现的,具体实现参考了PixelJihad项目,java实现时需要通过javax.script包调用js脚本。

Steganography封装了js调用的方法

1,初始化脚本引擎,加载main.jssjcl.js
static {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
try { //初始化脚本引擎,加载sjcl.jsmain.js,后者有改动
String prefix = GlobalConfig.getProperty("static_url")+"/js/steganography/";
engine.eval(FileUtil.readString(FileUtil.stream(prefix+"sjcl.js"), CharsetNames.UTF_8));
engine.eval(FileUtil.readString(FileUtil.stream(prefix+"main.js"), CharsetNames.UTF_8));
}catch(Exception e) {
logger.warn("fail to initialize Steganography", e);
}
invoke = (Invocable) engine;
}


2,调用js方法,main.js提供encode和decode方法,int[] pixels表示操作的是图片像素数据
private static String decode(final int[] pixels, final String password) {
Future<String> future = TaskUtil.submit(new Callable<String>() {
public String call() throws Exception {
try{
return (String)invoke.invokeFunction("decode", pixels, password);
}catch(Exception e) {
logger.warn("fail to decode pixels with password: "+password, e);
return null;
}
}
});
try {
return future.get(timeout, timeUnit); //这里用到了超时限制,encode方法没有超时限制
} catch (InterruptedException | ExecutionException | TimeoutException e) {
logger.warn("timeout to decode pixels with password: "+password);
TaskUtil.cancel(future);
return null;
}
}

3,图片编码解码
/** 隐写加密图片数据 */
public static BufferedImage encrypt(BufferedImage image, String text, String password) {
int w=image.getWidth(), h=image.getHeight(), s=w*h*4;
int[] pixels = new int[s];
WritableRaster raster = image.getRaster();
raster.getPixels(0, 0, w, h, pixels); //原main.js调用Context.getImageData获取数据
Object obj = encode(pixels, text, password); //调用后pixels本身发生了改变,之前一直以为返回值obj才是改变后的数据,折腾了好久
if(obj == null) return null; //信息太大或图片较小时返回编码失败
raster.setPixels(0, 0, w, h, pixels); //PNG图片效果还行,JPG图片会呈红色,有明显变化因此不适合JPG格式
return image;
}
/** 解密隐写图片数据 */
public static String decrypt(BufferedImage image, String password) {
int w=image.getWidth(), h=image.getHeight(), s=w*h*4;
int[] pixels = new int[s];
WritableRaster raster = image.getRaster();
raster.getPixels(0, 0, w, h, pixels);
return decode(pixels, password); //密码错误时返回空串
}

4,main.js去除浏览器相关代码:Image,Canvas等都不支持
var maxMessageSize = 2048;
function encode(pixels, text, password){
var message = password.length > 0 ? sjcl.encrypt(password, text) : JSON.stringify({ text : text });
if ((message.length + 1) * 16 > pixels.length * 0.75) return; //pixels.length = width*height*4;
if (message.length > maxMessageSize) return; //如果不加超长限制,js会超时
encodeMessage(pixels, sjcl.hash.sha256.hash(password), message);
return pixels;
}
function decode(pixels, password){
var message = decodeMessage(pixels, sjcl.hash.sha256.hash(password));
try{
var obj = JSON.parse(message);
if(!obj) return;
if(obj.ct) obj.text = sjcl.decrypt(password, message);
return escHtml(obj.text);
}catch(e){ }
}
function decodeMessage(colors, hash) {
var history = [];
var messageSize = getNumberFromBits(colors, history, hash);
if ((messageSize + 1) * 16 > colors.length * 0.75) return "";
if (messageSize === 0 || messageSize > maxMessageSize) return ""; //密码错误时messageSize非常大,js调用会超时
var message = [];
for (var i = 0; i < messageSize; i++) {
var code = getNumberFromBits(colors, history, hash);
message.push(String.fromCharCode(code));
}
return message.join("");
}

java接口调用:Steganography。