Certificates & Identifiers & Provisioning Profiles

转自>曾梦想仗剑走天涯 

引言

        关于开发证书配置(Certificates & Identifiers & Provisioning Profiles)相信做iOS开发的同学没少被折腾。对于一个iOS开发小白、半吊子(比如像我自己)抑或老兵或多或少会有或曾有过以下不详、疑问、疑惑甚至困惑:

  1. 什么是App ID?Explicit/Wildcard App ID有何区别?什么是App Group ID?
  2. 什么是证书(Certificate)?如何申请?有啥用?
  3. 什么是Key Pair(公钥/私钥)?有啥用?与证书有何关联?
  4. 什么是签名(Signature)?如何签名(CodeSign)?怎样校验(Verify)?
  5. 什么是(Team)Provisioning Profiles?有啥用?
  6. Xcode如何配置才能使用iOS真机进行开发调试?
  7. 多台机器如何共享开发者账号或证书?
  8. 遇到证书配置问题怎么办?

本文将围绕相关概念做个系统的梳理串烧。

写在前面

1.假设你使用过Apple设备(iMac/iPad/iPhone)且注册过Apple ID(Apple Account)。

2.假设你或你所在的开发组已加入苹果开发者计划(Enroll in iOS Developer Program to become a member)即已注册开发者账号(Apple Developer Account)。

  • 只有拥有开发者账号才可以申请开发/发布证书及相关配置授权文件进而在iOS真机上开发调试Apps或发布到App Store。
  • 开发者账号分为Individual和Company/Organization两种类型。如无特别交代下文基于$99/Year的普通个人开发者(Individual)账号展开。

3.若要真机调试实践你必须至少拥有一台装有Mac OS X/Xcode的Mac开发机(iMac or MacBook)其上自带原生的Keychain Access。

.App ID(bundle identifier)

App ID即Product ID用于标识一个或者一组App。

App ID应该和Xcode中的Bundle Identifier是一致(Explicit)的或匹配(Wildcard)的。

App ID字符串通常以反域名(reverse-domain-name)格式的Company Identifier(Company ID)作为前缀(Prefix/Seed)一般不超过255个ASCII字符。

App ID全名会被追加Application Identifier Prefix(一般为TeamID.分为两类:

  • Explicit App ID:唯一的App ID用于唯一标识一个应用程序。例如“com.apple.garageband”这个App ID用于标识Bundle Identifier为“com.apple.garageband”的App。
  • Wildcard App ID:含有通配符的App ID用于标识一组应用程序。例如“*”(实际上是Application Identifier Prefix)表示所有应用程序;而“com.apple.*”可以表示Bundle Identifier以“com.apple.”开头(苹果公司)的所有应用程序。
用户可在网站上删除(Delete)已注册的App IDs。
App ID被配置到【XcodeTarget|Info|Bundle Identifier】下;对于Wildcard App ID只要bundle identifier包含其作为Prefix/Seed即可。

.设备(Device)

Device就是运行iOS系统用于开发调试App的设备。每台Apple设备使用UDID来唯一标识。

iOS设备连接Mac后可通过iTunes->Summary或者Xcode->Window->Devices获取iPhone的UDID(identifier)。

Apple Member Center网站个人账号下的Devices中包含了注册过的所有可用于开发和测试的设备普通个人开发账号每年累计最多只能注册100个设备。

  • Apps signed by you or your team run only on designated development devices.
  • Apps run only on the test devices you specify.
用户可在网站上启用/禁用(Enable/Disable)已注册的Device。
本文的Devices即连接到Xcode被授权用于开发测试的iOS设备(iPhone/iPad)。

.开发证书(Certificates)

1.证书的概念

证书是由公证处或认证机关开具的证明资格或权力的证件它是表明(或帮助断定)事理的一个凭证

每个中国人一生可能需要70多个证件含15种身份证明。证件中“必需的”有30到40个。将这些证件按时间顺序铺开那就是一个天朝子民的一生——持准生证许可落地以户籍证明入籍以身份证认证身份持结婚证以合法同居最终以死亡证明注销。

2.数字证书的概念

数字证书就是互联网通讯中标志通讯各方身份信息的一串数字提供了一种在Internet上验证通信实体身份的方式其作用类似于司机的驾驶执照或日常生活中的身份证。它是由一个由权威机构——CA机构又称为证书授权(Certificate Authority)中心发行的人们可以在网上用它来识别对方的身份。
  • 数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名。
  • 数字证书还有一个重要的特征就是时效性:只在特定的时间段内有效。

某一认证领域内的根证书是CA认证中心给自己颁发的证书信任链的起始点。安装根证书意味着对这个CA认证中心的信任。

在天朝子民的一生中户籍证明可理解为等效的根证书:有了户籍证明才能办理身份证;有了上流的身份证才能办理下游居住证、结婚证、计划生育证、驾驶执照等认证。

3.iOS(开发)证书

iOS证书是用来证明iOS App内容(executable code)的合法性和完整性的数字证书。对于想安装到真机或发布到AppStore的应用程序(App)只有经过签名验证(Signature Validated)才能确保来源可信并且保证App内容是完整、未经篡改的。

iOS证书分为两类:Development和Production(Distribution)。

  • Development证书用来开发和调试应用程序:A development certificate identifies you, as a team member, in a development provisioning profile that allows apps signed by you to launch on devices. 
  • Production主要用来分发应用程序(根据证书种类有不同作用):A distribution certificate identifies your team or organization in a distribution provisioning profile and allows you to submit  your app to the store. Only a team agent or an admin can create a distribution certificate.

普通个人开发账号最多可注册iOS Development/Distribution证书各2个用户可在网站上删除(Revoke)已注册的Certificate。下文主要针对开发调试阶段的Development证书。

下文主要针对iOS App开发调试过程中的开发证书Certificate for Development)。

