Android端与JavaWeb传输加密(DES+RSA)

一、加密介绍


  本文采用对称式加密算法DES和非对称式加密算法RSA结合做数据传输加密的方式。
  先说一下对称式加密 DES:对称式加密即使用单钥密码加密的方法,信息的加密和解密使用同一个秘钥,这种方式也称为单秘钥加密。所谓对称就是指加密和解密使用的是同一个秘钥!
  常用的对称加密有:DES、IDEA、RC2、RC4、SKIPJACK、RC5、AES算法等。
  与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥 (privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
  RSA 公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。 RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。


二、RSA密钥生成


  RSA密钥采用OpenSSL协议进行生成,本文仅简单生成公钥和私钥,如有其它需要可以通过CA证书进行密钥的生成

1、OpenSSL安装

http://slproweb.com/products/Win32OpenSSL.html
 请自行选择32位64位进行下载安装

2、打开工作空间

  打开OpenSSL安装目录下的bin,运行OpenSSL.exe进入OpenSSL工作空间

3、密钥生成

①、私钥生成(生成位置位于bin目录下)

genrsa -out rsa_private_key.pem 1024

  openssl随机生成了一份私钥,加密长度是1024位。加密长度是指理论上最大允许”被加密的信息“长度的限制,也就是明文的长度限制。随着这个参数的增大(比方说2048),允许的明文长度也会增加,但同时也会造成计算复杂度的极速增长。一般推荐的长度就是1024位(128字节)
JAVA需要使用的私钥需要经过PKCS#8编码,PHP程序不需要
  当前私钥格式需要转换为pkcs#8的格式,命令为:

pkcs8 -topk8 -inform PEM -in pkcs8_rsa_private_key.pem -outform PEM -nocrypt

②、公钥生成

rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout

至此,RSA+DES相关前期准备工作完成



三、Android端配置

  本文主要针对数据传输过程进行加密,采取加密Json字符串完成整个加密过程,由此,需要统一传输参数为"data=********&sign="*******************"的格式,如有其它需求请自行更改。
  由于本项目网络框架采用Retrofit+OkHttp的实现方式,所以对参数进行加密的过程由OkHttp拦截器来实现

public class EncryptionInterceptor implements Interceptor {

private Context mContext;

public EncryptionInterceptor(Context context) {
    this.mContext = context;
}

@Override
public Response intercept(@NonNull Chain chain) throws IOException {
    Request request = chain.request();
    RequestBody oldBody = request.body();
    Buffer buffer = new Buffer();
    if (oldBody != null) {
        oldBody.writeTo(buffer);
    }
    String strOldBody = buffer.readUtf8();
    Map<String, String> map = new HashMap<>();
    String dataByte = URLDecoder.decode(strOldBody.substring(5), "utf-8");
    try {
        //获取DES的key
        byte[] desKey = DESCoder.initKey();
        //DES加密数据
        byte[] encrypt = DESCoder.encrypt(dataByte.getBytes(), desKey);
        map.put("data", parseByte2HexStr(encrypt));
        //RSA加密
        RSAEncrypt rsaEncrypt = new RSAEncrypt();
        InputStream inputStream =     mContext.getResources().getAssets().open("rsa_public_key.pem");
        //rsa设置公钥
        rsaEncrypt.loadPublicKey(inputStream);
        //rsa加密DES的key
        byte[] rsaData = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), desKey);
        map.put("sign", parseByte2HexStr(rsaData));
    } catch (Exception e) {
        e.printStackTrace();
    }

    FormBody body = new FormBody.Builder().add("data", map.get("data")).add("sign", map.get("sign")).build();
    request = request.newBuilder().header("Content-Type", body.contentType().type()).header("Content-Length", String.valueOf(body.contentLength())).method(request.method(), body).build();
    return chain.proceed(request);
}

/**
 * 将二进制转换成16进制
 *
 * @since v1.0
 */
