隱寫術是一種將秘密訊息隱藏於公開資訊中的技術,本文將以 Python 為例,示範如何將訊息嵌入圖片中。首先,訊息會被轉換為位元序列,然後嵌入到圖片的畫素中,修改畫素的最低有效位以隱藏訊息。這個過程需要仔細處理位元操作,確保訊息可以被正確地嵌入和提取。同時,為了驗證訊息的完整性,我們會使用雜湊演算法產生訊息摘要,並使用金鑰加密摘要,以防止竄改。最後,為了更進一步提升安全性,可以將嵌入訊息的圖片封裝成 ZIP 檔案,並將加密後的訊息摘要儲存在 ZIP 檔案的註解中。
資訊隱藏技術:使用隱寫術編碼秘密訊息
隱寫術是一種將秘密訊息隱藏在公開資訊中的技術。我們將探討如何使用Python將秘密訊息編碼成圖片。
字串轉換為位元組和位元
首先,我們需要將字串轉換為位元組和位元。我們可以使用UTF-8編碼將字串轉換為位元組。
message = "http://www.kearsarge.navy.mil"
message_bytes = message.encode("UTF-8")
print(message_bytes)
輸出結果為:
b'http://www.kearsarge.navy.mil'
內容解密:
這段程式碼將字串message使用UTF-8編碼轉換為位元組。UTF-8編碼是一種廣泛使用的編碼方式,可以將Unicode字元轉換為位元組。在這個例子中,由於字串只包含ASCII字元,因此UTF-8編碼後的結果與原始字串相似。
位元組轉換為位元
接下來,我們需要將位元組轉換為位元。我們可以定義兩個函式:to_bits和to_byte。
def to_bits(v):
b = []
for i in range(8):
b.append(v & 1)
v >>= 1
return tuple(reversed(b))
def to_byte(b):
v = 0
for bit in b:
v = (v << 1) | bit
return v
內容解密:
to_bits函式將一個位元組轉換為8個位元。它使用位運算來提取每個位元,並將結果儲存在一個列表中。to_byte函式則相反,將8個位元轉換回一個位元組。這兩個函式可以用來測試和驗證位元組和位元之間的轉換。
測試位元組和位元之間的轉換
我們可以使用一個迴圈來測試to_bits和to_byte函式。
for test in range(256):
b = to_bits(test)
v = to_byte(b)
assert v == test
內容解密:
這段程式碼測試了to_bits和to_byte函式的正確性。它將0到255之間的所有整數轉換為位元,然後再轉換回整數,並驗證結果是否正確。
將訊息轉換為位元
現在,我們可以將訊息轉換為位元。
message_bytes = message.encode("UTF-8")
bits = list(to_bits(c) for c in message_bytes)
print(bits)
輸出結果為:
[(0, 1, 1, 0, 1, 0, 0, 0), (0, 1, 1, 1, 0, 1, 0, 0), (0, 1, 1, 1, 0, 1, 0, 0), (0, 1, 1, 1, 0, 0, 0, 0), (0, 0, 1, 1, 1, 0, 1, 0), (0, 0, 1, 0, 1, 1, 1, 1), (0, 0, 1, 0, 1, 1, 1, 1), (0, 1, 1, 1, 0, 1, 1, 1), (0, 1, 1, 0, 1, 1, 1, 0), (0, 1, 1, 0, 1, 1, 1, 0), (0, 1, 1, 0, 1, 1, 1, 0), (0, 0, 1, 0, 1, 1, 1, 0), (0, 1, 1, 0, 1, 0, 1, 1), (0, 1, 1, 0, 0, 1, 0, 1), (0, 1, 1, 0, 0, 0, 0, 1), (0, 1, 1, 1, 0, 0, 1, 0), (0, 1, 1, 1, 0 , ...]
圖表說明:訊息轉換過程
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 圖表說明:訊息轉換過程
rectangle "UTF-8編碼" as node1
rectangle "to_bits函式" as node2
rectangle "隱寫術" as node3
node1 --> node2
node2 --> node3
@enduml此圖示展示了將原始訊息轉換為位元的過程。首先,使用UTF-8編碼將原始訊息轉換為位元組。然後,使用to_bits函式將位元組轉換為位元。最終,這些位元被用於隱寫術,將訊息隱藏在圖片中。
資訊隱藏技術:訊息編碼實作
資訊隱藏(Steganography)是一種將秘密訊息隱藏在公開媒體中的技術,常見的做法是將訊息嵌入圖片中。以下將詳細介紹如何實作訊息的編碼與解碼。
組合位元序列
要將訊息隱藏在圖片中,首先需要將訊息轉換為位元序列。我們可以透過以下步驟達成:
- 將訊息編碼為位元組(bytes)。
- 將每個位元組轉換為 8 位元的二進位表示。
位元序列生成器
為了有效地處理大量位後設資料,我們可以使用生成器(generator)函式來逐步產生位元序列,而非一次性將所有位元載入記憶體。
def bit_sequence(list_of_tuples):
for t8 in list_of_tuples:
for b in t8:
yield b
這個生成器函式接受一個由 8 位元 Tuple 組成的列表,並逐一產生每個位元。
將位元序列轉換為位元組
為了將位元序列重新轉換為原始的位元組資料,我們需要將 8 個位元組成一個位元組。
def byte_sequence(bits):
byte = []
for n, b in enumerate(bits):
if n % 8 == 0 and n != 0:
yield to_byte(byte)
byte = []
byte.append(b)
yield to_byte(byte)
內容解密:
bit_sequence函式負責將 8 位元 Tuple 列表轉換為單一位元的序列。byte_sequence函式則是將單一位元序列重新組合為位元組。- 使用
enumerate來追蹤位元的索引,以便在每 8 個位元時產生一個位元組。
編碼訊息
在將訊息隱藏到圖片之前,我們需要先對訊息進行編碼,並加入長度資訊,以便解碼時知道何時停止。
編碼流程:
- 將訊息轉換為 UTF-8 編碼的位元組。
- 計算訊息的長度,並將其表示為 2 個位元組(高位元組和低位元組)。
- 將長度資訊和訊息內容合併成一個位元序列。
message_bytes = message.encode("UTF-8")
bits_list = list(to_bits(c) for c in message_bytes)
len_h, len_l = divmod(len(message_bytes), 256)
size_list = [to_bits(len_h), to_bits(len_l)]
bit_sequence(size_list + bits_list)
內容解密:
message.encode("UTF-8")將訊息轉換為 UTF-8 編碼的位元組。- 使用
divmod計算訊息長度的高低位元組。 - 將長度資訊和訊息內容的位元表示合併,以便嵌入圖片。
隱寫術中的秘密訊息編碼技術深度解析
隱寫術是一種將秘密訊息隱藏在公開媒體中的技術,常見的做法是將訊息嵌入圖片中。本文將探討如何使用Python實作圖片隱寫術,包括編碼和解碼的過程。
編碼過程:將秘密訊息嵌入圖片
首先,我們需要將秘密訊息轉換成二進位序列,然後將其嵌入圖片的畫素中。以下是具體步驟:
- 準備圖片:選擇一張圖片作為載體,並將其開啟。
- 轉換訊息為二進位序列:將秘密訊息轉換成二進位序列。
- 嵌入訊息:使用
putpixel()和getpixel()方法更新圖片的畫素,將二進位序列嵌入紅色通道的最低位。
w, h = ship.size
for p, m in enumerate(bit_sequence(size_list + bits_list)):
y, x = divmod(p, w)
r, g, b = ship.getpixel((x, y))
r_new = (r & 0xfe) | m
print((r, g, b), m, (r_new, g, b))
ship.putpixel((x, y), (r_new, g, b))
內容解密:
w, h = ship.size:取得圖片的寬度和高度。divmod(p, w):將畫素位置p轉換成座標(x, y)。r & 0xfe:將紅色通道的最低位設為0。(r & 0xfe) | m:將二進位序列m嵌入紅色通道的最低位。
解碼過程:提取隱藏的秘密訊息
解碼過程需要提取嵌入在圖片中的二進位序列,並將其轉換回原始的秘密訊息。
- 提取二進位序列:使用
get_bits()函式從圖片中提取紅色通道的最低位。 - 轉換二進位序列為原始訊息:使用
byte_sequence()函式將二進位序列轉換成原始的秘密訊息。
def get_bits(image, offset=0, size=16):
w, h = image.size
for p in range(offset, offset + size):
y, x = divmod(p, w)
r, g, b = image.getpixel((x, y))
yield r & 0x01
size_H, size_L = byte_sequence(get_bits(ship, 0, 16))
size = size_H * 256 + size_L
message = byte_sequence(get_bits(ship, 16, size * 8))
內容解密:
get_bits()函式:從圖片中提取紅色通道的最低位,生成二進位序列。byte_sequence()函式:將二進位序列轉換成原始的秘密訊息。size_H * 256 + size_L:計算原始訊息的大小。
重建原始訊息
最後,使用Python的bytes.decode()方法將提取的位元組序列解碼成原始的字串。
print(bytes(message).decode("UTF-8"))
內容解密:
bytes(message):將位元組序列轉換成bytes物件。.decode("UTF-8"):使用UTF-8編碼將bytes物件解碼成字串。
使用隱寫術確保訊息完整性
隱寫術可用於確保訊息在傳輸過程中未被竄改。如果我們無法正確解碼嵌入的數位浮水印,就表示圖片已被竄改。這是一種檢測竄改的方法。更強健的方法是使用雜湊值(hash totals)。有多種雜湊演算法可用於產生一系列位元組的摘要或簽章。我們將訊息和雜湊碼分開傳送。如果收到的訊息與雜湊碼不匹配,就表示傳輸過程中出了問題。
使用雜湊值驗證檔案
Python 的 hashlib 模組提供了多種雜湊演算法。軟體下載通常會提供軟體套件的 MD5 雜湊值。我們可以使用 hashlib 計算檔案的 MD5 摘要,如下所示:
import hashlib
md5 = hashlib.new("md5")
with open("LHD_warship.jpg", "rb") as some_file:
md5.update(some_file.read())
print(md5.hexdigest())
內容解密:
- 建立 MD5 摘要物件:使用
hashlib.new("md5")建立一個 MD5 摘要物件,用於計算檔案的雜湊值。 - 開啟檔案:以二進位模式 (
"rb")開啟檔案LHD_warship.jpg,確保讀取的是原始位元組資料。 - 更新摘要:將檔案內容傳遞給
md5.update()方法,計算檔案的 MD5 雜湊值。對於大檔案,可以分塊讀取以避免一次性載入整個檔案到記憶體。 - 輸出十六進位雜湊值:使用
md5.hexdigest()輸出 MD5 雜湊值的十六進位字串表示。
輸出範例:
0032e5b0d9dd6e3a878a611b49807d24
這個安全的雜湊值允許我們確認檔案在傳輸過程中未被竄改。
使用金鑰增強訊息摘要安全性
我們可以透過新增金鑰到訊息摘要來提供額外的安全性。這不會加密訊息本身,而是加密摘要以確保摘要在傳輸過程中未被竄改。
Python 的 hmac 模組處理了這項工作,如下所示:
import hmac
with open("LHD_warship.jpg", "rb") as some_file:
keyed = hmac.new(b"Agent Garbo", some_file.read())
print(keyed.hexdigest())
內容解密:
- 建立 HMAC 摘要物件:使用
hmac.new(b"Agent Garbo", ...)建立一個 HMAC 摘要物件,其中b"Agent Garbo"是金鑰。 - 傳遞檔案內容:將檔案內容傳遞給 HMAC 摘要物件,計算帶有金鑰的雜湊值。
- 輸出十六進位雜湊值:使用
keyed.hexdigest()輸出 HMAC 雜湊值的十六進位字串表示。
輸出範例:
42212d077cc5232f3f2da007d35a726c
HQ 知道我們的金鑰,因此可以確認訊息是否來自我們。同樣,當 HQ 向我們傳送訊息時,我們可以使用相同的金鑰驗證訊息的真實性。
加密訊息的挑戰
雖然加密似乎可以防止竄改,但它需要謹慎管理加密金鑰。加密並非萬能藥。使用良好的加密演算法但失去對金鑰的控制,會使加密變得無效。擁有未授權金鑰的人可以重寫檔案而無人知曉。
結合隱寫術與 ZIP 壓縮
我們可以將隱寫術與建立 ZIP 檔案結合,將訊息嵌入圖片中,並將其封裝成 ZIP 檔案。由於 ZIP 檔案可以包含註解字串,我們可以在 ZIP 檔案的註解中加入 HMAC 簽章。
定義封裝函式
def package(text, image_source, key_hmac, filename):
image = Image.open(image_source)
steg_embed(image, text)
image.save("/tmp/package.tiff", format="TIFF")
with open("/tmp/package.tiff", "rb") as saved:
digest = hmac.new(key_hmac.encode("ASCII"), saved.read())
with ZipFile(filename, "w") as archive:
archive.write("/tmp/package.tiff", "image.tiff")
archive.comment = digest.hexdigest().encode("ASCII")
os.remove("/tmp/package.tiff")
內容解密:
- 開啟圖片:使用
Image.open(image_source)開啟來源圖片。 - 嵌入訊息:呼叫
steg_embed(image, text)將秘密訊息嵌入圖片中。 - 儲存圖片:將更新後的圖片儲存到臨時檔案
/tmp/package.tiff。 - 計算 HMAC 摘要:讀取臨時檔案並計算其 HMAC 摘要,使用提供的金鑰
key_hmac。 - 建立 ZIP 壓縮檔:建立一個新的 ZIP 檔案,將處理後的圖片寫入其中,並將 HMAC 摘要作為 ZIP 檔案的註解。
- 清理臨時檔案:刪除臨時檔案
/tmp/package.tiff以避免留下潛在的證據。
這個過程結合了隱寫術和加密技術,提高了訊息傳輸的安全性。接下來,我們需要實作 steg_embed() 函式來完成隱寫編碼的工作。
地理定位資訊與隱寫術的整合應用
在前一章中,我們探討瞭如何利用隱寫術在影像檔案中隱藏訊息。本章將進一步擴充套件相關技術,結合地理定位資訊與網路服務,實作更複雜的資料蒐集與分析。
解碼隱藏訊息的函式實作
要完整實作隱寫術的應用,我們需要實作 steg_extract() 函式,用於從影像中提取隱藏的訊息。該函式的實作細節將在後續章節中詳細說明。
解封裝函式 unpackage()
首先,我們需要實作一個用於解封裝 ZIP 檔案的函式 unpackage(),其定義如下:
def unpackage(filename, key_hmac):
try:
os.remove("/tmp/image.tiff")
except FileNotFoundError:
pass
with ZipFile(filename, "r") as archive:
with archive.open("image.tiff", "r") as member:
keyed = hmac.new(key_hmac.encode("ASCII"), member.read())
assert archive.comment == keyed.hexdigest().encode("ASCII"), "Invalid HMAC"
archive.extract("image.tiff", "/tmp")
image = Image.open("/tmp/image.tiff")
text = steg_extract(image)
os.remove("/tmp/image.tiff")
return text, image
#### 內容解密:
- 移除暫存檔案:首先嘗試刪除暫存檔案
/tmp/image.tiff,若檔案不存在則捕捉FileNotFoundError例外並忽略。 - 開啟 ZIP 檔案:使用
ZipFile開啟指定的 ZIP 檔案,並讀取其中的image.tiff成員。 - 驗證 HMAC:計算
image.tiff的 HMAC 值,並與 ZIP 檔案的註解進行比對。若驗證失敗,則丟出例外。 - 提取影像檔案:將
image.tiff提取到/tmp目錄下,並開啟該影像檔案。 - 提取隱藏訊息:使用
steg_extract()函式從影像中提取隱藏的訊息。 - 清理暫存檔案:刪除提取出的影像檔案
/tmp/image.tiff。 - 傳回結果:傳回提取出的訊息和影像物件。
地理定位資訊的基礎知識
在進行地理定位資訊處理之前,我們需要了解一些基本的術語和概念。
全球定位系統(GPS)
GPS 是一種根據衛星的定位系統,能夠精確地確定接收器的位置和時間。每顆 GPS 衛星都會傳送包含其位置和精確時間戳的資料流。接收器透過接收多個衛星的資料,可以計算出自己的位置。
緯度和經度
- 緯度:是相對於赤道和極點測量的角度,需提供方向(N 或 S)。例如,36°50′40.12′′N 表示北緯。
- 經度:是相對於本初子午線(格林威治子午線)測量的角度,向東為正,向西為負。例如,76°17′35.21′′W 可表示為 -76.293114。
球面幾何
由於地球是球體,經緯度的計算需要使用球面幾何而非簡單的平面幾何。緯度線是與赤道平行的,而經度線則在南北極相交。