加密的各种姿势

一、Base6

原理:

  • base64的编码都是按字符串长度,以每3个8bit的字符为一组
  • 然后针对每组,首先获取每个字符的ASCII编码
  • 然后将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节
  • 然后再将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节
  • 然后将这4个8bit的字节转换成10进制,对照Base64编码表 (下表),得到对应编码后的字符。
    注:1、 要求被编码字符是8bit的,所以须在ASCII编码范围内,\u0000-\u00ff,中文就不行。
    2、 如果被编码字符长度不是3的倍数的时候,则都用0代替,对应的输出字符为=
系统自带的base64代码
 //只支持加密英文
 NSString *originStr = @"originStr";
    NSData* originData = [originStr dataUsingEncoding:NSASCIIStringEncoding];
    NSString* encodeResult = [originData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSLog(@"encodeResult=%@",encodeResult);
    NSData* decodeData = [[NSData alloc] initWithBase64EncodedString:encodeResult options:0];
    NSString* decodeStr = [[NSString alloc] initWithData:decodeData encoding:NSASCIIStringEncoding];
    NSLog(@"decodeStr=%@",decodeStr);

二、SSkeychain第三方加密 采用DES 对本地化用户名 密码进行加密 对称加密

DES(数据加密标准)原理

DES是一个分组加密算法,它以64位为分组对数据加密。64位一组的明文从算法的一端输入,64位的密文从另一段输出。它是一个对称算法:加密和解密用的是同一个算法。
密钥通常表示为64位的数,但每个第8位都用作奇偶校验,可以忽略,所以密钥长度为56位。密钥可以是任意的56位的数,且可在任意的时候改变。
DES算法只不过是加密的两个基本技术——混乱和扩散的组合,即先代替后置换,它基于密钥作用于明文,这是一轮(round),DES在明文分组上实施16轮相同的组合技术。

这里用第三方SSKeychain(属于DES) 保存用户名密码 本地化加密

pod 'SSKeychain'

  • 加密、解密代码
NSString *bundleID = [NSBundle mainBundle].bundleIdentifier;
    [SSKeychain setPassword:@"111" forService:bundleID account:@"ni"];
    [SSKeychain setPassword:@"222" forService:bundleID account:@"wo"];
    [SSKeychain setPassword:@"333" forService:bundleID account:@"ta"];

    NSString *passwords = [SSKeychain passwordForService:bundleID account:@"wo"];
    NSLog(passwords);

三、AES加密 (高级加密标准) 对称加密

原理:

AES 是一个新的可以用于保护电子数据的加密算法。明确地说,AES 是一个迭代的、对称密钥分组的密码,它可以使用128、192 和 256 位密钥,并且用 128 位(16字节)分组加密和解密数据。与公共密钥密码使用密钥对不同,对称密钥密码使用相同的密钥加密和解密数据。通过分组密码返回的加密数据 的位数与输入数据相同。迭代加密使用一个循环结构,在该循环中重复置换(permutations )和替换(substitutions)输入数据。Figure 1 显示了 AES 用192位密钥对一个16位字节数据块进行加密和解密的情形。

加入第三方AES文件
  • 克隆文件
  • 拖动AES文件到自己项目。
  • 加密代码
// AES加密  (高级加密标准) 对称加密
    NSString *password = @"password";//一般来说用户名就是用户密码的Key
    NSString *passwordKey = @"userName";
    NSString *encryptedData = [AESCrypt encrypt:password password:passwordKey];//加密
    NSString *messagepassword = [AESCrypt decrypt:encryptedData password:passwordKey]; //解密
    NSLog(@"加密结果 = %@",encryptedData);
    NSLog(@"解密结果 = %@",messagepassword);
  • 加密实例代码
-(void)saveUserNameAndPwd:(NSString *)userName andPwd:(NSString *)pwd  
{  
    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];  
    [settings removeObjectForKey:@"UserName"];  
    [settings removeObjectForKey:@"Password"];  
    [settings setObject:userName forKey:@"UserName"];  

    pwd = [AESCrypt encrypt:pwd password:@"pwd"];  

    [settings setObject:pwd forKey:@"Password"];  
    [settings synchronize];  
}  
-(NSString *)getPwd  
{  
    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];  
    NSString * temp = [settings objectForKey:@"Password"];  
    return [AESCrypt decrypt:temp password:@"pwd"];  
}