private static String parseByte2HexStr(byte buf[]) {
    StringBuilder sb = new StringBuilder();
    for (byte aBuf : buf) {
        String hex = Integer.toHexString(aBuf & 0xFF);
        if (hex.length() == 1) {
            hex = '0' + hex;
        }
        sb.append(hex.toUpperCase());
    }
    return sb.toString();
}

/**
 * 按照key排序得到参数列表字符串
 *
 * @param paramValues 参数map对象
 * @return 参数列表字符串
 */
public static String getParamsOrderByKey(Map<String, String> paramValues) {
    String params = "";
    List<String> paramNames = new ArrayList<>(paramValues.size());
    paramNames.addAll(paramValues.keySet());
    Collections.sort(paramNames);
    for (String paramName : paramNames) {

        if (params.equals("")) {
            params += paramName + "=" + paramValues.get(paramName);
        } else {
            params += "&" + paramName + "=" + paramValues.get(paramName);
        }
    }

    return params;
}

/**
 * 将16进制转换为二进制
 *
 * @since v1.0
 */
public byte[] parseHexStr2Byte(String hexStr) {
    if (hexStr.length() < 1)
        return null;
    byte[] result = new byte[hexStr.length() / 2];
    for (int i = 0; i < hexStr.length() / 2; i++) {
        int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
        int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
        result[i] = (byte) (high * 16 + low);
    }
    return result;
}

}

添加OkHttp拦截器

new OkHttpClient.Builder()
                .addInterceptor(new EncryptionInterceptor(this))
                .build();

RSA工具类实现

public class RSAEncrypt {
public static final String DEFAULT_PUBLIC_KEY =
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1Qcf1zVOuhseFxvo6+FnVvEPs" + "\r" + "Uvczg6oX+HjMNksiiDWcNkbPHfznaPDtgoBY2xF0R8HGHbrT53LNvkj7UMcI48tq" + "\r" + "K+B4YdJHe9SgJVDCCiceLLGtf/ev206qJ/XgKgrLFD+vMmjIB8gQCkZvy/dxhEf1" + "\r" + "aAmoz5tdJhOVdxT7QwIDAQAB" + "\r";

public static final String DEFAULT_PRIVATE_KEY =
        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALVBx/XNU66Gx4XG" + "\r" +
                "+jr4WdW8Q+xS9zODqhf4eMw2SyKINZw2Rs8d/Odo8O2CgFjbEXRHwcYdutPncs2+" + "\r" +
                "SPtQxwjjy2or4Hhh0kd71KAlUMIKJx4ssa1/96/bTqon9eAqCssUP68yaMgHyBAK" + "\r" +
                "Rm/L93GER/VoCajPm10mE5V3FPtDAgMBAAECgYAf1hEAHHNhSS0MUzmqV+q3ftzT" + "\r" +
                "SnM+6hZbJXpaLAMgapo3+NSRFmxQXP9MSEqw0LGNIfloCdrB03o3pv98nOCIZCh7" + "\r" +
                "PHsU2GhxJ04Qro+wKhK358326KNXCjjqVIBG0xMbJxVhjM2/jjfocxFpe5iD7h53" + "\r" +
                "c+GvDgUVduAYO4I1GQJBAO21n2aIzQV3mScS1O8BRV+9CmHaDbVHqBetRoB3kJ2U" + "\r" +
                "piflKTNofwWmTA5A8sKt8WcOz7LsB2SWcp9jNvatxA8CQQDDNCmfo6eix9e5f11K" + "\r" +
                "Rf8sRiN7XGDzlKkZlmQAN0UtXdTP4AN9cuZrwnntWKysXr/zLntYLGYn9rdrohbD" + "\r" +
                "9RGNAkBOEsog7iuQcSCfQcMoIN29PSSs0OaRtNBTvniadyrLZuhP0CeBGAAoRd9T" + "\r" +
                "CyfwoxrXg3jaRkWDVxqcmQSTbq0nAkB8flcRhilSqsuNdYpE5VFxpiXY9jirAKO8" + "\r" +
                "Our6LEXFQjOIhCEVr+L+1OA4HDa8FA2thXaK7H4WfMXMMmr8fN69AkEAuR0YU9My" + "\r" +
                "snzWLDWYR5sNp90PhyDSL/HTZHBnebD+JlAYwoYRFYt+tXw0/PEmV2B3thYGPeiZ" + "\r" +
                "kHKd/TeLIVbxGg==" + "\r";

/**
 * 私钥
 */
private RSAPrivateKey privateKey;

/**
 * 公钥
 */
private RSAPublicKey publicKey;

/**
 * 字节数据转字符串专用集合
 */
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};


