Bitcoin私鑰、公鑰择浊、地址概念以及Java實(shí)現(xiàn)


??? 這幾個(gè)月一直在忙自己的終身大事戴卜,好久不寫文章了;靜下來(lái)分析寫作中斷的原因琢岩,發(fā)現(xiàn)一半是工作太忙投剥,一半是自己太懶,堅(jiān)持很多年做一件事情真得挺難的担孔。從今天開始江锨,我要拾起寫作計(jì)劃,寫作可以梳理自己的知識(shí)框架糕篇,同時(shí)分享給大家一起學(xué)習(xí)啄育,歡迎大家提出建議。

??? 之前的寫作內(nèi)容都是區(qū)塊鏈入門級(jí)別的拌消,出于個(gè)人愛好挑豌,主要圍繞ethereum展開,但是工作中基本不涉及ethereum的內(nèi)容墩崩,一直沒有深入下去氓英;由于工作圍繞BitCoin展開,以后的文章我先轉(zhuǎn)移到BTC領(lǐng)域鹦筹。

??? 今天先從BTC的基本概念入手铝阐,整理一下私鑰、公鑰铐拐、地址的概念饰迹,這些概念在網(wǎng)絡(luò)上已經(jīng)泛濫很久了,實(shí)在是太枯燥余舶,還是自己coding來(lái)得爽啊鸭,今天就和大家分享如何用Java生成BTC私鑰、公鑰和地址匿值。理論方面先分享幾個(gè)鏈接:

??? https://en.bitcoin.it/wiki/Private_key

??? 比特幣密鑰生成規(guī)則及 Go 實(shí)現(xiàn) (特別推薦赠制,這篇文章質(zhì)量很高)

??? 這里強(qiáng)調(diào)一下理論的重要性,只有徹底理解了BTC私鑰、公鑰钟些、地址的關(guān)系烟号,才能coding出來(lái),所有的代碼只是思想的體現(xiàn)政恍。

Java 代碼實(shí)現(xiàn)

?代碼部分只貼出了核心片段汪拥,稍微加工一下即可運(yùn)行。

// 主要使用了java lang 有關(guān)橢圓曲線算法的package, bouncycastle lib(bcprov-jdk15on-160.jar)以及bitcoinj的Base58類

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.spec.ECGenParameterSpec;

import java.security.spec.ECPoint;

import java.security.PublicKey;

import java.security.PrivateKey;

import java.security.interfaces.ECPrivateKey;

import java.security.interfaces.ECPublicKey;

import java.security.InvalidAlgorithmParameterException;

import java.security.NoSuchAlgorithmException;

import java.security.MessageDigest;

import java.security.NoSuchProviderException;

import java.security.Security;

import java.io.UnsupportedEncodingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bitcoinj.core.Base58;

// 定義Key類篙耗,用來(lái)封裝公私鑰對(duì)和地址

class Key {

? ? private String privkey;

? ? private String pubkey;

? ? private String address;

? ? public Key() {

? ? ? ? Reset();

? ? }

? ? public Key(String privkey, String pubkey, String address) {

? ? ? ? this.privkey = privkey;

? ? ? ? this.pubkey? = pubkey;

? ? ? ? this.address = address;

? ? }

? ? public void Reset() {

? ? ? ? this.privkey = null;

? ? ? ? this.pubkey? = null;

? ? ? ? this.address = null;

? ? }

? ? public void SetPrivKey(String privkey) {

? ? ? ? this.privkey = privkey;

? ? }

? ? public void SetPubKey(String pubkey) {

? ? ? ? this.pubkey = pubkey;

? ? }

? ? public void SetAddress(String address) {

? ? ? ? this.address = address;

? ? }

? ? public String ToString() {

? ? ? ? return "{\n"

? ? ? ? ? ? + "\t privkey:" + this.privkey + "\n"

? ? ? ? ? ? + "\t pubkey :" + this.pubkey? + "\n"

? ? ? ? ? ? + "\t address:" + this.address + "\n"

? ? ? ? ? ? + "}\n";

? ? }

}

