iOS常用加密算法

本篇主要介绍笔者在iOS开发工作中用到的加解密算法的使用,主要包括:
1)对称加密算法:AES、DES、3DES
2)非对称加密算法:RSA、DSA、ECC
3)哈希(散列)函数(消息摘要算法):MD5、SHA1、SHA256、MurmurHash
4)编码方式:Base64、Base58
5)苹果原生加密相关算法的系统库:CommonCrypto、Security、CryptoKit
在讲解每一种加解密算法的使用时,笔者都会尽可能的附上相应的代码,希望能让读者更容易理解或直接用于项目中。如有疑问欢迎留言探讨。
本篇对应的Deom地址:https://github.com/zhoushejun/SJSecurity,欢迎Star。

APP内的数据安全是非常重要的一部分,尤其是对用户的隐私数据的保护。在我们的日常开发工作中,需要对应用中的多项数据进行加解密处理,但从性能与安全性方面考虑,可能会使用一种或多种不同的加解密算法。

一、对称加密算法

基本概念:指加密和解密使用相同密钥的加密算法,也叫私钥加密
严格来讲不一定是相同的密钥,满足以下条件也是对称加密:加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。但在大多数的对称加密算法中,加密密钥和解密密钥是相同的。
优点:算法公开,计算量小,加解密速度快、效率高。
缺点:对称加密算法的安全性取决于加密密钥的保存情况,安全性得不到保证。
常用算法:AES、DES、3DES

AES:高级加密标准(Advanced Encryption Standard),是现在公认的最安全的加密方式,是对称密钥加密中最流行的算法。苹果的钥匙串访问采用的就AES加密。AES有两种常用的加密模式:
ECB模式:电子密码本(Electronic Code Book Mode),加密文件会被分为若干个加密块,每个块都是独立进行加密,无初始向量,相同的明文将永远加密成相同的密文。容易受到密码本重放攻击,一般情况下很少用。
CBC模式:密码块链(Cipher Block Chaining Mode),使用一个密钥和一个初始化向量(IV)对数据执行加密转换。加密文件同样会被分为若干个加密块,每个块都依赖于上一个加密块进行加密,明文被加密前要与前面的密文进行异或运算后再加密,因此只要选择不同的初始向量,相同的明文加密后会形成不同的密文,这是目前应用最广泛的模式。CBC加密后的密文是上下文相关的,但明文的错误不会传递到后续分组,但如果一个分组丢失,后面的分组将全部作废(同步错误)。

DES:数据加密标准,加密强度不够,能够暴力破解。笔者开发中未曾使用。
3DES:使用3个密钥对相同的数据执行3次加密,增强加密强度。但需要维护3个密钥,维护成本大。笔者开发中也未曾使用。
CommonCrypto库:苹果提供的系统加密库,有原生的对称加密算法和Hash算法,支持iOS和Mac开发。其支持加密算法有:AES、DES、3DES、CAST、RC2、RC4、Blowfish、MD5、SHA1/224/256/384/512等加密算法。其中AES、DES、3DES、CAST、RC2、Blowfish等6种为分组加密算法,RC4为流加密算法

AES加解密算法

image

二、非对称加密算法

基本概念:指加密和解密使用不同密钥的加密算法,也称为公私钥加密
优点:公钥是公开的,私钥是自己保存的,不需要将私钥给别人,安全性更高。
缺点:加解密花费时间长、速度慢,只适合对少量数据进行加密。加解密速度要远远慢于对称加密。
常用算法:RSA、DSA、ECC

Security.framework:苹果提供的系统加密库,支持iOS和Mac开发。需要了解这库里的两个结构体:
SecPadding:创建或验证数字签名时使用的填充类型。。
SecKeyAlgorithm:对SecKey对象执行加密操作的可用算法。其支持的算法有:RSA、ECDSA、ECIES、ECDH等算法。

RSA:该算法由3位麻省理工学院的数学家“罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)”一起设计的,可以实现非对称加密。所以用他们三个人的名字命名,叫RSA算法。欧拉定理是RSA算法的核心,理解了这个定理,就可以理解RSA。主要了解公私钥的生成、公钥加密、私钥解密、私钥签名、公钥验签、读取证书信息,以及在Keychain中存储、查找、删除密钥等功能。
ECDSA:椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟,是一种数字签名算法。
ECDH:ECC算法和DH结合使用,用于密钥磋商,这个密钥交换算法称为ECDH。交换双方可以在不共享任何秘密的情况下协商出一个密钥。ECC是建立在基于椭圆曲线的离散对数问题上的密码体制,给定椭圆曲线上的一个点P,一个整数k,求解Q=kP很容易;给定一个点P、Q,知道Q=kP,求整数k确是一个难题。ECDH是安全密钥交换算法。
ECIES:是一种集成加密方案。ECIES加密可以认为是先基于ECDH协商密钥,再用刚刚协商一致的密钥进行对称加密,这样能够结合非对称加密和对称加密的优点。

1)RSA生成密钥对算法:

image

2)RSA加解密算法:

image

扩展:根据官方资料显示,上图中的加解密算法加密的文本长度是受限制的。RSA加解密为块加密算法( block cipher algorithm),在固定长度的数据块上进行操作。 block length是跟key length 以及所使用的填充模式相关:
Raw模式:要加密的数据块长度 <= SecKeyGetBlockSize(:)
PKCS1模式:要加密的数据块长度 <= SecKeyGetBlockSize(:)-11。(Public Key Cryptography Standards)
OAEPX模式:要加密的数据块长度 <= SecKeyGetBlockSize(:)-42/58/66/98/130。(Optimal Asymmetric Encryption Padding)

RSA加密的两种算法分别是RSAES-PKCS-v1_5 and RSAES-OAEP。这里有必要了解一下加密算法参数algorithm的各种值的意义,在实际情况下才能根据需要使用:
rsaEncryptionRaw:原始的RSA加密或解密,数据的大小必须符合RSA密钥的模数大小。直接不使用填充的算法在密码学上是非常弱的。输入数据大小必须小于或等于key length大小,并且返回的块大小总是与SecKeyGetBlockSize()返回的块大小相同。
rsaEncryptionPKCS1:RSA加密或解密,数据填充使用PKCS#1填充方案。此算法仅用于与现有协议和数据的向后兼容,笔者不建议再使用这个填充方案的加密算法。建议选择更强的加密算法(参见rsaEncryptionOAEP)。输入数据最多只能是“SecKeyGetBlockSize() - 11”bytes字节长,并且返回的块大小总是与SecKeyGetBlockSize()返回的块大小相同。
rsaEncryptionOAEP(SHAX)系列:SHAX系列包括-SHA1(-42)、SHA224(-58)、SHA256(-66)、SHA384(-98)、SHA512(-130)。RSA加密或解密,使用OAEP填充方案填充的数据,其内部使用SHAX哈希算法。输入数据最多只能是“SecKeyGetBlockSize()- 42/58/66/98/130”bytes字节长,并且返回的块大小总是与SecKeyGetBlockSize()返回的块大小相同。使用对应的算法:rsaEncryptionOAEPSHAXAESGCM能够加密和解密任意长的数据。
rsaEncryptionOAEP(SHAXAESGCM)系列:使用OAEP填充方案,由RSA加密随机生成的AES会话密钥。在GCM模式下,使用全部为0的16字节长IV(初始化向量)的会话密钥对用户数据进行加密。最后,将16字节的AES-GCM标记附加到密文后。如果RSA密钥为4096bit或更大,则使用256bit长的AES密钥,否则使用128bit长的AES密钥。原始public key公钥数据用作AES-GCM加密的身份验证数据。

这里需要了解一个问题:什么是AES-GCM?待完善。。。

3)RSA签名与验签算法:

image

扩展:目前主流的RSA签名方式包括RSA-PSSRSA-PKCS,签名过程包括hash和加密。
PSS: (Probabilistic Signature Scheme)私钥签名流程的一种填充模式。相对应PKCS是一种能够自我从签名中恢复原来的签名,而PSS无法从签名中恢复原来的签名。openssl-1.1.x以后默认在ssl握手的server key exchange阶段使用更安全的PSS的RSA签名模式。
PKCS1:(Public Key Cryptography Standards)
PKCS1SHAX:选择对应的SHA1/224/256/384/512哈希算法把要签名的数据转换后成标准的“ASN.1”格式的数据后,再用private key按PKCS数据填充模式对这个数据进行加密。
问题1:为什么RSA签名要引入模式?
因为RSA算法沒有加入乱数,TLS流程中的密钥材料若不进行填充而直接加密,那么显然相同的key,会得到相同的密文。这种在语义上来说,当出现重复性的原始资料,攻击者会通过相同加密密文而猜测出原文,这是不安全的。因此导入padding的机制來加強安全性。

4)读取本地证书公私钥算法

image
image
image
image

扩展:证书相关的一些概念或操作
SSL/TLS:Secure Sockets Layer (安全套接层协议)/Transport Layer Security (传输层安全)。TLS协议包括两层:TLS记录和TLS握手协议,SSL/TLS 握手是为了安全地协商出一份对称加密的密钥。可以通过Wireshark 抓包工具查看在TLS的握手过程中的各种参数。

SSL/TLS协议的基本过程:
1)客户端向服务器端索要并验证公钥
2)双方协商生成会话密钥,也叫“共享密钥”、“公共密钥”。
3)双方采用会话密钥进行加密通信。

OpenSSL:Open Secure Sockets Layer(开放式安全套接层协议),是一个开放源代码的软件库包。主要功能包括:SSL协议库应用程序以及密码算法库等3个部分,囊括了主要的密码算法、常用的密钥和证书封装管理功能以及SSL协议,并提供了丰富的应用程序供测试或其它目的使用。

OpenSSL实现的算法:
8种对称加密算法:AES、DES、CAST、RC2、RC4、RC5、Blowfish、IDEA。
4种非对称加密算法:DH、RSA、DSA、ECC。
6种信息摘要算法:MD2、MD5、MDC2、SHA(SHA1)、RIPEMD、DSS(DSS1)。

X.509:密码学里公钥证书的格式标准,即证书标准,主要定义了证书中应该包含哪些内容。

证书的编码格式
PEM:ASCII格式。Privacy Enhanced Mail(隐私增强型电子邮件),DER编码的证书再进行Base64编码后的数据,打开看文本格式,以“-----BEGIN CERTIFICATE-----”开头,“-----END CERTIFICATE-----”结尾。
DER:二进制格式。Distinguished Encoding Rules,打开看是二进制格式,不可读,但Base64编码后也很常见。
证书的扩展名
.pem:见编码格式。
.crt:证书可以被编码为二进制DER或ASCII PEM。 cer和crt扩展几乎是同义词。
.cer: .crt的替代形式。
.der:见编码格式。
.p7b,.p7c:PKCS#7格式,签名的数据结构没有数据,只有证书或URL。
.pfx:PKCS#12之前的格式(通常用PKCS#12格式,比如那些由IIS产生的PFX文件)。
.p12:PKCS#12格式,二进制格式,包含证书的同时可能还有带密码保护的私钥。

前向保密性:Forward Secrecy,将来任何加密密钥的披露都无法解密过去的任何记录。

参见:https://en.wikipedia.org/wiki/Forward_secrecy

5)Keychain密钥操作
待完善。。。
扩展:把公钥和私钥信息保存到Keychain里面。

6)ECDSA
扩展:ECDSA(Elliptic Curve Digital Signature Algorithm) 是椭圆曲线数字签名算法,是 DSA 作用于椭圆曲线的一个变种算法,是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。ECDSA 需要使用明文的哈希结果,而不是明文本身。哈希函数的选择取决于使用者,但是需要明确的是必须选择加密安全的哈希函数。对于相同的安全级别,ECDSA密钥和签名比RSA中的短,256-bit的ECDSA签名具有与3072-bit的RSA签名相同的安全强度。

7)DH、ECDH、X3DH
扩展
DH:Diffie-Hellman密钥交换协议/算法(Diffie-Hellman Key Exchange/Agreement Algorithm),DH密钥交换是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道创建起一个密钥。这个密钥可以在后续的通讯中作为对称密钥来加密通讯内容。公钥交换的概念最早由瑞夫·墨克提出,而这个密钥交换方法,由惠特菲尔德·迪菲和马丁·赫尔曼在1976年首次发表。 DH密钥交换的同义词包括: DH密钥协商、DH密钥创建、指数密钥交换、DH协议。虽然DH密钥交换本身是一个匿名的密钥交换协议,它却是很多认证协议的基础,并且被用来提供传输层安全协议的短暂模式中的前向安全性。DH密钥交换协议/算法只能用于密钥的交换,而不能进行消息的加密和解密。双方确定要用的密钥后,要使用其他对称密钥操作加密算法实现加密和解密消息。DH与RSA的不同之处在于,DH不是加密算法,它只是生成可用作对称密钥的秘密数值。在DH密钥交换过程中,发送方和接收方分别生成一个秘密的随机数,并根据随机数推导出公开值,然后,双方再交换公开值。DH算法的基础是具备生成共享密钥的能力。只要交换了公开值,双方就能使用自己的私有数和对方的公开值来生成对称密钥,称为共享密钥,对双方来说,该对称密钥是相同的,可以用于使用对称加密算法加密数据。
ECDH:本质是密钥协商协议,是椭圆曲线的Diffie-Hellman算法的一种变体,而不仅仅是加密算法。从根本上讲,这意味着ECDH在一定程度上定义了各方之间如何生成和交换密钥。如何使用这些密钥加密数据取决于通信双方。常用来做ECDH密钥交换的椭圆曲线Curve25519和Curve448,也用来做数字签名。
为什么Curve25519和Curve448的公私钥对可以用来做数字签名?
Ed25519,Ed448和Curve25519和Curve448的差异,这两者分别是曲线的不同表达形式,一个是 Twisted Edwards曲线(适合做签名曲线) ,一个是Montgomery曲线(适合做DH曲线),两者之间可以相互转化(一个曲线上的点可以对应到另外一个曲线上的点),这也是为什么Curve25519和Curve448的公私钥对可以用来做数字签名。
X3DH:是ECDH的扩展,用来在通信双方之间建立共享密钥。相比ECDH,它最大的不同就是引入了第三方服务器的角色,使得X3DH可以实现某一方在离线的时候也可以进行密钥交换来建立共享密钥,同时根据用户唯一的身份密钥提供身份认证,以及前向安全性。为了实现上述的功能,需要用户提前在服务器存储自己的身份密钥IK,带签名的预存密钥SPK,以及一组一次性密钥OPK。把以上密钥上传到第三方服务器后,其他用户就可以根据这些密钥异步地和该用户协商共同密钥,而不需用户保持在线。协商密钥的消息上传到服务器,等用户上线的时候再去获取计算共同密钥。

8)ECC
扩展:ECC(Elliptic Curve Cryptography)椭圆加密算法是一种基于椭圆曲线数学的公开密钥加密算法,是一种公钥加密体制,其数学基础是利用椭圆曲线上的有理点构成Abel加法群上椭圆离散对数的计算困难性。公钥密码体制根据其所依据的难题一般分为三类:大素数分解问题类、离散对数问题类、椭圆曲线类。有时也把椭圆曲线类归为离散对数类。

9)HKDF
扩展:HMAC-based KDF(Key Derivation Function),基于HMAC的密钥推导函数(也叫密钥派生函数)。HKDF算法是TLS1.3中关于密钥生成的重要的基础算法,跟密钥推导密切相关。HKDF根据初始获取的一些密钥材料,从中派生出一个或多个安全强度很大的密钥。用法详见第三方库 **HKDFKit **地址:https://github.com/WhisperSystems/HKDFKit.git

10)Curve25519
扩展:Curve25519是目前最高水平的Diffie-Hellman函数。给定用户的32字节密钥和另一个用户的32字节公共密钥,Curve25519计算两个用户共享的32字节密钥即共享密钥。然后,在这两个用户之间可以用这个共享密钥进行身份验证和加密消息。用法详见第三方库 **Curve25519Kit **地址:https://github.com/WhisperSystems/Curve25519Kit

三、哈希(散列)函数(消息摘要算法)

基本概念哈希函数,又称散列函数、散列算法,是一种从任何数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(或哈希值)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。
哈希函数分:非加密哈希函数加密哈希函数

第一代非加密哈希函数代表:lookup3,大约为0.5 bytes/cycle,于2006年由Bob Jenkins(人名)发布。
第二代非加密哈希函数代表:MurmurHash,大约是lookup3速度的2倍(大约为1 byte/cycle),当前的版本是MurmurHash3, 能够产生出32-bit或128-bit哈希值。
第三代非加密哈希函数代表:CityHash 和 SpookyHash。于2011年Google发布了CityHash,Bob Jenkins发布了SpookyHash。它们都拥有2倍于MurmurHash的速度,但它们都只使用了64位数学函数而没有32位版本,并且CityHash的速度取决于CRC32 指令。SpookyHash给出128位输出,而CityHash有64位,128位以及256位等几个变种。
以上三代非加密散列函数用的最多的地方是Hash Table。