/**
 * 获取私钥
 *
 * @return 当前的私钥对象
 */
public RSAPrivateKey getPrivateKey() {
    return privateKey;
}

/**
 * 获取公钥
 *
 * @return 当前的公钥对象
 */
public RSAPublicKey getPublicKey() {
    return publicKey;
}

/**
 * 随机生成密钥对
 */
public void genKeyPair() {
    KeyPairGenerator keyPairGen = null;
    try {
        keyPairGen = KeyPairGenerator.getInstance("RSA");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    keyPairGen.initialize(1024, new SecureRandom());
    KeyPair keyPair = keyPairGen.generateKeyPair();
    this.privateKey = (RSAPrivateKey) keyPair.getPrivate();
    this.publicKey = (RSAPublicKey) keyPair.getPublic();
}

/**
 * 从文件中输入流中加载公钥
 *
 * @param in 公钥输入流
 * @throws Exception 加载公钥时产生的异常
 */
public void loadPublicKey(InputStream in) throws Exception {
    try {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String readLine = null;
        StringBuilder sb = new StringBuilder();
        while ((readLine = br.readLine()) != null) {
            if (readLine.charAt(0) == '-') {
                continue;
            } else {
                sb.append(readLine);
                sb.append('\r');
            }
        }
        loadPublicKey(sb.toString());
    } catch (IOException e) {
        throw new Exception("公钥数据流读取错误");
    } catch (NullPointerException e) {
        throw new Exception("公钥输入流为空");
    }
}


/**
 * 从字符串中加载公钥
 *
 * @param publicKeyStr 公钥数据字符串
 * @throws Exception 加载公钥时产生的异常
 */
public void loadPublicKey(String publicKeyStr) throws Exception {
    try {
    // byte[] buffer = Base64.decode(publicKeyStr.getBytes(), Base64.DEFAULT);
        BASE64Decoder base64Decoder= new BASE64Decoder();
        byte[] buffer= base64Decoder.decodeBuffer(publicKeyStr);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
        this.publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
    } catch (NoSuchAlgorithmException e) {
        throw new Exception("无此算法");
    } catch (InvalidKeySpecException e) {
        throw new Exception("公钥非法");
    } catch (NullPointerException e) {
        throw new Exception("公钥数据为空");
    }
}

/**
 * 从文件中加载私钥
 *
 * @return 是否成功
 * @throws Exception
 */
public void loadPrivateKey(InputStream in) throws Exception {
    try {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String readLine = null;
        StringBuilder sb = new StringBuilder();
        while ((readLine = br.readLine()) != null) {
            if (readLine.charAt(0) == '-') {
                continue;
            } else {
                sb.append(readLine);
                sb.append('\r');
            }
        }
        loadPrivateKey(sb.toString());
    } catch (IOException e) {
        throw new Exception("私钥数据读取错误");
    } catch (NullPointerException e) {
        throw new Exception("私钥输入流为空");
    }
}

public void loadPrivateKey(String privateKeyStr) throws Exception {
    try {
        //byte[] buffer = Base64.encode(privateKeyStr.getBytes(), Base64.DEFAULT);
        BASE64Decoder base64Decoder= new BASE64Decoder();
        byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        this.privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    } catch (NoSuchAlgorithmException e) {
        throw new Exception("无此算法");
    } catch (InvalidKeySpecException e) {
        throw new Exception("私钥非法");
    } catch (NullPointerException e) {
        throw new Exception("私钥数据为空");
    }
}

/**
 * 加密过程
 *
 * @param publicKey     公钥
 * @param plainTextData 明文数据
 * @return
 * @throws Exception 加密过程中的异常信息
 */
public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception {
    if (publicKey == null) {
        throw new Exception("加密公钥为空, 请设置");
    }
    Cipher cipher = null;
    try {
     
        cipher = Cipher.getInstance("RSA");
        //Android端无需添加此加密提供者,已默认实现
        //cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(plainTextData);
    } catch (NoSuchAlgorithmException e) {
        throw new Exception("无此加密算法");
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
        return null;
    } catch (InvalidKeyException e) {
        throw new Exception("加密公钥非法,请检查");
    } catch (IllegalBlockSizeException e) {
        throw new Exception("明文长度非法");
    } catch (BadPaddingException e) {
        throw new Exception("明文数据已损坏");
    }
}

/**
 * 解密过程
 *
 * @param privateKey 私钥
 * @param cipherData 密文数据
 * @return 明文
 * @throws Exception 解密过程中的异常信息
 */
public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception {
    if (privateKey == null) {
        throw new Exception("解密私钥为空, 请设置");
    }
    Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("RSA");
        //Android端无需添加此加密提供者,已默认实现
        //cipher = Cipher.getInstance("RSA", new BouncyCastleProvider());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] output = cipher.doFinal(cipherData);
        return output;
    } catch (NoSuchAlgorithmException e) {
        throw new Exception("无此解密算法");
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
        return null;
    } catch (InvalidKeyException e) {
        throw new Exception("解密私钥非法,请检查");
    } catch (IllegalBlockSizeException e) {
        throw new Exception("密文长度非法");
    } catch (BadPaddingException e) {
        throw new Exception("密文数据已损坏");
    }
}