四、MD5摘要算法

MD5简介

MD5的全称为Message-Digest Algorithm 5,即消息摘要算法第五版,是当前计算机领域用于确保信息传输完整一致而广泛使用的散列算法之一。MD5算法的功能是将数据运算变为另一固定长度值,是散列算法的基础原理。MD5的前身有MD2、MD3和MD4。

原理简介

对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

用途

  • 防止被篡改,比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。
  • 防止直接看到明文,现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。
  • 防止抵赖(数字签名),例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。这样可以防止出现以后A不承认此事而带来的麻烦。
MD5安全性

在用户密码的处理方面,MD5总的来看还算是安全的,至少比明文保存密码要好的多,目前破解MD5主要依靠大型字典的方法,将常用密码进行MD5后建立数据库,然后和MD5数值进行对比,通过这样的方法来“破解”MD5,因此,通常直接将密码进行MD5处理的话,一些弱密码很容易可以通过这种手段“破解”出来。

不过,如果在散列的过程中,加入足够长的salt(即干扰字符串),并且salt加入一些动态信息,例如username、随机码等,这样生成的MD5还是很难被破解的,因为仅仅从数据库无法看到MD5具体的处理过程,必须同时看到处理时的源代码才可以,这就给破解MD5带来相当大的难度。

还有一个方法,既然简单密码的MD5是不安全的,网站的开发者只需要一个简单的技巧就能提高密码的安全度:在用户注册的时候,录入新密码后进行判断,强制密码必须8位以上,并包含字母和数字,否则不让注册,这样用户注册后使用的密码就都是不容易被破解的密码了。

如果需要更安全的算法,建议不用MD5,而使用SHA-256, SHA(Secure Hash Algorithm,安全散列算法)是美国国家安全局(NSA)设计,美国国家标准与技术研究院(NIST)发布的一系列密码散列函数。目前还没有出现针对SHA-256算法的有效碰撞攻击方法,该算法也是开源算法,在很多地方可以找到,是MD5的一个不错的后继者。

MD5集成

1、添加依赖库 libSystem.tdb

2.png

2、新建工具类#import "JHMD5.h"
.h文件

#import <Foundation/Foundation.h>

@interface JHMD5 : NSObject
+(NSString *)md5HexDigest:(NSString *)input;
@end

.m文件

#import "FLTMD5.h"
//需要加入依赖库  libSystem.tdb
#import <CommonCrypto/CommonDigest.h>
@implementation FLTMD5
+(NSString *)md5HexDigest:(NSString *)input{

    const char* str = [input UTF8String];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(str, strlen(str), result);
    NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH];

    for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
        [ret appendFormat:@"%02X",result];
    }
    return ret;
}
@end

3、MD5代码

//MD5加密 MD5是不可逆的只有加密没有解密
    NSString *userName = @"cerastes";
    NSString *password = @"hello Word";
    //   MD5加密 单纯的MD5加密不安全 需要加盐,加盐就是在要加密的内容后加一些常量,常量越长越安全
    //盐值
    NSString *salt = @"追加的:盐值";
    password = [password stringByAppendingString:salt];
    //MD5有两种方式防止暴力破解 1是多次MD5加密,次数是要保密的 2加盐 可变盐,文件越大越安全,当然也就越费时间。3当然还要防止别人破解你的源码,源码被破解是非常危险的。可以咨询爱加密.
    NSString *md5 = [FLTMD5 md5HexDigest:password];
    NSLog(@"%@",md5);
    NSLog(@"%lu",(unsigned long)md5.length);

RSA加密 非对称加密

简介

1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的"非对称加密算法"。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。
这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。

原理简介

RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用。
解密者拥有私钥,并且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送到解密者,解密者用私钥解密将密文解码为明文。
以甲要把信息发给乙为例,首先确定角色:甲为加密者,乙为解密者。首先由乙随机确定一个KEY,称之为密匙,将这个KEY始终保存在机器B中而不发出来;然后,由这个 KEY计算出另一个KEY,称之为公匙。这个公钥的特性是几乎不可能通过它自身计算出生成它的私钥。接下来通过网络把这个公钥传给甲,甲收到公钥后,利用公钥对信息加密,并把密文通过网络发送到乙,最后乙利用已知的私钥,就对密文进行解码了。以上就是RSA算法的工作流程。

RSA集成

1、导入依赖库 Security.framework

