|
|
<!DOCTYPE html> |
|
|
<html lang="zh-CN"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>隐写与解密工具</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<style> |
|
|
.tab-content { |
|
|
display: none; |
|
|
} |
|
|
.tab-content.active { |
|
|
display: block; |
|
|
} |
|
|
.tab-btn.active { |
|
|
background-color: #3b82f6; |
|
|
color: white; |
|
|
} |
|
|
#imageCanvas { |
|
|
max-width: 100%; |
|
|
height: auto; |
|
|
border: 1px solid #ddd; |
|
|
border-radius: 0.375rem; |
|
|
} |
|
|
.drop-area { |
|
|
border: 2px dashed #ccc; |
|
|
border-radius: 0.5rem; |
|
|
padding: 2rem; |
|
|
text-align: center; |
|
|
transition: all 0.3s; |
|
|
} |
|
|
.drop-area.highlight { |
|
|
border-color: #3b82f6; |
|
|
background-color: #f0f7ff; |
|
|
} |
|
|
.hidden { |
|
|
display: none; |
|
|
} |
|
|
.result-box { |
|
|
min-height: 150px; |
|
|
border: 1px solid #ddd; |
|
|
border-radius: 0.375rem; |
|
|
padding: 1rem; |
|
|
background-color: #f8fafc; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-50 min-h-screen"> |
|
|
<div class="container mx-auto px-4 py-8 max-w-4xl"> |
|
|
<header class="text-center mb-8"> |
|
|
<h1 class="text-3xl font-bold text-blue-600 mb-2">隐写与解密工具</h1> |
|
|
<p class="text-gray-600">安全地在图片或文本中隐藏秘密信息</p> |
|
|
</header> |
|
|
|
|
|
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8"> |
|
|
<div class="flex border-b"> |
|
|
<button class="tab-btn active px-6 py-3 font-medium text-sm flex-1" data-tab="image-stego">图片隐写</button> |
|
|
<button class="tab-btn px-6 py-3 font-medium text-sm flex-1" data-tab="text-stego">文本隐写</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="image-stego" class="tab-content active p-6"> |
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
<div> |
|
|
<h2 class="text-xl font-semibold mb-4 text-blue-600"> |
|
|
<i class="fas fa-lock mr-2"></i>隐藏信息到图片 |
|
|
</h2> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">选择图片</label> |
|
|
<div id="imageDropArea" class="drop-area cursor-pointer"> |
|
|
<i class="fas fa-cloud-upload-alt text-4xl text-blue-400 mb-2"></i> |
|
|
<p class="text-gray-500">拖放图片到此处或点击选择</p> |
|
|
<input type="file" id="imageInput" accept="image/*" class="hidden"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="secretMessage" class="block text-sm font-medium text-gray-700 mb-2">要隐藏的信息</label> |
|
|
<textarea id="secretMessage" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入要隐藏的秘密信息..."></textarea> |
|
|
</div> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="encryptionKey" class="block text-sm font-medium text-gray-700 mb-2">加密密钥 (可选)</label> |
|
|
<input type="text" id="encryptionKey" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入加密密钥..."> |
|
|
</div> |
|
|
|
|
|
<button id="hideInImageBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out"> |
|
|
<i class="fas fa-key mr-2"></i>隐藏信息 |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<h2 class="text-xl font-semibold mb-4 text-blue-600"> |
|
|
<i class="fas fa-lock-open mr-2"></i>从图片提取信息 |
|
|
</h2> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">选择包含隐藏信息的图片</label> |
|
|
<div id="stegoImageDropArea" class="drop-area cursor-pointer"> |
|
|
<i class="fas fa-cloud-upload-alt text-4xl text-blue-400 mb-2"></i> |
|
|
<p class="text-gray-500">拖放图片到此处或点击选择</p> |
|
|
<input type="file" id="stegoImageInput" accept="image/*" class="hidden"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="decryptionKey" class="block text-sm font-medium text-gray-700 mb-2">解密密钥 (如果加密过)</label> |
|
|
<input type="text" id="decryptionKey" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入解密密钥..."> |
|
|
</div> |
|
|
|
|
|
<button id="extractFromImageBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out"> |
|
|
<i class="fas fa-search mr-2"></i>提取信息 |
|
|
</button> |
|
|
|
|
|
<div class="mt-4"> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">提取结果</label> |
|
|
<div id="extractedMessage" class="result-box"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6"> |
|
|
<canvas id="imageCanvas" class="mx-auto"></canvas> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="text-stego" class="tab-content p-6"> |
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
<div> |
|
|
<h2 class="text-xl font-semibold mb-4 text-blue-600"> |
|
|
<i class="fas fa-lock mr-2"></i>隐藏信息到文本 |
|
|
</h2> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="coverText" class="block text-sm font-medium text-gray-700 mb-2">载体文本</label> |
|
|
<textarea id="coverText" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入公开的文本内容..."></textarea> |
|
|
</div> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="secretText" class="block text-sm font-medium text-gray-700 mb-2">要隐藏的信息</label> |
|
|
<textarea id="secretText" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入要隐藏的秘密信息..."></textarea> |
|
|
</div> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="textEncryptionKey" class="block text-sm font-medium text-gray-700 mb-2">加密密钥 (可选)</label> |
|
|
<input type="text" id="textEncryptionKey" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入加密密钥..."> |
|
|
</div> |
|
|
|
|
|
<button id="hideInTextBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out"> |
|
|
<i class="fas fa-key mr-2"></i>隐藏信息 |
|
|
</button> |
|
|
|
|
|
<div class="mt-4"> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">结果文本</label> |
|
|
<div id="stegoTextResult" class="result-box"></div> |
|
|
<button id="copyTextBtn" class="mt-2 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out hidden"> |
|
|
<i class="fas fa-copy mr-2"></i>复制文本 |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<h2 class="text-xl font-semibold mb-4 text-blue-600"> |
|
|
<i class="fas fa-lock-open mr-2"></i>从文本提取信息 |
|
|
</h2> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="stegoTextInput" class="block text-sm font-medium text-gray-700 mb-2">包含隐藏信息的文本</label> |
|
|
<textarea id="stegoTextInput" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入包含隐藏信息的文本..."></textarea> |
|
|
</div> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label for="textDecryptionKey" class="block text-sm font-medium text-gray-700 mb-2">解密密钥 (如果加密过)</label> |
|
|
<input type="text" id="textDecryptionKey" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" placeholder="输入解密密钥..."> |
|
|
</div> |
|
|
|
|
|
<button id="extractFromTextBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out"> |
|
|
<i class="fas fa-search mr-2"></i>提取信息 |
|
|
</button> |
|
|
|
|
|
<div class="mt-4"> |
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">提取结果</label> |
|
|
<div id="extractedTextMessage" class="result-box"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-lg shadow-md p-6 mb-8"> |
|
|
<h2 class="text-xl font-semibold mb-4 text-blue-600"> |
|
|
<i class="fas fa-info-circle mr-2"></i>关于隐写术 |
|
|
</h2> |
|
|
<div class="text-gray-700 space-y-4"> |
|
|
<p>隐写术是一种将秘密信息隐藏在看似普通的载体(如图片、文本、音频或视频)中的技术,与加密不同,隐写术的重点是隐藏信息的存在。</p> |
|
|
|
|
|
<div class="bg-blue-50 border-l-4 border-blue-500 p-4"> |
|
|
<h3 class="font-semibold text-blue-800 mb-2">图片隐写原理</h3> |
|
|
<p>本工具使用LSB(最低有效位)算法,通过修改图片像素的最低有效位来隐藏信息。人眼几乎无法察觉这些微小变化,但计算机可以提取这些隐藏的位来重建原始信息。</p> |
|
|
</div> |
|
|
|
|
|
<div class="bg-green-50 border-l-4 border-green-500 p-4"> |
|
|
<h3 class="font-semibold text-green-800 mb-2">文本隐写原理</h3> |
|
|
<p>文本隐写使用Unicode零宽度字符和特殊空格字符来隐藏信息。这些字符在大多数文本编辑器和浏览器中不可见,但可以被程序检测和提取。</p> |
|
|
</div> |
|
|
|
|
|
<p class="text-sm text-gray-500">注意:隐写术不能替代加密,敏感信息应先加密再隐藏。本工具仅供学习和合法用途。</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
document.querySelectorAll('.tab-btn').forEach(btn => { |
|
|
btn.addEventListener('click', () => { |
|
|
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); |
|
|
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); |
|
|
|
|
|
btn.classList.add('active'); |
|
|
document.getElementById(btn.dataset.tab).classList.add('active'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const imageInput = document.getElementById('imageInput'); |
|
|
const imageDropArea = document.getElementById('imageDropArea'); |
|
|
const stegoImageInput = document.getElementById('stegoImageInput'); |
|
|
const stegoImageDropArea = document.getElementById('stegoImageDropArea'); |
|
|
const canvas = document.getElementById('imageCanvas'); |
|
|
const ctx = canvas.getContext('2d'); |
|
|
let originalImageData = null; |
|
|
|
|
|
|
|
|
function setupDropArea(dropArea, input) { |
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
|
|
dropArea.addEventListener(eventName, preventDefaults, false); |
|
|
}); |
|
|
|
|
|
function preventDefaults(e) { |
|
|
e.preventDefault(); |
|
|
e.stopPropagation(); |
|
|
} |
|
|
|
|
|
['dragenter', 'dragover'].forEach(eventName => { |
|
|
dropArea.addEventListener(eventName, highlight, false); |
|
|
}); |
|
|
|
|
|
['dragleave', 'drop'].forEach(eventName => { |
|
|
dropArea.addEventListener(eventName, unhighlight, false); |
|
|
}); |
|
|
|
|
|
function highlight() { |
|
|
dropArea.classList.add('highlight'); |
|
|
} |
|
|
|
|
|
function unhighlight() { |
|
|
dropArea.classList.remove('highlight'); |
|
|
} |
|
|
|
|
|
dropArea.addEventListener('drop', handleDrop, false); |
|
|
dropArea.addEventListener('click', () => input.click()); |
|
|
|
|
|
function handleDrop(e) { |
|
|
const dt = e.dataTransfer; |
|
|
const files = dt.files; |
|
|
input.files = files; |
|
|
handleImageUpload(files[0], input === imageInput); |
|
|
} |
|
|
} |
|
|
|
|
|
setupDropArea(imageDropArea, imageInput); |
|
|
setupDropArea(stegoImageDropArea, stegoImageInput); |
|
|
|
|
|
imageInput.addEventListener('change', (e) => { |
|
|
if (e.target.files.length) { |
|
|
handleImageUpload(e.target.files[0], true); |
|
|
} |
|
|
}); |
|
|
|
|
|
stegoImageInput.addEventListener('change', (e) => { |
|
|
if (e.target.files.length) { |
|
|
handleImageUpload(e.target.files[0], false); |
|
|
} |
|
|
}); |
|
|
|
|
|
function handleImageUpload(file, isOriginal) { |
|
|
const reader = new FileReader(); |
|
|
reader.onload = function(event) { |
|
|
const img = new Image(); |
|
|
img.onload = function() { |
|
|
canvas.width = img.width; |
|
|
canvas.height = img.height; |
|
|
ctx.drawImage(img, 0, 0); |
|
|
|
|
|
if (isOriginal) { |
|
|
originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); |
|
|
} |
|
|
}; |
|
|
img.src = event.target.result; |
|
|
}; |
|
|
reader.readAsDataURL(file); |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('hideInImageBtn').addEventListener('click', () => { |
|
|
if (!originalImageData) { |
|
|
alert('请先选择一张图片'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const secretMessage = document.getElementById('secretMessage').value; |
|
|
if (!secretMessage) { |
|
|
alert('请输入要隐藏的信息'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const encryptionKey = document.getElementById('encryptionKey').value; |
|
|
let messageToHide = secretMessage; |
|
|
|
|
|
if (encryptionKey) { |
|
|
messageToHide = simpleEncrypt(secretMessage, encryptionKey); |
|
|
} |
|
|
|
|
|
const stegoImageData = hideMessageInImage(originalImageData, messageToHide); |
|
|
ctx.putImageData(stegoImageData, 0, 0); |
|
|
|
|
|
|
|
|
const downloadBtn = document.createElement('a'); |
|
|
downloadBtn.href = canvas.toDataURL('image/png'); |
|
|
downloadBtn.download = 'stego-image.png'; |
|
|
downloadBtn.className = 'mt-4 inline-block bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-md transition duration-150 ease-in-out'; |
|
|
downloadBtn.innerHTML = '<i class="fas fa-download mr-2"></i>下载隐藏信息的图片'; |
|
|
|
|
|
const existingDownloadBtn = document.querySelector('.download-stego-btn'); |
|
|
if (existingDownloadBtn) { |
|
|
existingDownloadBtn.replaceWith(downloadBtn); |
|
|
} else { |
|
|
downloadBtn.classList.add('download-stego-btn'); |
|
|
canvas.parentNode.appendChild(downloadBtn); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('extractFromImageBtn').addEventListener('click', () => { |
|
|
if (!canvas.width || !canvas.height) { |
|
|
alert('请先选择一张包含隐藏信息的图片'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); |
|
|
const extractedMessage = extractMessageFromImage(imageData); |
|
|
|
|
|
const decryptionKey = document.getElementById('decryptionKey').value; |
|
|
let finalMessage = extractedMessage; |
|
|
|
|
|
if (decryptionKey && extractedMessage) { |
|
|
finalMessage = simpleDecrypt(extractedMessage, decryptionKey); |
|
|
} |
|
|
|
|
|
const resultDiv = document.getElementById('extractedMessage'); |
|
|
if (finalMessage) { |
|
|
resultDiv.innerHTML = `<p class="text-green-600 font-medium">成功提取隐藏信息:</p><p class="mt-2">${finalMessage}</p>`; |
|
|
} else { |
|
|
resultDiv.innerHTML = '<p class="text-red-600">未检测到隐藏信息或密钥错误</p>'; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function hideMessageInImage(imageData, message) { |
|
|
const data = imageData.data; |
|
|
const messageBits = stringToBits(message + '\0'); |
|
|
|
|
|
if (messageBits.length > data.length * 4) { |
|
|
alert('图片太小无法隐藏这么多信息'); |
|
|
return imageData; |
|
|
} |
|
|
|
|
|
for (let i = 0; i < messageBits.length; i++) { |
|
|
const bytePos = Math.floor(i / 8); |
|
|
const bitPos = i % 8; |
|
|
const mask = 1 << bitPos; |
|
|
const bit = (message.charCodeAt(bytePos) & mask) ? 1 : 0; |
|
|
|
|
|
const pixelPos = Math.floor(i / 3); |
|
|
const channel = i % 3; |
|
|
|
|
|
|
|
|
if (bit) { |
|
|
data[pixelPos * 4 + channel] |= 1; |
|
|
} else { |
|
|
data[pixelPos * 4 + channel] &= ~1; |
|
|
} |
|
|
} |
|
|
|
|
|
return imageData; |
|
|
} |
|
|
|
|
|
function extractMessageFromImage(imageData) { |
|
|
const data = imageData.data; |
|
|
let message = ''; |
|
|
let currentByte = 0; |
|
|
let bitCount = 0; |
|
|
|
|
|
for (let i = 0; i < data.length; i += 4) { |
|
|
|
|
|
for (let channel = 0; channel < 3; channel++) { |
|
|
const bit = data[i + channel] & 1; |
|
|
currentByte = (currentByte << 1) | bit; |
|
|
bitCount++; |
|
|
|
|
|
if (bitCount === 8) { |
|
|
if (currentByte === 0) { |
|
|
return message; |
|
|
} |
|
|
message += String.fromCharCode(currentByte); |
|
|
currentByte = 0; |
|
|
bitCount = 0; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return message || null; |
|
|
} |
|
|
|
|
|
function stringToBits(str) { |
|
|
let bits = []; |
|
|
for (let i = 0; i < str.length; i++) { |
|
|
const charCode = str.charCodeAt(i); |
|
|
for (let j = 0; j < 8; j++) { |
|
|
bits.push((charCode >> (7 - j)) & 1); |
|
|
} |
|
|
} |
|
|
return bits; |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('hideInTextBtn').addEventListener('click', () => { |
|
|
const coverText = document.getElementById('coverText').value; |
|
|
const secretText = document.getElementById('secretText').value; |
|
|
|
|
|
if (!coverText) { |
|
|
alert('请输入载体文本'); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (!secretText) { |
|
|
alert('请输入要隐藏的信息'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const encryptionKey = document.getElementById('textEncryptionKey').value; |
|
|
let messageToHide = secretText; |
|
|
|
|
|
if (encryptionKey) { |
|
|
messageToHide = simpleEncrypt(secretText, encryptionKey); |
|
|
} |
|
|
|
|
|
const stegoText = hideMessageInText(coverText, messageToHide); |
|
|
const resultDiv = document.getElementById('stegoTextResult'); |
|
|
resultDiv.textContent = stegoText; |
|
|
|
|
|
const copyBtn = document.getElementById('copyTextBtn'); |
|
|
copyBtn.classList.remove('hidden'); |
|
|
copyBtn.onclick = () => { |
|
|
navigator.clipboard.writeText(stegoText).then(() => { |
|
|
const originalText = copyBtn.innerHTML; |
|
|
copyBtn.innerHTML = '<i class="fas fa-check mr-2"></i>已复制'; |
|
|
setTimeout(() => { |
|
|
copyBtn.innerHTML = originalText; |
|
|
}, 2000); |
|
|
}); |
|
|
}; |
|
|
}); |
|
|
|
|
|
document.getElementById('extractFromTextBtn').addEventListener('click', () => { |
|
|
const stegoText = document.getElementById('stegoTextInput').value; |
|
|
|
|
|
if (!stegoText) { |
|
|
alert('请输入包含隐藏信息的文本'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const extractedMessage = extractMessageFromText(stegoText); |
|
|
|
|
|
const decryptionKey = document.getElementById('textDecryptionKey').value; |
|
|
let finalMessage = extractedMessage; |
|
|
|
|
|
if (decryptionKey && extractedMessage) { |
|
|
finalMessage = simpleDecrypt(extractedMessage, decryptionKey); |
|
|
} |
|
|
|
|
|
const resultDiv = document.getElementById('extractedTextMessage'); |
|
|
if (finalMessage) { |
|
|
resultDiv.innerHTML = `<p class="text-green-600 font-medium">成功提取隐藏信息:</p><p class="mt-2">${finalMessage}</p>`; |
|
|
} else { |
|
|
resultDiv.innerHTML = '<p class="text-red-600">未检测到隐藏信息或密钥错误</p>'; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function hideMessageInText(coverText, secretText) { |
|
|
|
|
|
let binarySecret = ''; |
|
|
for (let i = 0; i < secretText.length; i++) { |
|
|
binarySecret += secretText.charCodeAt(i).toString(2).padStart(8, '0'); |
|
|
} |
|
|
|
|
|
|
|
|
const zeroWidthSpace = '\u200B'; |
|
|
const zeroWidthJoiner = '\u200D'; |
|
|
const zeroWidthNonJoiner = '\u200C'; |
|
|
|
|
|
let hiddenChars = ''; |
|
|
for (let bit of binarySecret) { |
|
|
hiddenChars += bit === '0' ? zeroWidthSpace : zeroWidthJoiner; |
|
|
} |
|
|
|
|
|
|
|
|
return coverText + zeroWidthNonJoiner + hiddenChars; |
|
|
} |
|
|
|
|
|
function extractMessageFromText(stegoText) { |
|
|
const zeroWidthSpace = '\u200B'; |
|
|
const zeroWidthJoiner = '\u200D'; |
|
|
const zeroWidthNonJoiner = '\u200C'; |
|
|
|
|
|
|
|
|
const separatorPos = stegoText.indexOf(zeroWidthNonJoiner); |
|
|
if (separatorPos === -1) return null; |
|
|
|
|
|
const hiddenPart = stegoText.slice(separatorPos + 1); |
|
|
let binaryString = ''; |
|
|
|
|
|
for (let char of hiddenPart) { |
|
|
if (char === zeroWidthSpace) { |
|
|
binaryString += '0'; |
|
|
} else if (char === zeroWidthJoiner) { |
|
|
binaryString += '1'; |
|
|
} else { |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let result = ''; |
|
|
for (let i = 0; i < binaryString.length; i += 8) { |
|
|
const byte = binaryString.slice(i, i + 8); |
|
|
if (byte.length < 8) break; |
|
|
result += String.fromCharCode(parseInt(byte, 2)); |
|
|
} |
|
|
|
|
|
return result || null; |
|
|
} |
|
|
|
|
|
|
|
|
function simpleEncrypt(text, key) { |
|
|
let result = ''; |
|
|
for (let i = 0; i < text.length; i++) { |
|
|
const charCode = text.charCodeAt(i) ^ key.charCodeAt(i % key.length); |
|
|
result += String.fromCharCode(charCode); |
|
|
} |
|
|
return result; |
|
|
} |
|
|
|
|
|
function simpleDecrypt(text, key) { |
|
|
return simpleEncrypt(text, key); |
|
|
} |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=liangzhidanta/simple-steganography-tool" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |