ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RGB加解密法:用Python把文字藏进彩色像素的趣味实验

RGB加解密法:用Python把文字藏进彩色像素的趣味实验 最近看到有人聊一个新的加解密想法RGB加解密法。它不追求算法复杂度而是把文字的每个字节拆开映射到图片的 R、G、B 三个颜色通道上最后生成一张彩色图片作为“密文”。解密时反向操作从像素颜色里把字节读回来还原成文字。先给一个明确判断这套方法从密码学角度看不是严肃加密本质上是一种可视化编码 简单混淆。它不适合用来保护真正的密码、Token、隐私数据但非常适合用来理解“字节、通道、像素、编码”之间的关系。如果你正在找一个小而有趣的 Python 练手项目想感受一下“信息藏在颜色里”的玩法这套思路值得研究。本文会从问题出发讲清楚 RGB 加解密法的核心原理给出三个可以直接跑通的 Python 示例基础版、密钥偏移版、RGBA 扩展版并说明运行结果、常见坑和工程边界。建议收藏备用动手试一次会有完全不同的体感。1. 这套 RGB 加解密法到底解决了什么问题做过程序员的人大概都有这种体验写完一个加解密 demo控制台打印出一串十六进制乱码自己都分不清到底加对没有。这类脚本功能是完整的但缺少一个很重要的东西——直观反馈。当你想确认“密文到底变成了什么样”时最痛快的验证方式是把它变成人能直接感知的形态。RGB 加解密法的出发点就在这里。它把数据拆成字节每三个字节一组分别写入 R、G、B 通道。三通道合在一起就是一个像素。所有像素排成一排或者铺成矩形就形成了一张彩色图片。解密时按顺序读出像素颜色拼回字节流再按 UTF-8 解码成原始文本。这个过程真正降低的不是“加密强度”上的成本而是理解成本。它把看不见的二进制流变成看得见的颜色新手很容易建立起“字节 - 数值 - 通道 - 像素”的直觉。相比对着 hexdump 看半天直接在图片里看到自己消息的变化反馈要强烈得多。它适合这几类人刚接触编码和数据结构的学生需要动手项目理解字节、进制和文件组织。想搞趣味编程 Demo 的开发者发送一张密文图片比发送一串十六进制更有话题性。对隐写术感兴趣的人RGB 通道本身也是信息隐藏的常见载体先玩这套编码逻辑会是很好的铺垫。它不适合什么场景如果你的需求是存储口令、备份助记词、传输 Token请直接使用 AES-GCM、ChaCha20 这类经过验证的成熟算法不要把自己的数据安全寄托在玩具级方案上。这个问题在后面的工程边界部分还会重点说。2. RGB 加解密法的核心概念与原理要理解这套方法先明确三个基本概念。2.1 RGB 颜色模型RGB 是计算机里最常见的颜色表示方式用红、绿、蓝三个通道的亮度组合出一种颜色。每个通道取值在 0 到 255 之间正好是一个字节能表示的范围。R 红色0 表示没有红色255 表示红色拉满。G 绿色同理。B 蓝色同理。三个通道组合起来可以表示 1600 多万种颜色。对于编码来说更有意义的是每个通道就是一个字节的天然容器。2.2 字节到像素的映射一段文本在计算机里本质是字节序列。例如英文字母A在 UTF-8 编码下是0x41也就是十进制 65汉字在 UTF-8 下通常占三个字节。RGB 加解密法做的事情很简单把文本按 UTF-8 编码成字节流。每三个字节拆成一组第一个值给 R第二个给 G第三个给 B。用这一组值创建一个像素点。像素连成图片图片就是“密文”。解密就是反向操作读取像素颜色提取 R、G、B 三个数值拼成字节流再按 UTF-8 解码成文本。2.3 长度前缀与填充这里有一个很容易踩的坑字节流长度未必是 3 的倍数。如果最后一组只有两个字节图片像素数量就对不齐。解决方案是在字节流最前面加一个固定长度的长度前缀。例如用前 4 个字节记录原始数据的字节长度解码时先读出长度再从后面的数据里截取对应长度的字节。多出来的填充字节直接丢弃。这个设计在二进制协议里非常常见类似思路在 TCP 自定义协议、文件头格式、消息帧设计中都能看到。学会这个小技巧以后写其他二进制解析代码也能用上。2.4 与“编码”“加密”“隐写”的区别这部分容易混淆用一个表格说明概念作用关键词编码信息表示形式的转换可逆不强调安全性Base64、UTF-8加密在密钥作用下让信息不可读安全性依赖算法和密钥AES、RSA隐写把信息藏进载体中让对方不知道有信息存在图片隐写、音频隐写RGB 加解密法把字节流映射到颜色通道便于可视化传输字节、像素、通道所以把 RGB 加解密法叫“加解密”可以但更准确的说法是“通道编码 简单混淆”。它真正的价值在于演示过程而不是提供机密性。3. 算法设计的几种思路这套方法并非只有一种写法稍微调整规则就能得到不同变体。这里整理几个典型版本后面会给出其中三个的完整代码。3.1 变体 A字节直通 RGB最简单版本。原文编码成字节后每三个字节生成一个像素。没有密钥所有信息直接暴露在颜色里。优点代码最短逻辑最清晰。 缺点只要对方知道字节到像素的映射规则就能还原原文。这只能算“透明编码”。3.2 变体 B密钥偏移版在字节写进通道之前先对字节做一次变换。例如用密钥对每个字节做 XOR 运算或者做一个可逆的加减偏移。优点不知道密钥的人拿到图也只能看到彩色条纹。 缺点XOR 加密强度有限频率分析下容易被破解密钥空间太小。但它很适合用来理解“混淆”的概念。3.3 变体 CRGBA 四通道版RGB 只有三个通道但常见图片格式还支持第四个 Alpha 通道表示透明度。推广思路后可以四个字节一组分别写入 R、G、B、A。优点相同像素数量下能存放更多数据图片保存为 PNG 时 Alpha 通道不会丢失。 缺点JPEG 格式不支持 Alpha 通道保存格式受限。3.4 变体 D通道置换版如果想让规则再多一点乐趣可以在写入通道前根据密钥决定三个字节分别进入 R、G、B 中的哪一个。例如密钥为 1 时按 R-G-B 顺序密钥为 2 时按 G-R-B 顺序。优点增加排列混淆适合作为进阶题。 缺点实现复杂度上升解密时必须完全还原置换规则。变体复杂度可视化程度是否有密钥适合场景A 字节直通低高无入门教学B 密钥偏移低高有理解混淆C RGBA 扩展中中无提高存储密度D 通道置换中中有进阶练习这些变体之间不冲突完全可以组合使用先用密钥做字节偏移再做通道置换最后写进 RGBA。这套思路很像搭积木每一步都可以作为独立模块拆分讲解。4. 环境准备与前置条件动手之前先准备好环境。这套示例不依赖重型框架只需要 Python 和 Pillow 图像库。4.1 安装 Python建议使用 Python 3.6 以上版本。具体版本以你本机为准本文示例不涉及高版本专属语法兼容性很稳。检查 Python 是否已安装python3 --versionWindows 用户可能要用python --version或py --version根据实际环境调整。4.2 安装 PillowPillow 是 Python 生态里最常用的图像处理库支持创建图像、读取像素、保存 PNG 等操作。pip install pillow如果本机同时存在 Python 2 和 Python 3可能需要使用pip3 install pillow安装完成后可以验证一下版本python3 -c from PIL import Image; print(Image.__version__)只要能输出 Pillow 版本号就说明环境没问题。4.3 准备一个工作目录建议单独建一个目录存放脚本和生成的密文图片避免文件散落。例如mkdir rgb-cipher-demo cd rgb-cipher-demo后续代码都放在这个目录下执行。5. 完整示例代码实现下面给出三个可运行示例从最简单的“文本转 RGB 图片”开始逐步加入密钥、通道扩展。每个脚本都可以直接复制保存为.py文件运行。5.1 基础版文本转 RGB 像素图这个版本体现核心逻辑文本 - 字节 - RGB 像素 - 图片。# -*- coding: utf-8 -*- # 文件rgb_cipher_basic.py from PIL import Image def text_to_rgb(text: str, output_path: str secret.png): data text.encode(utf-8) length len(data) # 前 4 个字节存长度方便解码时去掉填充 payload length.to_bytes(4, big) data # 补齐到 3 的倍数 padding (3 - len(payload) % 3) % 3 payload b\x00 * padding pixels [] for i in range(0, len(payload), 3): r, g, b payload[i], payload[i 1], payload[i 2] pixels.append((r, g, b)) # 生成 1 x n 的像素图 img Image.new(RGB, (len(pixels), 1)) img.putdata(pixels) img.save(output_path) print(f密文图像已生成{output_path}) print(f图像尺寸{len(pixels)} x 1 像素) def rgb_to_text(image_path: str) - str: img Image.open(image_path).convert(RGB) pixels list(img.getdata()) payload b for r, g, b in pixels: payload bytes([r, g, b]) # 读取长度并截取数据 length int.from_bytes(payload[:4], big) return payload[4:4 length].decode(utf-8) if __name__ __main__: message Hello CSDNRGB加解密法真好玩 text_to_rgb(message) print(解密结果, rgb_to_text(secret.png))代码逻辑拆解text.encode(utf-8)把字符串转成字节流。len(data).to_bytes(4, big)用 4 字节大端序记录数据长度。填充字节统一用\x00因为真实数据长度已知填充部分解码时会丢弃。Image.new(RGB, (len(pixels), 1))创建一张高度为 1、宽度等于像素数的图片。解码时先拼回完整字节流再截取有效长度。运行方式python3 rgb_cipher_basic.py运行后会生成secret.png并在终端输出解密结果。5.2 密钥偏移版XOR 混淆在基础版之上加入一个密钥。编码前用密钥对每个字节做 XOR 运算。XOR 是可逆的同一个密钥再算一次就能还原所以解密代码和加密逻辑非常对称。这里真正容易踩坑的地方是密钥必须限制在 0 到 255 之间否则超出的高位会污染 byte 转换。# -*- coding: utf-8 -*- # 文件rgb_cipher_xor.py from PIL import Image def _apply_key(payload: bytes, key: int) - bytes: key key 0xFF return bytes([b ^ key for b in payload]) def text_to_rgb_with_key(text: str, key: int, output_path: str secret_xor.png): data text.encode(utf-8) length len(data) payload length.to_bytes(4, big) data padding (3 - len(payload) % 3) % 3 payload b\x00 * padding encrypted _apply_key(payload, key) pixels [] for i in range(0, len(encrypted), 3): pixels.append((encrypted[i], encrypted[i 1], encrypted[i 2])) img Image.new(RGB, (len(pixels), 1)) img.putdata(pixels) img.save(output_path) print(f带密钥的密文图像已生成{output_path}) def rgb_to_text_with_key(image_path: str, key: int) - str: img Image.open(image_path).convert(RGB) pixels list(img.getdata()) encrypted b for r, g, b in pixels: encrypted bytes([r, g, b]) payload _apply_key(encrypted, key) length int.from_bytes(payload[:4], big) return payload[4:4 length].decode(utf-8) if __name__ __main__: k 86 msg 这个密钥是 86不知道密钥只能看到彩色噪点。 text_to_rgb_with_key(msg, k) print(解密结果, rgb_to_text_with_key(secret_xor.png, k))运行方式python3 rgb_cipher_xor.py运行后可以看到同一个密钥能正确还原原文。如果把解密时的k改成其他值例如 87解密结果大概率是一段乱码或者直接抛 UnicodeDecodeError。这就是混淆带来的效果。5.3 RGBA 扩展版第四个通道RGB 三个通道容量有限如果改成 RGBA四字节一组相同像素数量下能多存三分之一的数据。注意 Alpha 通道只在 PNG 等支持透明度的格式中保留保存为 JPEG 会丢失 A 通道。# -*- coding: utf-8 -*- # 文件rgb_cipher_rgba.py from PIL import Image def text_to_rgba(text: str, output_path: str secret_rgba.png): data text.encode(utf-8) length len(data) payload length.to_bytes(4, big) data padding (4 - len(payload) % 4) % 4 payload b\x00 * padding pixels [] for i in range(0, len(payload), 4): pixels.append((payload[i], payload[i 1], payload[i 2], payload[i 3])) img Image.new(RGBA, (len(pixels), 1)) img.putdata(pixels) img.save(output_path) print(fRGBA 密文图像已生成{output_path}) def rgba_to_text(image_path: str) - str: img Image.open(image_path).convert(RGBA) pixels list(img.getdata()) payload b for r, g, b, a in pixels: payload bytes([r, g, b, a]) length int.from_bytes(payload[:4], big) return payload[4:4 length].decode(utf-8) if __name__ __main__: msg RGBA 扩展版第四条通道也参与编码。 text_to_rgba(msg) print(解密结果, rgba_to_text(secret_rgba.png))运行方式python3 rgb_cipher_rgba.py这个版本的代码和基础版几乎一样核心区别是把分组长度从 3 改成 4图片模式从RGB改成RGBA。它演示了一个很重要的工程思想存储格式决定了你能利用的通道数。6. 运行结果与效果验证运行基础版脚本后终端预期输出类似下面这样密文图像已生成secret.png 图像尺寸xx x 1 像素 解密结果 Hello CSDNRGB加解密法真好玩注意图像尺寸中的xx会根据消息长度变化因为不同文本 UTF-8 编码后的字节数不同像素数量也会不同。验证成功最直接的方式打开同目录下的secret.png你会看到一条彩色条纹。每一个像素点都代表三个字节颜色本质上就是数据本身。终端输出的“解密结果”和原始消息完全一致说明编码、解码链路是闭合的。对密钥偏移版用错误密钥解码会得到乱码或报错用正确密钥解码得到原文说明密钥逻辑生效。如果运行失败第一步先看报错信息。最常见的无非两类ModuleNotFoundError表示 Pillow 没装好UnicodeDecodeError表示解码字节不是合法 UTF-8通常是密钥不对或图片被修改过。7. 常见问题与排查方法问题现象可能原因排查方式解决方案ModuleNotFoundError: No module named PILPillow 未安装执行pip list查看包列表执行pip install pillow解码后中文乱码字节拼错、长度截取错误或密钥不匹配检查length读取是否准确确认原始脚本逻辑不要手动复制字节图片保存后再解码结果不对保存格式不是 PNG导致通道数据丢失查看图片后缀和格式统一使用 PNG 格式Windows 终端输出中文乱码控制台编码不是 UTF-8检查终端字符编码设置chcp 65001或改用支持 UTF-8 的终端解密时抛UnicodeDecodeError密钥不对或图片被压缩/裁剪用正确密钥重新解码重新生成密文图避免二次保存图片宽度过大不好查看1 x n 平铺图像过长查看图片尺寸改成矩形铺像素扩大行数这里特别提醒一个隐蔽问题JPEG 是有损压缩格式。如果生成的密文图片被保存为 JPEG颜色值会发生微小变化。表面上看图片差不多但像素的 RGB 值已经不是原始值解码结果会彻底错乱。所以这套方案只适合 PNG、BMP 这类无损格式。8. RGB 加解密法的边界与工程建议8.1 为什么不能替代成熟加密算法虽然这个方法叫“加解密”但它不是真正的安全加密。核心问题有三个没有密钥派生机制。字节直通版甚至没有密钥密钥偏移版也只是一个 0 到 255 的 XOR 混淆暴力枚举 256 种情况就能破解。没有完整性校验。图片被人改了一个像素解码结果就会损坏而且无法检测出来更无法判断是不是被恶意篡改。没有语义安全。相同消息每次加密结果都一样攻击者可以对比多张密文图推断规律这在现代密码学里是必须避免的。如果你的业务场景需要保护真实数据请直接使用 AesGcm、ChaCha20 这类经过验证的算法同时把密钥放在 KMS 或密钥管理服务里不要自己发明算法。8.2 这个项目最大的学习价值它最大的价值在于教学和演示。理解字节和像素的关系。理解二进制协议中“长度前缀 数据体”的设计模式。理解图像格式与色彩通道的区别。理解混淆和加密在强度上的巨大差异。如果作为编程作业或趣味实验可以继续扩展把像素铺成正方形而不是一行加入通道置换规则对原始数据做 Base64 后再编码或者把消息隐藏在一张看起来正常的彩色图片里也就是向隐写术迈一步。8.3 如果开源以下几点要注意如果你打算把类似项目开源到 GitHub 或 Gitee参考以下几点项目会被认为更专业写清楚 README说明“这是一个教学 Demo不是生产级加密算法”避免被人误用于真实场景。选择开源许可证时想清楚用途。想让人随便用选 MIT 或 Apache-2.0希望别人修改后也开源选 GPL。开源许可证没有绝对好坏只有是否匹配你的意图。把示例图片和测试文本一起放进仓库方便别人跑通。在安全说明里明确写一句请勿用于保护真实口令、Token、隐私数据。这样做的好处是其他人搜索到你的项目时能快速判断它是什么、能不能用、边界在哪里。8.4 进一步优化方向当前示例生成的密文图宽度随数据长度增长文本稍长就会变成一条很长的横线。实际项目中可以把像素排成矩形计算像素总数 n。取宽 ceil(sqrt(n))高 ceil(n / 宽)。末尾空位补纯黑色像素。解码时忽略多余像素或者继续用长度前缀截断。改成矩形后密文图更像一张“正常图片”可玩性会高很多。这部分留给你自己实现也是很好的练习。9. 总结RGB 加解密法是一个看起来很“鬼脑”实际上非常容易讲透的项目。它没有复杂的数学背景也没有高深的算法结构核心就是字节和颜色通道之间的映射。通过这套思路你可以直观看到文本变成图片、图片又变回文本的完整过程这种反馈感比纯命令行加密有趣得多。本文整理了基础版、密钥偏移版、RGBA 扩展版三个 Python 示例覆盖了最核心的编码思路、长度前缀设计、通道扩展和常见坑位。如果你刚接触 Python 图像处理建议先跑通基础版再动手加上矩形排布、通道置换、密钥组合这些自定义规则。只有亲手改过代码才能真正理解这类“编码 混淆”方案的特点和局限。最后再强调一次这个项目适合学习、适合玩、适合用来演示编程思路但不要把真实密码、私钥、助记词藏进图片里。信息安全这件事上成熟的算法和规范的安全管理才是最稳妥的选择。如果你对这类可视化编码感兴趣下一步可以了解 LSB 隐写、PNG 文件结构和 Base64 编码原理会发现很多思路都是相通的。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进