2、导入pod第三方库pod 'Base64nl', '~> 1.2'

3、制作秘钥
1.打开Terminal (终端) 进入保存秘钥文件夹

openssl genrsa -out private_key.pem 1024

openssl req -new -key private_key.pem -out rsaCertReq.csr 

openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt

openssl x509 -outform der -in rsaCert.crt -out public_key.der               // Create public_key.der For IOS

openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt  // Create private_key.p12 For IOS. 这一步,请记住你输入的密码,IOS代码里会用到

openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout             // Create rsa_public_key.pem For Java
 
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt     // Create pkcs8_private_key.pem For Java  `

上面七个步骤,总共生成7个文件。其中 public_key.der 和 private_key.p12 这对公钥私钥是给IOS用的, rsa_public_key.pem 和 pkcs8_private_key.pem 是给JAVA用的。
  它们的源都来自一个私钥:private_key.pem , 所以IOS端加密的数据,是可以被JAVA端解密的,反过来也一样。

2.把 public_key.der 和 private_key.p12 拖进你的Xcode项目里去 。

4、新建RSA工具类 #import "RSAEncryptor.h"
.h文件

#import <Foundation/Foundation.h>

@interface RSAEncryptor : NSObject
#pragma mark - Instance Methods

-(void) loadPublicKeyFromFile: (NSString*) derFilePath;
-(void) loadPublicKeyFromData: (NSData*) derData;

-(void) loadPrivateKeyFromFile: (NSString*) p12FilePath password:(NSString*)p12Password;
-(void) loadPrivateKeyFromData: (NSData*) p12Data password:(NSString*)p12Password;




-(NSString*) rsaEncryptString:(NSString*)string;
-(NSData*) rsaEncryptData:(NSData*)data ;

-(NSString*) rsaDecryptString:(NSString*)string;
-(NSData*) rsaDecryptData:(NSData*)data;





#pragma mark - Class Methods

+(void) setSharedInstance: (RSAEncryptor*)instance;
+(RSAEncryptor*) sharedInstance;
@end

.m文件

#import "RSAEncryptor.h"
#import <Base64.h>
#import <Security/Security.h>
@implementation RSAEncryptor
{
    SecKeyRef publicKey;

    SecKeyRef privateKey;
}


-(void)dealloc
{
    CFRelease(publicKey);
    CFRelease(privateKey);
}



-(SecKeyRef) getPublicKey {
    return publicKey;
}

-(SecKeyRef) getPrivateKey {
    return privateKey;
}



-(void) loadPublicKeyFromFile: (NSString*) derFilePath
{
    NSData *derData = [[NSData alloc] initWithContentsOfFile:derFilePath];
    [self loadPublicKeyFromData: derData];
}
-(void) loadPublicKeyFromData: (NSData*) derData
{
    publicKey = [self getPublicKeyRefrenceFromeData: derData];
}

-(void) loadPrivateKeyFromFile: (NSString*) p12FilePath password:(NSString*)p12Password
{
    NSData *p12Data = [NSData dataWithContentsOfFile:p12FilePath];
    [self loadPrivateKeyFromData: p12Data password:p12Password];
}

-(void) loadPrivateKeyFromData: (NSData*) p12Data password:(NSString*)p12Password
{
    privateKey = [self getPrivateKeyRefrenceFromData: p12Data password: p12Password];
}




#pragma mark - Private Methods

-(SecKeyRef) getPublicKeyRefrenceFromeData: (NSData*)derData
{
    SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)derData);
    SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
    SecTrustRef myTrust;
    OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
    SecTrustResultType trustResult;
    if (status == noErr) {
        status = SecTrustEvaluate(myTrust, &trustResult);
    }
    SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);
    CFRelease(myCertificate);
    CFRelease(myPolicy);
    CFRelease(myTrust);

    return securityKey;
}


-(SecKeyRef) getPrivateKeyRefrenceFromData: (NSData*)p12Data password:(NSString*)password
{
    SecKeyRef privateKeyRef = NULL;
    NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
    [options setObject: password forKey:(__bridge id)kSecImportExportPassphrase];
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data, (__bridge CFDictionaryRef)options, &items);
    if (securityError == noErr && CFArrayGetCount(items) > 0) {
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
        securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
        if (securityError != noErr) {
            privateKeyRef = NULL;
        }
    }
    CFRelease(items);

    return privateKeyRef;
}