MD5(Message - Digest Algorithm 5):MD5是一种被广泛使用的密码散列函数,由MD4、MD3、MD2改进而来,主要增强算法复杂度和不可逆性,可以产生出一个128bit(16bytes)的散列值(hash value),用于确保信息传输完整一致。因其普遍、稳定、快速的特点,仍广泛应用于普通数据的加密保护领域。对普通的请求、返回数据,生成MD5校验(MD5中加入动态密钥),进行数据完整性校验。
SHA256:SHA256是SHA-2下细分出的一种算法。SHA-2,名称来自于安全散列算法2(Secure Hash Algorithm 2)的缩写,一种密码散列函数算法标准,由美国国家安全局研发,属于SHA算法之一,是SHA-1的后继者。SHA-2下又可再分为六个不同的算法标准,包括:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。这些标准除了生成摘要的长度 、循环运行的次数等一些微小差异外,算法的基本结构是一致的。回到SHA256上,说白了,它就是一个哈希函数。对于任意长度的消息,SHA256都会产生一个256bit长的哈希值,称作消息摘要。SHA256算法也叫信息摘要算法。这个摘要相当于是个长度为32个字节的数组,通常用一个长度为64的十六进制字符串来表示。主要适用于数字签名标准(DigitalSignature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。

常用算法:SHA-256、SHA-512

CryptoKit:苹果提供的系统加密库,是一个新的Swift框架,支持iOS13.0及以上操作系统,能简化哈希、密钥生成和加密之类的操作。用法详见Demo:https://github.com/zhoushejun/SJSecurity
CryptoKit 支持的算法有:
加密哈希函数:SHA256、SHA384、SHA512。
消息认证码算法:HMAC、SymmetricKey。
对称加密:AES、ChaChaPoly。
非对称加密:Curve25519、P256\P384\P521。
CryptoKit 废弃的算法有:MD5、SHA1。
其中,笔者未用过的算法有:
SymmetricKey:生成对称密钥,可指定任意长度。
ChaChaPoly:使用ChaCha20-Poly1305加密和密封数据。
P256\P384\P521:NIST的P-X系列椭圆曲线,支持硬件加解密。

笔者实现的CryptoKit支持的部分算法的用法详见连接里的Demo:https://github.com/zhoushejun/SJSecurity

四、编码方式

Base58编码:是用于Bitcoin中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+"和"/"符号。设计Base58主要的目的是:避免混淆。在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似。不使用"+"和"/"的原因是非字母或数字的字符串作为帐号较难被接受。没有标点符号,通常不会被从中间分行。大部分的软件支持双击选择整个字符串。但是这个base58的计算量比base64的计算量多了很多。因为58不是2的整数倍,需要不断用除法去计算。而且长度也比base64稍微多了一点。
Base58Check编码:是一种用在比特币中的Base58 编码格式,比特币有内置的检查错误的编码。“校验和”是从编码的数据的哈希值中得到的,可以用来检测并避免转录和输入中产生的错误。使用Base58Check编码时,解码软件会计算数据的“校验和”并和编码中自带的“校验和”进行对比。二者不匹配则表明有错误产生,那么这个Base58Check的数据就是无效的。在比特币中,大多数需要向用户展示的数据都使用Base58Check编码,可以实现数据压缩,易读而且有错误检验。Base58Check编码中的版本前缀是用来创造易于辨别的格式,在Base58Check编码的有效载荷的开始包含了明确的属性。这些属性使用户可以轻松明确被编码的数据的类型以及如何使用它们。
Base64编码:Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。10个数字+26大写字母+26小写字母+“+”+“/”。Base64不是一种加密算法,虽然编码后的字符串看起来有点加密的赶脚,但它实际上是一种“二进制到文本”的编码方法,它能够将给定的任意二进制数据转换(映射)为ASCII字符串的形式,以便在只支持文本的环境中也能够顺利地传输二进制数据。采用Base64编码具有不可读性,需要解码后才能阅读。

参考资料:
MurmurHash:https://sites.google.com/site/murmurhash/
SMHasher:https://github.com/aappleby/smhasher
椭圆加密算法-百度百科:https://baike.baidu.com/item/椭圆加密算法/10305582
curve25519-donna算法实现:https://github.com/agl/curve25519-donna
CryptoKit加密库:http://www.awhisper.net/2019/06/06/wwdc2019-cryptography/
CryptoKit 苹果原生加密库官网:https://developer.apple.com/documentation/cryptokit

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342