public class KeyGenerator {

?? // Base58 encode prefix迫筑,不同的prefix可以定制地址的首字母

? ? static final byte PubKeyPrefix = 65;

? ? static final byte PrivKeyPrefix = -128;

? ? static final String PrivKeyPrefixStr = "80";

? ? static final byte PrivKeySuffix = 0x01;

? ? static int keyGeneratedCount = 1;

? ? static boolean debug = true;

? ? static KeyPairGenerator sKeyGen;

? ? static ECGenParameterSpec sEcSpec;

? ? static {

? ? ? ? ? ? Security.addProvider(new BouncyCastleProvider());

? ? }

? ? private static boolean ParseArguments(String []argv) {

? ? ? ? for (int i = 0; i < argv.length - 1; i++) {

? ? ? ? ? ? if ("-n".equals(argv[i])) {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? keyGeneratedCount = Integer.parseInt(argv[i + 1]);

? ? ? ? ? ? ? ? ? ? i = i + 1;

? ? ? ? ? ? ? ? ? ? continue;

? ? ? ? ? ? ? ? } catch (NumberFormatException e) {

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? ? ? return false;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } else if ("-debug".equals(argv[i])) {

? ? ? ? ? ? ? ? debug = true;

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? System.out.println(argv[i] + " not supported...");

? ? ? ? ? ? ? ? return false;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return keyGeneratedCount > 0;

? ? }

? ? public static void main(String args[]) {

? ? ? ? if (args.length > 1) {

? ? ? ? ? if (!ParseArguments(args)) {

? ? ? ? ? ? ? System.out.println("Arguments error, please check...");

? ? ? ? ? ? ? System.exit(-1);

? ? ? ? ? }

? ? ? ? }

? ? ? ? Key key = new Key();

? ? ? ? key.Reset();

? ? ? ? KeyGenerator generator = new KeyGenerator();

? ? ? ? for (int i = 0; i < keyGeneratedCount; i++) {

? ? ? ? ? ? key.Reset();

? ? ? ? ? ? if (generator.GenerateKey(key)) {

? ? ? ? ? ? ? ? System.out.println(key.ToString());

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? System.out.println("Generate key error...");

? ? ? ? ? ? ? ? System.exit(-1);

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? public KeyGenerator() {

? ? ? ? Init();

? ? }

? ? private void Init() {

? ? ? ? // Initialize key generator

? ? ? ? // The specific elliptic curve used is the secp256k1.

? ? ? ? try {

? ? ? ? ? ? sKeyGen = KeyPairGenerator.getInstance("EC");

? ? ? ? ? ? sEcSpec = new ECGenParameterSpec("secp256k1");

? ? ? ? ? ? if (sKeyGen == null) {

? ? ? ? ? ? ? ? System.out.println("Error: no ec algorithm");

? ? ? ? ? ? ? ? System.exit(-1);

? ? ? ? ? ? }

? ? ? ? ? ? sKeyGen.initialize(sEcSpec); // 采用secp256K1標(biāo)準(zhǔn)的橢圓曲線加密算法

? ? ? ? } catch (InvalidAlgorithmParameterException e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? System.exit(-1);

? ? ? ? } catch (NoSuchAlgorithmException e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? System.exit(-1);

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? System.exit(-1);

? ? ? ? }

? ? }

? ? public boolean GenerateKey(Key key) {

? ? ? ? key.Reset();

? ? ? ? // Generate key pair,依據(jù)橢圓曲線算法產(chǎn)生公私鑰對(duì)

? ? ? ? KeyPair kp = sKeyGen.generateKeyPair();

? ? ? ? PublicKey pub = kp.getPublic();

? ? ? ? PrivateKey pvt = kp.getPrivate();

? ? ? ? ECPrivateKey epvt = (ECPrivateKey)pvt;

? ? ? ? String sepvt = Utils.AdjustTo64(epvt.getS().toString(16)).toUpperCase(); // 私鑰16進(jìn)制字符串

? ? ? ? if (debug) {

? ? ? ? ? ? System.out.println("Privkey[" + sepvt.length() + "]: " + sepvt);

? ? ? ? }

????? // 獲取X宗弯,Y坐標(biāo)點(diǎn)脯燃,“04” + sx + sy即可獲得完整的公鑰,但是這里我們需要壓縮的公鑰

? ? ? ? ECPublicKey epub = (ECPublicKey)pub;

? ? ? ? ECPoint pt = epub.getW();

? ? ? ? String sx = Utils.AdjustTo64(pt.getAffineX().toString(16)).toUpperCase();

? ? ? ? String sy = Utils.AdjustTo64(pt.getAffineY().toString(16)).toUpperCase();

? ? ? ? String bcPub = "04" + sx + sy;

? ? ? ? if (debug) {

? ? ? ? ? ? System.out.println("Pubkey[" + bcPub.length() + "]: " + bcPub);

? ? ? ? }

? ? ? ? // Here we get compressed pubkey

?????? // 獲取壓縮公鑰的方法:Y坐標(biāo)最后一個(gè)字節(jié)是偶數(shù)蒙保,則 "02" + sx辕棚,否則 "03" + sx

? ? ? ? byte[] by = Utils.HexStringToByteArray(sy);

? ? ? ? byte lastByte = by[by.length - 1];

? ? ? ? String compressedPk;

? ? ? ? if ((int)(lastByte) % 2 == 0) {

? ? ? ? ? ? compressedPk = "02" + sx;

? ? ? ? } else {

? ? ? ? ? ? compressedPk = "03" + sx;

? ? ? ? }

? ? ? ? if (debug) {

? ? ? ? ? ? System.out.println("compressed pubkey: " + compressedPk);

? ? ? ? }

? ? ? ? key.SetPubKey(compressedPk);

? ? ? ? // We now need to perform a SHA-256 digest on the public key,

? ? ? ? // followed by a RIPEMD-160 digest.

?????? // 對(duì)壓縮的公鑰做SHA256摘要

? ? ? ? byte[] s1 = null;

? ? ? ? MessageDigest sha = null;

? ? ? ? try {

? ? ? ? ? ? sha = MessageDigest.getInstance("SHA-256");

? ? ? ? ? ? s1 = sha.digest(Utils.HexStringToByteArray(compressedPk));

? ? ? ? ? ? if (debug) {

? ? ? ? ? ? ? ? System.out.println("sha: " + Utils.BytesToHex(s1).toUpperCase());

? ? ? ? ? ? }

? ? ? ? } catch (NoSuchAlgorithmException e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? return false;

? ? ? ? }

? ? ? ? // We use the Bouncy Castle provider for performing the RIPEMD-160 digest

? ? ? ? // since JCE does not implement this algorithm.

??????? // SHA256摘要之后做RIPEMD-160,這里調(diào)用Bouncy Castle的庫(kù)邓厕,不知道的同學(xué)百度搜一下就懂了

? ? ? ? byte[] r1 = null;

? ? ? ? byte[] r2 = null;

? ? ? ? try {

? ? ? ? ? ? MessageDigest rmd = MessageDigest.getInstance("RipeMD160", "BC");

? ? ? ? ? ? if (rmd == null || s1 == null) {

? ? ? ? ? ? ? ? System.out.println("can't get ripemd160 or sha result is null");

? ? ? ? ? ? ? ? return false;

? ? ? ? ? ? }

? ? ? ? ? ? r1 = rmd.digest(s1);

? ? ? ? ? ? r2 = new byte[r1.length + 1];

? ? ? ? ? ? r2[0] = PubKeyPrefix; // RipeMD160 摘要之后加上公鑰前綴

? ? ? ? ? ? for (int i = 0; i < r1.length; i++)

? ? ? ? ? ? ? ? r2[i + 1] = r1[i]; // 寫的有點(diǎn)low逝嚎,大家采用System.arraycopy自行修改吧

? ? ? ? ? ? if (debug) {

? ? ? ? ? ? ? ? System.out.println("rmd: " + Utils.BytesToHex(r2).toUpperCase());

? ? ? ? ? ? }

? ? ? ? } catch (NoSuchAlgorithmException e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? return false;

? ? ? ? } catch (NoSuchProviderException e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? return false;

? ? ? ? }

? ? ? ? byte[] s2 = null; // 加上前綴之后做兩次SHA256

? ? ? ? if (sha != null && r2 != null) {

? ? ? ? ? ? sha.reset();

? ? ? ? ? ? s2 = sha.digest(r2);

? ? ? ? ? ? if (debug) {

? ? ? ? ? ? ? ? System.out.println("sha: " + Utils.BytesToHex(s2).toUpperCase());

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? System.out.println("cant't do sha-256 after ripemd160");

? ? ? ? ? ? return false;

? ? ? ? }

? ? ? ? byte[] s3 = null;

? ? ? ? if (sha != null && s2 != null) {

? ? ? ? ? ? sha.reset();

? ? ? ? ? ? s3 = sha.digest(s2);

? ? ? ? ? ? if (debug) {

? ? ? ? ? ? ? ? System.out.println("sha: " + Utils.BytesToHex(s3).toUpperCase());

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? System.out.println("cant't do sha-256 after sha-256");

? ? ? ? ? ? return false;

? ? ? ? }

??????? // 讀懂下面內(nèi)容,大家仔細(xì)閱讀比特幣密鑰生成規(guī)則及 Go 實(shí)現(xiàn)

? ? ? ? byte[] a1 = new byte[r2.length + 4];

? ? ? ? for (int i = 0 ; i < r2.length ; i++) a1[i] = r2[i];

? ? ? ? for (int i = 0 ; i < 4 ; i++) a1[r2.length + i] = s3[i];

? ? ? ? if (debug) {

? ? ? ? ? ? System.out.println("before base58: " + Utils.BytesToHex(a1).toUpperCase());

? ? ? ? }

? ? ? ? key.SetAddress(Base58.encode(a1)); // 到此详恼,可以獲取WIF格式的地址

? ? ? ? if (debug) {

? ? ? ? ? ? System.out.println("addr: " + Base58.encode(a1));

? ? ? ? }

? ? ? ? // Lastly, we get compressed privkey 最后獲取壓縮的私鑰

? ? ? ? byte[] pkBytes = null;

? ? ? ? pkBytes = Utils.HexStringToByteArray("80" + sepvt + "01");//sepvt.getBytes("UTF-8");

? ? ? ? if (debug) {

? ? ? ? ? ? ? ? System.out.println("raw compressed privkey: " + Utils.BytesToHex(pkBytes).toUpperCase());

? ? ? ? ? ? }

? ? ? ? try {

? ? ? ? ? ? sha = MessageDigest.getInstance("SHA-256");

? ? ? ? } catch (NoSuchAlgorithmException e) {

? ? ? ? ? ? System.out.println("Error:" + e);

? ? ? ? ? ? return false;

? ? ? ? }

? ? ? ? sha.reset();

? ? ? ? byte[] shafirst? = sha.digest(pkBytes);

? ? ? ? sha.reset();

? ? ? ? byte[] shasecond = sha.digest(shafirst);

? ? ? ? byte[] compressedPrivKey = new byte[pkBytes.length + 4];

? ? ? ? for (int i = 0; i < pkBytes.length; i++) {

? ? ? ? ? ? compressedPrivKey[i] = pkBytes[i];

? ? ? ? }

? ? ? ? for (int j = 0; j < 4; j++) {

? ? ? ? ? ? compressedPrivKey[j + pkBytes.length] = shasecond[j];

? ? ? ? }

? ? ? ? //compressedPrivKey[compressedPrivKey.length - 1] = PrivKeySuffix;

? ? ? ? key.SetPrivKey(Base58.encode(compressedPrivKey));

? ? ? ? if (debug) {

? ? ? ? ? ? System.out.println("compressed private key: " + Base58.encode(compressedPrivKey));

? ? ? ? }

? ? ? ? return true;

? ? }

}

// 附上Utils中的靜態(tài)方法补君,都很簡(jiǎn)單

public class Utils {

? ? public static String AdjustTo64(String s) {

? ? ? ? switch(s.length()) {

? ? ? ? ? ? case 62: return "00" + s;

? ? ? ? ? ? case 63: return "0" + s;

? ? ? ? ? ? case 64: return s;

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? throw new IllegalArgumentException("not a valid key: " + s);

? ? ? ? }

? ? }

? ? public static String BytesToHex(byte[] src) {

? ? ? ? StringBuilder stringBuilder = new StringBuilder("");

? ? ? ? if (src == null || src.length <= 0) {

? ? ? ? ? ? return null;

? ? ? ? }

? ? ? ? for (int i = 0; i < src.length; i++) {

? ? ? ? ? ? int v = src[i] & 0xFF;

? ? ? ? ? ? String hv = Integer.toHexString(v);

? ? ? ? ? ? if (hv.length() < 2) {

? ? ? ? ? ? ? ? stringBuilder.append(0);

? ? ? ? ? ? }

? ? ? ? ? ? stringBuilder.append(hv);

? ? ? ? }

? ? ? ? return stringBuilder.toString();

? ? }

? ? public static byte[] HexStringToByteArray(String s) {

? ? ? ? int len = s.length();

? ? ? ? byte[] data = new byte[len / 2];

? ? ? ? for (int i = 0; i < len; i += 2) {

? ? ? ? ? ? data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)

? ? ? ? ? ? ? ? + Character.digit(s.charAt(i+1), 16));

? ? ? ? }

? ? ? ? return data;

? ? }

}

總結(jié)

??? 如果以上代碼仍有疑問,請(qǐng)仔細(xì)閱讀上面推薦的兩篇文章单雾,請(qǐng)時(shí)刻記鬃:code只是理論的體現(xiàn)。

這里總結(jié)一下BitCoin 生成私鑰硅堆、壓縮格式私鑰屿储、公鑰、壓縮格式公鑰渐逃、WIF錢包地址的過程:

a. secp256K1標(biāo)準(zhǔn)的EC算法生成公私鑰對(duì):(privkey, pubkey)够掠;

b. privkey 生成壓縮格式私鑰:假設(shè)privkey1 = 私鑰前綴0x80+privkey+私鑰后綴x01,

??? result1 = sha256(sha256(privkey1)),對(duì) privkey1做兩次SHA256摘要茄菊, result1前4個(gè)字節(jié)添加到privkey1疯潭, privkey2 = privkey1 + result1[0:3],壓縮私鑰compPrivkey = base58(privkey2)面殖;

c. pubkey 生成完整的公鑰和壓縮格式的公鑰:pubkey對(duì)應(yīng)一個(gè)坐標(biāo)點(diǎn)(X竖哩,Y),由X可以推算出Y脊僚,

??? 0x04 + X + Y就是完整的公鑰相叁;設(shè)Y的最后一個(gè)字節(jié)為b,則:

??? b為偶數(shù),壓縮格式的公鑰compPubkey = 0x02 + x,

??? b為奇數(shù)增淹,壓縮格式的公鑰compPubkey = 0x03 + x椿访。

d. 壓縮的公鑰compPubkey生成WIF格式的地址address:

??? 假設(shè) r1 =? RIPEMD160(SHA256(compPubkey)),壓縮公鑰先做SHA256虑润,在做RIPEMD160摘要;

??? 假設(shè) r2 = PubkeyPrefix(這里為10進(jìn)制65) + r1;

??? 假設(shè) s3 = SHA256(SHA256(r2))成玫,r2兩次SHA256摘要,s3的前4個(gè)字節(jié)為s3[0:3];

??? 假設(shè) a = r2 + s3[0:3]拳喻,WIF address = base58(a)哭当。

??? 以上就是簡(jiǎn)單的總結(jié),比較繁瑣舞蔽,至于為什么這么做荣病,那是bitcoin設(shè)計(jì)師設(shè)計(jì)的码撰,請(qǐng)大家查看官方資料渗柿。

??? 最后留給大家一個(gè)問題:compressed privkey如何得到完整的私鑰匙?脖岛?別看補(bǔ)充內(nèi)容朵栖,自己先想想!

補(bǔ)充(壓縮私鑰匙轉(zhuǎn)為完整私鑰):

神奇的事情是這樣發(fā)生的柴梆,對(duì)compressed privkey做base58 decode陨溅,結(jié)果為38個(gè)字節(jié),結(jié)構(gòu)為:

1字節(jié)前綴(0x80) + 32 字節(jié)私鑰 + 1字節(jié)后綴(0x01) + 4 字節(jié)(這四個(gè)字節(jié)就是上面result1頭4個(gè)字節(jié))绍在。

神奇吧门扇?So amazing! 最后送給小伙伴們壓縮私鑰轉(zhuǎn)換為原始私鑰的code:

public static String convertWIFPrivkeyIntoPrivkey(String wifPrivKey) throws AddressFormatException {

? ? ? ? if (wifPrivKey == null || "".equals(wifPrivKey)) {

? ? ? ? ? ? throw new AddressFormatException("Invalid WIF private key");

? ? ? ? }

? ? ? ? byte[] base58Decode = null;

? ? ? ? try {

? ? ? ? ? ? base58Decode = Base58.decode(wifPrivKey);

? ? ? ? } catch (AddressFormatException e) {

? ? ? ? ? ? throw e;

? ? ? ? }

? ? ? ? String decodeStr = Utils.bytesToHexString(base58Decode);

? ? ? ? if (decodeStr.length() != 76) {

? ? ? ? ? ? throw new AddressFormatException("Invalid WIF private key");

? ? ? ? }

? ? ? ? String version = decodeStr.substring(0, 2);

? ? ? ? String suffix = decodeStr.substring(66, 68);

? ? ? ? if (!"80".equals(version) || !"01".equals(suffix)) {

? ? ? ? ? ? throw new AddressFormatException("Invalid WIF private key");

? ? ? ? }

? ? ? ? String privKeyStr = decodeStr.substring(2, 66);

? ? ? ? return privKeyStr;

? ? }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市偿渡,隨后出現(xiàn)的幾起案子臼寄,更是在濱河造成了極大的恐慌,老刑警劉巖溜宽,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吉拳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡适揉,警方通過查閱死者的電腦和手機(jī)留攒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嫉嘀,“玉大人炼邀,你說我怎么就攤上這事〖粑辏” “怎么了拭宁?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我红淡,道長(zhǎng)不狮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任在旱,我火速辦了婚禮摇零,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桶蝎。我一直安慰自己驻仅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布登渣。 她就那樣靜靜地躺著噪服,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胜茧。 梳的紋絲不亂的頭發(fā)上粘优,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音呻顽,去河邊找鬼雹顺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛廊遍,可吹牛的內(nèi)容都是我干的嬉愧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼喉前,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼没酣!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起卵迂,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤裕便,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后狭握,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闪金,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年论颅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哎垦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恃疯,死狀恐怖漏设,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情今妄,我是刑警寧澤郑口,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布鸳碧,位于F島的核電站,受9級(jí)特大地震影響犬性,放射性物質(zhì)發(fā)生泄漏瞻离。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一乒裆、第九天 我趴在偏房一處隱蔽的房頂上張望套利。 院中可真熱鬧,春花似錦鹤耍、人聲如沸肉迫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)喊衫。三九已至,卻和暖如春杆怕,著一層夾襖步出監(jiān)牢的瞬間族购,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工财著, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留联四,地道東北人撑碴。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓撑教,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親醉拓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子伟姐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法亿卤,內(nèi)部類的語(yǔ)法愤兵,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法排吴,線程的語(yǔ)...
    子非魚_t_閱讀 31,623評(píng)論 18 399
  • 【程序1】 題目:古典問題:有一對(duì)兔子秆乳,從出生后第3個(gè)月起每個(gè)月都生一對(duì)兔子,小兔子長(zhǎng)到第三個(gè)月后每個(gè)月又生一對(duì)兔...
    開心的鑼鼓閱讀 3,318評(píng)論 0 9
  • 前幾天去客戶那里開會(huì),聽對(duì)方講了這樣一件事:他去英國(guó)旅行街氢,在英國(guó)的地鐵上扯键,與一家三口毗鄰而坐,對(duì)面是一個(gè)白發(fā)老婦和...
    周三有理閱讀 405評(píng)論 0 2
  • 如果他也愛你珊肃,他才不會(huì)讓你受盡委屈還求不來(lái)結(jié)局荣刑。 朱小北馅笙,你是豬嗎,他不喜歡你厉亏,池陽(yáng)他不喜歡你董习,你還沒明白嗎,在他...
    木易每文閱讀 394評(píng)論 0 3
  • 已累計(jì)成功幫助4300多學(xué)員 如何讓你的英語(yǔ)水平踏上十倍速以上的成長(zhǎng)之路? 如果你學(xué)了很多年英語(yǔ)還是開口不會(huì)說爱只,聽...
    妮蘭芝閱讀 1,248評(píng)論 0 3