#pragma mark - Encrypt

-(NSString*) rsaEncryptString:(NSString*)string {
    NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSData* encryptedData = [self rsaEncryptData: data];
    NSString* base64EncryptedString = [encryptedData base64EncodedString];
    return base64EncryptedString;
}

// 加密的大小受限于SecKeyEncrypt函数,SecKeyEncrypt要求明文和密钥的长度一致,如果要加密更长的内容,需要把内容按密钥长度分成多份,然后多次调用SecKeyEncrypt来实现
-(NSData*) rsaEncryptData:(NSData*)data {
    SecKeyRef key = [self getPublicKey];
    size_t cipherBufferSize = SecKeyGetBlockSize(key);
    uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
    size_t blockSize = cipherBufferSize - 11;       // 分段加密
    size_t blockCount = (size_t)ceil([data length] / (double)blockSize);
    NSMutableData *encryptedData = [[NSMutableData alloc] init] ;
    for (int i=0; i<blockCount; i++) {
        int bufferSize = MIN(blockSize,[data length] - i * blockSize);
        NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
        OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes], [buffer length], cipherBuffer, &cipherBufferSize);
        if (status == noErr){
            NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
            [encryptedData appendData:encryptedBytes];
        }else{
            if (cipherBuffer) {
                free(cipherBuffer);
            }
            return nil;
        }
    }
    if (cipherBuffer){
        free(cipherBuffer);
    }
    return encryptedData;
}




#pragma mark - Decrypt

-(NSString*) rsaDecryptString:(NSString*)string {
    NSData *data = [NSData dataWithBase64EncodedString:string];
//    NSData* data = [NSData dataFromBase64String: string];
    NSData* decryptData = [self rsaDecryptData: data];
    NSString* result = [[NSString alloc] initWithData: decryptData encoding:NSUTF8StringEncoding];
    return result;
}

-(NSData*) rsaDecryptData:(NSData*)data {
    SecKeyRef key = [self getPrivateKey];
    size_t cipherLen = [data length];
    void *cipher = malloc(cipherLen);
    [data getBytes:cipher length:cipherLen];
    size_t plainLen = SecKeyGetBlockSize(key) - 12;
    void *plain = malloc(plainLen);
    OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen);

    if (status != noErr) {
        return nil;
    }

    NSData *decryptedData = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen];

    return decryptedData;
}








#pragma mark - Class Methods

static RSAEncryptor* sharedInstance = nil;

+(void) setSharedInstance: (RSAEncryptor*)instance
{
    sharedInstance = instance;
}

+(RSAEncryptor*) sharedInstance
{
    return sharedInstance;
}
@end

5、加密解密代码

//配置秘钥
    RSAEncryptor* rsaEncryptor = [[RSAEncryptor alloc] init];
    NSString* publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"];
    NSString* privateKeyPath = [[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"];
    [rsaEncryptor loadPublicKeyFromFile: publicKeyPath];
    [rsaEncryptor loadPrivateKeyFromFile: privateKeyPath password:@"www.57.com"];    // 这里,请换成你生成p12时的密码
    //字符串RSA加密
    NSString *string = @"ios RSA加密";
    NSString* restrinBASE64STRING = [rsaEncryptor rsaEncryptString:string];
    NSLog(@"Encrypted==== %@", restrinBASE64STRING);       // 请把这段字符串Copy到JAVA这边main()里做测试
    //字符串RSA解密
    NSString* decryptString = [rsaEncryptor rsaDecryptString: restrinBASE64STRING];
    NSLog(@"Decrypted==== %@", decryptString);


    // System.out.println the encrypt string from Java , and paste it here
    // 这里请换成你的JAVA这边产生的加密的Base64 Encode的字符串
            NSString* rsaEncrypyBase64 = [NSString stringWithFormat:@"%@\r%@\r%@",
                                          @"ZNKCVpFYd4Oi2pecLhDXHh+8kWltUMLdBIBDeTvU5kWpTQ8cA1Y+7wKO3d/M8bhULYf1FhWt80Cg",
                                          @"7e73SV5r+wSlgGWBvTIxqgTWFS4ELGzsEJpVVSlK1oXF0N2mugOURUILjeQrwn1QTcVdXXTMQ0wj",
                                          @"50GNwnHbAwyLvsY5EUY="];

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

推荐阅读更多精彩内容