4.iOS(开发)证书的根证书

那么iOS开发证书是谁颁发的呢?或者说我们是从哪个CA申请到用于Xcode开发调试的证书呢?

iOS以及Mac OS X系统(在安装Xcode时)将自动安装AppleWWDRCA.cer这个中间证书(Intermediate Certificates)它实际上就是iOS(开发)证书的证书根证书(Apple Root Certificate)。

AppleWWDRCA(Apple Root CA)类似注册管理户籍的公安机关户政管理机构AppleWWDRCA.cer之于iOS(开发)证书则好比户籍证之于身份证

如果Mac Keychain Access证书助理在申请证书时尚未安装过该证书请先下载安装(Signing requires that you have both the signing identity and the intermediate certificate installed in your keychain)。

5.申请证书(CSR:Certificate Signing Request)

可以在缺少证书时通过Xcode Fix Issue自动请求证书这里通过Keychain证书助理从证书颁发机构请求证书:填写开发账号邮件和常用名称勾选【存储到磁盘】。

keychain将生成一个包含开发者身份信息的CSRCertificate Signing Request)文件;同时Keychain Access|Keys中将新增一对Public/Private Key Pair(This signing identity consists of a public-private key pair that Apple issues)。

private key始终保存在Mac OS的Keychain Access中用于签名(CodeSign)对外发布的App;public key一般随证书(随Provisioning Profile随App)散布出去对App签名进行校验认证。用户必须保护好本地Keychain中的private key以防伪冒。

  • Keep a secure backup of your public-private key pair. If the private key is lost, you’ll have to create an entirely new identity to sign code. 
  • Worse, if someone else has your private key, that person may be able to impersonate you.

在Apple开发网站上传该CSR文件来添加证书(Upload CSR file to generate your certificate):

Apple证书颁发机构WWDRCA(Apple Worldwide Developer Relations Certification Authority)将使用private key对CSR中的public key和一些身份信息进行加密签名生成数字证书(ios_development.cer)并记录在案(Apple Member Center)。

从Apple Member Center网站下载证书到Mac上双击即可安装(当然也可在Xcode中添加开发账号自动同步证书和[生成]配置文件)。证书安装成功后在KeychainAccess|Keys中展开创建CSR时生成的Key Pair中的私钥前面的箭头可以查看到包含其对应公钥的证书(Your requested certificate will be the public half of the key pair.);在Keychain Access|Certificates中展开安装的证书(ios_development.cer)前面的箭头可以看到其对应的私钥。

Certificate被配置到【Xcode Target|Build Settings|Code Signing|Code Signing Identity】下下拉选择Identities from Profile "..."(一般先配置Provisioning Profile)。以下是Xcode配置示例:

.供应配置文件(Provisioning Profiles

1.Provisioning Profile的概念

Provisioning Profile文件包含了上述的所有内容:证书、App ID和设备

一个Provisioning Profile对应一个Explicit App ID或Wildcard App ID(一组相同Prefix/Seed的App IDs)。在网站上手动创建一个Provisioning Profile时需要依次指定App ID(单选)、证书(Certificates可多选)和设备(Devices可多选)。用户可在网站上删除(Delete)已注册的Provisioning Profiles。

Provisioning Profile决定Xcode用哪个证书(公钥)/私钥组合(Key Pair/Signing Identity)来签署应用程序(Signing Product),将在应用程序打包时嵌入到.ipa包里。安装应用程序时Provisioning Profile文件被拷贝到iOS设备中运行该iOS App的设备也通过它来认证安装的程序。

如果要打包或者在真机上运行一个APP一般要经历以下三步:

  • 首先需要证书对应的私钥来进行签名用于标识这个APP是合法、安全、完整的;
  • 其次需要指明它的App ID并且验证Bundle ID是否与其一致;
  • 然后如果是真机调试需要确认这台设备是否授权运行该APP。

Provisioning Profile把这些信息全部打包在一起方便我们在调试和发布程序打包时使用。这样只要在不同的情况下选择不同的Provisioning Profile文件就可以了。

Provisioning Profile也分为Development和Distribution两类有效期同Certificate一样。Distribution版本的ProvisioningProfile主要用于提交App Store审核其中不指定开发测试的Devices(0unlimited)。App ID为Wildcard App ID(*)。App Store审核通过上架后允许所有iOS设备(Deployment Target)上安装运行该App。

Xcode将全部供应配置文件(包括用户手动下载安装的和Xcode自动创建的Team Provisioning Profile)放在目录~/Library/MobileDevice/Provisioning Profiles下。

2.Provisioning Profile的构成

以下为典型供应配置文件*.mobileprovision的构成简析

(1)Name:该mobileprovision的文件名。

(2)UUID:该mobileprovision文件的真实文件名。

(3)TeamName:Apple ID账号名。

(4)TeamIdentifier:Team Identity。

(5)AppIDName:explicit/wildcard App ID name(ApplicationIdentifierPrefix)。

(6)ApplicationIdentifierPrefix:完整App ID的前缀(TeamIdentifier.*)。

(7)DeveloperCertificates:包含了可以为使用该配置文件应用签名的所有证书<data><array>。

证书是基于Base64编码符合PEM(PrivacyEnhanced Mail, RFC 1848)格式的可使用OpenSSL来处理(opensslx509 -text -in file.pem)。

从DeveloperCertificates提取<data></data>之间的内容到文件cert.cer(cert.perm):

-----BEGIN CERTIFICATE-----

将<data></data>之间的内容拷贝至此

-----END CERTIFICATE-----`

Mac下右键QuickLook查看cert.cer(cert.perm)在Keychain Access中右键Get Info查看对应证书ios_development.cer正常情况(公私钥KeyPair配对)应吻合;Windows下没有足够信息(WWDRCA.cer)无法验证该证书。

如果你用了一个不在这个列表中的证书进行签名无论这个证书是否有效这个应用都将CodeSign Fail。

(8)Entitlements键<key>对应的<dict>:

keychain-access-groups:$(AppIdentifierPrefix)参见Code Signing Entitlements(*.entitlements)。

每个应用程序都有一个可以用于安全保存一些如密码、认证等信息的keychain一般而言自己的程序只能访问自己的keychain。通过对应用签名时的一些设置还可以利用keychain的方式实现同一开发者签证(就是相同bundle seed)下的不同应用之间共享信息的操作。比如你有一个开发者帐户并开发了两个不同的应用A和B然后通过对A和B的keychain access group这个东西指定共用的访问分组就可以实现共享此keychain中的内容。

application-identifier:带前缀的全名例如$(AppIdentifierPrefix)com.apple.garageband。

com.apple.security.application-groups:App Group ID(group. com.apple)参见Code Signing Entitlements(*.entitlements)。

com.apple.developer.team-identifier:同Team Identifier。

(9)ProvisionedDevices:该mobileprovision授权的开发设备的UDID <array>。

Provisioning Profile被配置到【XcodeTarget|Build Settings|Code Signing|Provisioning Profile】下然后在Code Signing Identity下拉可选择Identities from Profile "..."(即Provisioning Profile中包含的Certificates)。

.开发组供应配置文件(Team Provisioning Profiles

1.Team Provisioning Profile的概念

每个Apple开发者账号都对应一个唯一的Team IDXcode3.2.3预发布版本中加入了Team Provisioning Profile这项新功能。

在Xcode中添加Apple Developer Account时它将与Apple Member Center后台勾兑自动生成iOS Team Provisioning Profile(Managed by Xcode)。

Team Provisioning Profile包含一个为Xcode iOS Wildcard App ID(*)生成的iOS Team Provisioning Profile:*(匹配所有应用程序)账户里所有的Development Certificates和Devices都可以使用它在这个eam注册的所有设备上调试所有的应用程序(不管bundleidentifier是什么)。同时它还会为开发者自己创建的Wildcard/Explicit App IDs创建对应的iOS Team Provisioning Profile。

2.Team Provisioning Profile生成/更新时机

  • Add an Apple ID account to Xcode
  • Fix issue "No Provisioning Profiles with a valid signing identity" in Xcode
  • Assign Your App to a Team in Xcode project settings of General|Identity
  • Register new device on the apple development website or Xcode detected new device connected

利用Xcode生成和管理的iOS Team Provisioning Profile来进行开发非常方便可以不需要上网站手动生成下载Provisioning Profile。

Team Provisioning Profile同Provisioning Profile只不过是由Xcode自动生成的被配置到【XcodeTarget|Build Settings|Code Signing|Provisioning Profile】下。

.App Group (ID)

1.App Group的概念

WWDC14除了发布了OS X v10.10和switf外iOS 8.0也开始变得更加开放了。说到开放当然要数应用扩展(App Extension)了。顾名思义应用扩展允许开发者扩展应用的自定义功能和内容能够让用户在使用其他应用程序时使用该项功能从而实现各个应用程序间的功能和资源共享。可以将扩展理解为一个轻量级(nimble and lightweight)的分身。

扩展和其Containing App各自拥有自己的沙盒虽然扩展以插件形式内嵌在Containing App中但是它们是独立的二进制包不可以互访彼此的沙盒。为了实现Containing App与扩展的数据共享苹果在iOS 8中引入了一个新的概念——App Group它主要用于同一Group下的APP实现数据共享具体来说是通过以App Group ID标识的共享资源区——App Group Container。

App Group ID同App ID一样一般不超过255个ASCII字符。用户可在网站上编辑Explicit App IDs的App Group Assignment;可以删除(Delete)已注册的AppGroup (ID)。

2.App Group的配置

Containing App与Extension的Explicit App ID必须Assign到同一App Group下才能实现数据共享并且Containing App与Extension的App ID命名必须符合规范:

  1. 置于同一App Group下的App IDs必须是唯一的(Explicitnot Wildcard)
  2. Extension App ID以Containing App ID为Prefix/Seed

假如Garageband这个App ID为“com.apple.garageband”则支持从语音备忘录导入到Garageband应用的插件的App ID可能形如“com.apple.garageband.extImportRecording”。

 

App(ex)

 

App Group ID

Provisioning Profile

Code Signing Identity

Certificate Key Pair)

App ID

(bundle identifier)

Devices

(test)

GarageBand

置于同一分组:

group.com.apple

(1)共用同一证书:ios_development.cer

(2)共用证书Key Pair中的Private Key进行CodeSign

com.apple.garageband

授权开发测试设备的UDIDs

GarageBand扩展插件

com.apple.garageband.extImportRecording

关于Provisioning Profile可以使用自己手动生成的也可以使用Xcode自动生成的Team Provisioning Profile。

App Group会被配置到【Xcode Target|Build Settings|Code Signing|Code Signing Entitlements】文件(*.entitlements)的键com.apple.security.application-groups下不影响Provisioning Profile生成流程。

.证书与签名(Certificate& Signature)

1.Code Signing Identity

2.Code Signing

每个证书(其实是公钥)对应Key Pair中的私钥会被用来对内容(executable coderesources such as images and nib files aren’t signed)进行数字签名(CodeSign)——使用哈希算法生成内容摘要(digest)。上面已经提到公钥被包含在数字证书里数字证书又被包含在描述文件(Provisioning File)中描述文件在应用被安装的时候会被拷贝到iOS设备中。

Xcode使用指定证书配套的私钥进行签名时需要授权选择【始终允许】后以后使用该私钥进行签名便不会再弹出授权确认窗口。

3.Verify Code Signature with Certificate

iOS/Mac机上的ios_development.cer可以被AppleWWDRCA.cer中的 public key解密从而获取每个开发证书中可信任的公钥。

(1).iOS/Mac设备(系统)使用CA证书(WWDRCA.cer)来判断App Provisioning Profile(Code Signing Identity)中的开发证书的合法性:

  • 若用WWDRCA公钥能成功解密出证书并得到公钥(Public Key)和内容摘要(Signature)证明此证书确乃AppleWWDRCA发布即证书来源可信;
  • 再对证书本身使用哈希算法计算摘要若与上一步得到的摘要一致则证明此证书未被篡改过即证书完整。

(2).iOS/Mac设备(系统)使用App Provisioning ProfileCode Signing Identity)中的开发证书来判断App的合法性:

  • 若用证书公钥能成功解密出App(executable code)的内容摘要(Signature)证明此App确乃认证开发者发布即来源可信;
  • 再对App(executable code)本身使用哈希算法计算摘要若与上一步得到的摘要一致则证明此App(executable code)未被篡改过即内容完整。

.在多台机器上共享开发账户/证书

1.Xcode导出开发者账号(*.developerprofile)或PKCS12文件(*.p12)

进入Xcode Preferences|Accounts:

  • 选中Apple IDs列表中对应Account的的Email点击+-之后的☸|Export Accounts可导出包含account/code signing identity/provisioning profiles信息的*.developerprofile(Exporting a Developer Profile)文件供其他机器上的Xcode开发使用(Import该Account)。
选中右下列表中某行Account Name条目|ViewDetails可以查看Signing Identities和Provisioning Profiles。
  • 选中欲导出的Signing Identity条目点击栏底+之后的☸|Export必须输入密码并需授权export key "privateKey" from keychain将导出Certificates.p12
点击左下角的刷新按钮可从Member Center同步该账号下所有的Provisioning Profile到本地。
选中右击列表中某个Provisioning Profile可以【Show in Finder】到[~/Library/MobileDevice/Provisioning\ Profiles]目录其中Provisioning Profile的真实名称为$(UUID).mobileprovision名如"2488109f-ff65-442e-9774-fd50bd6bc827.mobileprovision"其中<key>Name</key>中为Xcode中看到的描述性别名。

2.Keychain Access导出PKCS12文件(*.p12)

在Keychain Access|Certificates中选中欲导出的certificate或其下private key右键Export或者通过菜单File|Export Items导出Certificates.p12——PKCS12 file holds the private key and certificate

其他Mac机器上双击Certificates.p12(如有密码需输入密码)即可安装该共享证书。有了共享证书之后在开发者网站上将欲调试的iOS设备注册到该开发者账号名下并下载对应证书授权了iOS调试设备的Provisioning Profile文件方可在iOS真机设备上开发调试。

.证书配置常见错误

1.no such provisioning profile was found

Xcode Target|Genera|Identity Team下提示"Your build settings specify a provisioning profile with the UUID "xxx",howerver, no such provisioning profile was found."

Xcode Target|BuildSettings|Code Signing|当前配置的指定UDID的provisioning profile在本地不存在此时需要更改Provisioning Profile。必要时手动去网站下载或重新生成Provisioning Profile或直接在Xcode中Fix issue予以解决(可能自动生成iOS Team ProvisioningProfile)!

2.No identities from profile

Build Settings|CodeSigning的Provisioning Profile中选择了本地安装的provisioning profile之后Code Signing Identity中下拉提示No identities from profile “…”or No identities from keychain.

Xcode配置指定UDID的provisioning profile中的DeveloperCertificates在本地KeyChain中不存在(No identities are available)或不一致(KeyPair中的Private Key丢失)此时需去网站检查ProvisioningProfile中的App ID-Certificate-Device配置是否正确。如果是别人提供的共享账号(*.developerprofile)或共享证书(*.p12)请确保导出了对应Key Pair中的Private Key。必要时也直接在Xcode中Fix issue予以解决(可能自动生成iOS Team ProvisioningProfile)。

3.Code Signing Entitlements file do not match profile

"Invalid application-identifier Entitlement" or "Code Signing Entitlements file do not match those specified in your provisioning profile.(0xE8008016)."

(1)检查对应版本(Debug)指定的*.entitlements文件中的“Keychain Access Groups”键值是否与ProvisioningProfile中的Entitlements项相吻合(后者一般为前者的Prefix/Seed)。

(2)也可以将Build Settings|Code Signing的Provisioning Profile中对应版本(Debug)的Entitlements置空。

4.Xcode配置反应有时候不那么及时可刷新、重置相关配置项开关(若有)或重启Xcode试试。


参考:

iPhone真机调试应用程序》《iOS Developer:真机测试

iOS Development--Certificates, Provisioning Profiles

关于Certificate、Provisioning Profile、App ID的介绍及其关系


数字签名和数字证书》《iOS keyChain 研究

苹果开发者账号那些事儿》《iOS关於Provisioning Profiles这些事

iOS Code Signing 学习笔记》《代码签名探析/Inside Code Signing

iOS Code Signing: 解惑/iOS Code Signing: Under The Hood

iOS发布遇到的一些问题

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

推荐阅读更多精彩内容