/**
 * 字节数据转十六进制字符串
 *
 * @param data 输入数据
 * @return 十六进制内容
 */
public static String byteArrayToString(byte[] data) {
    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
        //取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移  
        stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);
        //取出字节的低四位 作为索引得到相应的十六进制标识符  
        stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
        if (i < data.length - 1) {
            stringBuilder.append(' ');
        }
    }
    return stringBuilder.toString();
}


public static void main(String[] args) {
    RSAEncrypt rsaEncrypt = new RSAEncrypt();

    //加载公钥
    try {
        rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY);
        System.out.println("加载公钥成功");
    } catch (Exception e) {
        System.err.println(e.getMessage());
        System.err.println("加载公钥失败");
    }
    /*try {
        rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY);
        System.out.println("加载公钥成功");
    } catch (Exception e) {
        System.err.println(e.getMessage());
        System.err.println("加载公钥失败");
    }
    //加载私钥
    try {
        rsaEncrypt.loadPrivateKey(RSAEncrypt.DEFAULT_PRIVATE_KEY);
        System.out.println("加载私钥成功");
    } catch (Exception e) {
        System.err.println(e.getMessage());
        System.err.println("加载私钥失败");
    }
    try {
        SecureRandom sr = new SecureRandom();
        KeyGenerator kg = KeyGenerator.getInstance("DES");
        kg.init(56, sr);
        SecretKey generateKey = kg.generateKey();
        String encodeHexString = Hex.toHexString(generateKey.getEncoded());
        System.out.println(encodeHexString);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    //测试字符串
    String encryptStr= "Test String chaijunkun";

    try {
        //加密
        long encryptStart = System.currentTimeMillis();
        byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), encryptStr.getBytes());
        long encryptEnd = System.currentTimeMillis();
        System.out.println("密文长度:"+ cipher.length);
        System.out.println(RSAEncrypt.byteArrayToString(cipher));
        System.out.println("加密时间"+ (encryptEnd-encryptStart));

        //解密
        long decryptStart = System.currentTimeMillis();
        byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), cipher);
        long decryptEnd = System.currentTimeMillis();
        System.out.println("明文长度:"+ plainText.length);
        System.out.println(RSAEncrypt.byteArrayToString(plainText));
        System.out.println("解密时间"+ (decryptEnd-decryptStart));

        System.out.println(new String(plainText));
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }*/ 
        }
}  

DES工具类实现

public class DESCoder {
/**
 * 密钥算法
 * java 7只支持56位密钥
 * Bouncy Castle 支持64位密钥
 */
public static final String KEY_ALGORITHM = "DES";
/**
 * 加密/解密算法 /工作模式/填充方式
 */
public static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
/**
 * 转换密钥
 * @param key 二进制密钥
 * @return key 密钥
 * @throws Exception
 */
private static Key toKey(byte[] key) throws Exception{
    //实例化DES密钥材料
    DESKeySpec dks = new DESKeySpec(key);
    //实例化密钥工厂
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
    //生产密钥
    SecretKey secretKey = keyFactory.generateSecret(dks);
    return secretKey;
}
/**
 * 解密
 * @param data 待解密数据
 * @param key 密钥
 * @return byte[] 解密数据
 * @throws Exception
 */
public static byte[] decrypt(byte[] data,byte[] key) throws Exception{
    //还原密钥
    Key k = toKey(key);
    //实例化 
    Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
    //初始化,设置为解密模式
    cipher.init(Cipher.DECRYPT_MODE, k);
    //执行操作
    return cipher.doFinal(data);
}
/**
 * 加密
 * @param data 待加密数据
 * @param key 密钥
 * @return byte[] 加密数据
 * @throws Exception
 */
public static byte[] encrypt(byte[] data,byte[] key) throws Exception{
    //还原密钥
    Key k = toKey(key);
    //实例化
    Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM,new BouncyCastleProvider());
    //初始化,设置为加密模式
    cipher.init(Cipher.ENCRYPT_MODE,k);
    //执行操作
    return cipher.doFinal(data);
}
/**
 * 生产密钥
 * java 7只支持56位 密钥
 * Bouncy Castle 支持64位密钥
 * @return byte[] 二进制密钥
 * @throws Exception
 */
public static byte[] initKey() throws Exception{
    /*
     * 实例化密钥生成器
     * 若要使用64位密钥注意替换
     * 讲下述代码中的
     * KeyGenerator.getInstance(KEY_ALGORITHM);
     * 替换为
     * KeyGenerator.getInstance(KEY_ALGORITHM,"BC");
     */
    KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM,new BouncyCastleProvider());

    kg.init(64);
    //生成密钥
    SecretKey secretKey = kg.generateKey();
    //获得密钥的二进制编码形式
    return secretKey.getEncoded();
}
}

四、JavaWeb端配置

  Web后端只需要在Controller中添加以下代码,接受服务端传递的data和sign,并完成接收的Json字符串转换为实体类即可

/**
 * 解密所需数据
 *
 * @param data 接受客户端上传的Json格式的数据
 * @param sign 接受客户端上传的解密数据的key值
 */
public <T> T convertJson(String data, String sign,Class<T> clazz) {
    System.out.println(data);
    System.out.println(sign);
    T tClass = null;
    try {
        //rsa加密
        RSAEncrypt rsaEncrypt = new RSAEncrypt();
        //加载rsa私钥
        InputStream in = new FileInputStream(new File("C:\\OpenSSL-Win64\\bin\\pkcs8_rsa_private_key.pem"));
        rsaEncrypt.loadPrivateKey(in);
        //获取RSA加密的key的数据,并把该16进制的sign转成byte[],(客户端采用将byte[]转成16进制进行数据上传)
        byte[] keyBytes = parseHexStr2Byte(sign);
        //通过RSA解密DES的key值
        byte[] rsaKey = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), keyBytes);

        //通过DES的key值解密需要的json数据
        byte[] desData = DESCoder.decrypt(parseHexStr2Byte(data), rsaKey);
        System.out.println(Arrays.toString(desData));
        System.out.println(new String(desData));
        tClass = JSON.parseObject(new String(desData), clazz);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return tClass;
}

五、备注

  后续会上传相关Demo到Github,欢迎Star
  GitHub
  该文章仅供学习交流所用,转载须注明出处

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

推荐阅读更多精彩内容