文章851
标签121
分类10

解决 PHPStorm 无法格式化代码的问题

在使用 PHPStorm 进行开发时,代码格式化是一个非常重要的功能,能够帮助我们保持代码风格的一致性和可读性。然而,有时候可能会遇到 PHPStorm 无法格式化代码的问题,这可能会影响我们的开发效率。在本文中,我们将介绍一些可能的解决方法来解决 PHPStorm 无法格式化代码的情况。

可能的解决方法

1. 检查快捷键

首先,确保您使用的是正确的快捷键来触发代码格式化功能。在 PHPStorm 中,默认的代码格式化快捷键是 Ctrl + Alt + L(Windows/Linux)或 Cmd + Option + L(Mac)。如果您的快捷键设置有变化,可以前往设置中进行调整。

2. 检查文件类型

确保 PHPStorm 正确识别了您正在编辑的文件类型。有时候,PHPStorm 可能无法识别文件类型,导致无法正确应用代码格式化规则。您可以手动设置文件类型,或者检查文件的后缀名是否正确。

3. 检查代码风格设置

在 PHPStorm 中,您可以定义自己的代码风格规则。确保您的代码风格设置与您想要应用的代码格式化规则一致。您可以在 PHPStorm 的设置中找到代码风格设置,并根据需要进行调整。

4. 清除缓存

有时候,PHPStorm 的缓存可能会导致一些问题,包括无法正确格式化代码。尝试清除 PHPStorm 的缓存,可以通过 File -> Invalidate Caches / Restart 来进行操作。

5. 更新 PHPStorm

如果您的 PHPStorm 版本较老,可能会存在一些已知的问题。尝试更新到最新版本,看看是否能够解决代码格式化的问题。

结论

在开发过程中,代码格式化是一个非常重要的环节,能够提高代码的可读性和维护性。如果遇到 PHPStorm 无法格式化代码的问题,可以尝试以上方法来解决。希望本文对您有所帮助!

PHP 中判断 AJAX 请求:HTTP_X_REQUESTED_WITH

在 Web 开发中,经常需要区分普通请求和 AJAX 请求,以便在服务器端做出相应的处理。在 PHP 中,可以通过检查 HTTP 头部的 HTTP_X_REQUESTED_WITH 字段来判断请求是否来自 AJAX。

HTTP_X_REQUESTED_WITH 字段简介

HTTP_X_REQUESTED_WITH 是一个非标准的 HTTP 头部字段,通常在 AJAX 请求中被设置为 XMLHttpRequest。通过检查该字段,可以确定请求是否是通过 AJAX 发起的。

PHP 中判断 AJAX 请求的方法

在 PHP 中,您可以使用以下代码来检查 HTTP_X_REQUESTED_WITH 字段,以判断请求是否为 AJAX:

if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
    // 处理 AJAX 请求
    echo 'This is an AJAX request.';
} else {
    // 处理普通请求
    echo 'This is a normal request.';
}

上述代码首先检查 HTTP_X_REQUESTED_WITH 字段是否被设置,并且其值是否为 XMLHttpRequest。如果是,即可确定请求为 AJAX 请求,可以根据需要做出相应的处理。

注意事项

  • HTTP_X_REQUESTED_WITH 字段并非标准的 HTTP 头部字段,因此在某些情况下可能不可靠。
  • AJAX 请求可以被伪造,因此仅依赖此字段可能不足以确保请求的安全性。

结论

通过在 PHP 中检查 HTTP_X_REQUESTED_WITH 字段,您可以方便地区分普通请求和 AJAX 请求,从而根据不同类型的请求做出相应的处理。在开发 Web 应用程序时,灵活运用这种方法可以提高应用的交互性和用户体验。

Google 开源技术 Protobuf(Protocol Buffers)简介与示例

Google 的开源技术 Protobuf(Protocol Buffers)是一种轻量级且高效的数据交换格式,用于结构化数据的序列化,适用于通信协议、数据存储等场景。本文将介绍 Protobuf 的基本概念,并提供一个简单的示例来演示如何使用 Protobuf。

Protobuf 简介

什么是 Protobuf?

Protobuf 是 Google 开发的一种数据序列化格式,类似于 XML 或 JSON,但更轻量且更高效。通过定义结构化数据的消息格式,可以自动生成代码来读写这些数据结构,从而实现不同平台和语言之间的数据交换。

Protobuf 的优点

  • 高效性:相比 XML 和 JSON,Protobuf 生成的数据更小,序列化和反序列化速度更快。
  • 可扩展性:可以通过版本化来更新消息格式,而不会破坏旧版本的兼容性。
  • 代码生成:根据定义的消息格式自动生成代码,简化开发过程。

Protobuf 示例

1. 定义消息格式

首先,创建一个 .proto 文件来定义消息格式。例如,创建一个 person.proto 文件:

syntax = "proto3";

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
}

2. 编译 .proto 文件

使用 Protobuf 编译器 protoc 来生成对应语言的代码。假设您要生成 Python 代码,可以运行以下命令:

protoc --python_out=. person.proto

3. 使用生成的代码

在 Python 中使用生成的代码来序列化和反序列化数据:

import person_pb2

person = person_pb2.Person()
person.name = "Alice"
person.id = 123
person.email = "alice@example.com"

# Serialize to bytes
data = person.SerializeToString()

# Deserialize from bytes
new_person = person_pb2.Person()
new_person.ParseFromString(data)

print(new_person)

结论

通过上述示例,您可以了解如何使用 Protobuf 来定义消息格式,并通过生成的代码进行数据序列化和反序列化。Protobuf 的简洁性和高效性使其成为一种流行的数据交换格式,适用于各种应用场景。

记一次Java加密加签算法到php的坑

写代码的经历中,总少不了与外部的程序对接,一旦有这样的事,往往周期会很长,很麻烦,因为你要考虑的事会多了很多,其中安全性的加密解密就是重要的一项。写代码,可以出Bug,但逼格不能弱。什么是逼格?和别人对接一下,连加密解密都没有,连验证签名都没有,别人一眼就望穿你,这就是眼界的问题了。

这次的故事是对接一个大的支付系统,对方也是第一个对接我们,然后定了接口和加解密算法,给了个Java的Demo,问了声,有没有PHP的,没有,歇菜,自己来吧。
代码说多不多,说少不少,为了先说事,代码放在最后面。

第一个坑:加密算法多多,你到底要闹咋样?

码农兄弟们可以先下去看一眼代码,然后说说它用了啥算法?
接口传输的安全性算法,首先要区分是加签名还是加密?区别是,签名+原文可以验证收到的信息是否被篡改,不过别指望有了签名就可以还原出原文来。加密就是不让别人看到原文是啥,然后给把钥匙,要让接收的人解密看出原文来。两者的算法基本上来说,就是完全不同的。

加密还分对称非对称。对称有:DES3DESTDEABlowfishRC2RC4RC5IDEASKIPJACKAES等,非对称有:RSAElgamal背包算法RabinD-HECC(椭圆曲线加密算法)

还有,你以为拿个公钥就够了?当然不是,还要一对。更坑的是,可能有不同的格式。对方给了我一个keystore格式的,发觉php完全不支持,想办法转成了pem格式的。

常见的密钥格式:jksjcep12pfxbksubr等等
常见的证书文件格式:cer,crtrsap7b,p7r,p7c,pem,p10,csr等等

最后还有一点,这次碰到的算法具体的参数。
我这次遇到的是3DES加密,AlGORITHM = "DESede/ECB/PKCS5Padding";,后面的类型和填充方式都不能差一点。

第二个坑:到底什么是密钥?

你会说这个很简单啊
Java里就是像这样:

PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password.toCharArray()); 

php里就是像这样:

$privateKey=openssl_pkey_get_private(file_get_contents($this->privatePemFilePath)); 

你以为你以为的就是你以为的吗?前面说了,即使算法一样,密钥格式不一样,开发语言一样,用法也完全不一样。

上面的只是格式不同,下面的还有编码的不同:
看起来我从代码里读出的密码是这个,但其实送入算法中的可不是,还要做一个Base64转换,如果你送入了错误的,会永远困在迷惘中。

$this->dESCORPKey = C('lakala_encrypt_key');
$key = $this->$dESCORPKey;
$encryptData = self::encrypt($key, $signedReq);
...
public function encrypt($key,$data){
    $decode_key = base64_decode($key);//此处需要BASE64解码(变为2进制)
    $encData = openssl_encrypt($data, 'DES-EDE3', $decode_key, OPENSSL_RAW_DATA);
    $encData = base64_encode($encData);
    return $encData;
}

PS:网上有在线加密解密算法的工具,往往和代码出来的结果不一致,除了各种参数都需要写对以外,特别注意密码(密钥)的输入格式,要不要Base64编码或者解码。

第三个坑:带中文的字符串格式

看一眼下面的代码,你就会知道,php里有很多json_encodejson_decodejava代码里有很多getByte()这样转换成字节的操作,一个经典的问题就来了,你如果传入了中文,中文按什么格式进行编码?编码不同,决定了加密算法操作时二进制的不同,也决定了最终输出的不同。
在写代码的时候查阅了javagetByte()方法,默认值竟然是读取机器的字符格式!!!所以,写代码的时候一定要注意,最好加上具体格式,不要用默认值,要这么写:getByte("UTF-8"),否则换了机器表现不一样,你会死的很难看。

第四个坑:语言的问题

虽然上帝说人人平等,但现实远远复杂的多。
虽然加密解密本质上说就是一种运算变换,什么语言都可以实现的,可你不会从底层开始搞的,那就涉及语言的问题。
最简单的例子:java里具体运算时都会把String转换成byte进行操作。PHP就不行。

加密算法这个玩意虽然复杂,不过前人已经给我们造了很多轮子了,网上随便一搜就可以找到很多资料,问题是没有成系统,特别各个语言间的轮子差异还蛮大的。如何做适配,在网上的资料非常的难找。

PHP里加密方法还有mcryptopenssl两套,互相之间的写法还差异蛮大的,让人头疼。

举个栗子(这里的Java解析使用了fastjson):

1 java中:
2 CommReq req = JSON.parseObject(jsonStr, CommReq.class, Feature.OrderedField);
3 PHP中:
4 $req = json_decode(trim($jsonStr), true);
5 ksort($req);

看起来很像了吧?才不是呢!以下是输入的Json

{
    "head": {
        "serviceSn": "B5D79F38B96040B7B992B6BE329D9975",
        "serviceId": "MPBF001",
        "channelId": "05",
        "inputSource": "I002",
        "opId": "",
        "requestTime": "20180628142105",
        "versionId": "1.0.0",
        "businessChannel": "LKLZFLLF"
    },
    "request": {
        "userId": "40012345678",
        "userName": "AA",
        "userMobile": "18675529912",
        "idNo": "110101198609096078"
    }
}

问题出在具体的类型定义,以及Json解析的深度
JAVA中有定义具体按哪个类型解析

1 public class CommReq implements Serializable {
2     private static final long serialVersionUID = 1L;
3     private CommReqHead head;
4     private String request;
5 }

JAVA这种强类型语言,必须定义类型,将request定义成String型,导致JSON解析出来的结果:

"request":"{\"userId\":\"40012345678\",\"userName\": \"AA\", \"userMobile\": \"18675529912\", \"idNo\": \"110101198609096078\" }";

PHP是弱类型语言,直接嵌套进去也解析出来了

"request": {"userId":"40012345678","userName": "AA", "userMobile": "18675529912", "idNo": "110101198609096078" } }

如果PHP要和JAVA真正保持一致(因为一旦不一致,加密结果就不一样了)

1 $req = json_decode(trim($jsonStr), true);
2 ksort($req);
3 req['request']=json_encode(req['request']);

前面也提到了密钥类型的问题,其实也和语言有关,PHP只支持PEM格式,所以,还用工具对keystore进行了转换,转换之后发现几个密码都已经不需要了。生成公钥PEM私钥PEM加上加密解密Key就可以了。

小结

回顾来看,其实只是解决了很小的一个问题,将一段JAVA代码转换成了PHP代码,甚至中间复杂的算法细节都调用原来就有的模块,更不用怀疑这些模块写的算法的正确性,但调试这样一个东西,却的的确确花费了非常大的精力。技术没有任何中间地带,只有行或者不行,容不得半分作假。开发必须要注重细节,细节到位了才不会出Bug,这点在加密解密这件事上,尤其的明显,输入差一个字符,输出完全不同。开发没有很容易的事,只有我做过,我熟悉的事。

代码在这里哦。钥匙就不提供了,这样直接copy代码跑不起来是正常的。哈哈

/**
 *
 */
package com.chuangmi.foundation.lakala.service.impl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.security.cert.X509Certificate;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.chuangmi.foundation.lakala.service.SignService;
import com.chuangmi.foundation.lakala.service.models.CommReq;
import com.chuangmi.foundation.lakala.service.models.CommRequest;
import com.chuangmi.foundation.lakala.service.models.CommRes;
import com.chuangmi.foundation.lakala.service.models.CommResponse;


@Service
public class SignServiceImpl implements SignService {

    private static final Logger logger = LoggerFactory.getLogger(SignService.class);

    @Value("${cer.filePath}")
    private String cerFilePath;

    @Value("${key.filePath}")
    private String keyFilePath;

    @Value("${key.passWord}")
    private String keyPassWord;

    @Value("${key.alias}")
    private String alias;

    @Value("${encrypt.key}")
    private String dESCORPKey;

    /**
     * 加密算法与填充方式
     */
    public static final String AlGORITHM = "DESede/ECB/PKCS5Padding"; // 定义加密算法,可用

    @PostConstruct
    public void init(){
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){

            System.out.println("security provider BC not found, will add provider");

            Security.addProvider(new BouncyCastleProvider());

        }
    }

    /**
     * 加签并加密需要发送的请求报文
     * @param 接口报文
     * @return 加签后可以发送的json密文
     * @throws JSONException
     */
    @Override
    public String signRequestJsonToSend(String jsonStr) throws JSONException {
        JSONObject resultObj = null;
        if(jsonStr.indexOf("\"head\"") >= 0)
        {
            CommReq req = JSON.parseObject(jsonStr, CommReq.class, Feature.OrderedField);
            // 对报文体签名
            String signData = signData(req.getRequest());

            logger.info("加签成功,原始报文:" + jsonStr + ",报文体签名:" + signData);

            req.getHead().setSignData(signData);
            resultObj = JSONObject.parseObject(JSON.toJSONString(req), Feature.OrderedField);
        }
        else if(jsonStr.indexOf("\"comm\"") >= 0)
        {
            CommRequest req = JSON.parseObject(jsonStr, CommRequest.class, Feature.OrderedField);
            // 对报文体签名
            String signData = signData(req.getData());

            logger.info("加签成功,原始报文:" + jsonStr + ",报文体签名:" + signData);

            req.getComm().setSigntx(signData);
            resultObj = JSONObject.parseObject(JSON.toJSONString(req), Feature.OrderedField);
        }

        String signedReq = String.valueOf(JSONObject.toJSON(resultObj));
        logger.info("加签后的报文:" + signedReq);

        //加密
        byte[] key = Base64.decodeBase64(dESCORPKey);
        byte[] encryptData = encrypt(key, signedReq.getBytes());

        String encryptStr = Base64.encodeBase64String(encryptData);

        logger.info("加密成功,密文:" + encryptStr);

        return encryptStr;
    }

    /**
     * 加签并加密需要发送的响应报文
     * @param 接口报文
     * @return 加签后可以发送的json密文
     * @throws JSONException
     */
    @Override
    public String signResponseJsonToSend(String jsonStr) throws JSONException {
        JSONObject resultObj = null;
        if(jsonStr.indexOf("\"head\"") >= 0)
        {
            CommRes response = JSON.parseObject(jsonStr, CommRes.class, Feature.OrderedField);
            resultObj = JSONObject.parseObject(JSON.toJSONString(response), Feature.OrderedField);
        }
        else if(jsonStr.indexOf("\"comm\"") >= 0)
        {
            CommResponse response = JSON.parseObject(jsonStr, CommResponse.class, Feature.OrderedField);
            // 对报文体签名
            String signData = signData(response.getData());

            logger.info("加签成功,原始报文:" + jsonStr + ",报文体签名:" + signData);

            response.getComm().setSigntx(signData);
            resultObj = JSONObject.parseObject(JSON.toJSONString(response), Feature.OrderedField);
        }

        String signedReq = String.valueOf(JSONObject.toJSON(resultObj));
        logger.info("加签后的响应报文:" + signedReq);

        //加密
        byte[] key = Base64.decodeBase64(dESCORPKey);
        byte[] encryptData = encrypt(key, signedReq.getBytes());

        String encryptStr = Base64.encodeBase64String(encryptData);

        logger.info("加密成功的响应报文,密文:" + encryptStr);

        return encryptStr;
    }

    /**
     * 从request提取json data,并解密,验签, 验签成功则返回data,否则返回null
     * @param request
     * @return
     * @throws IOException
     * @throws JSONException
     */
    @Override
    public String verifyReceivedRequest(HttpServletRequest request) throws IOException, JSONException {
        String json = extractJson(request);
        logger.info("接收报文密文:" + json);

        return verifyRequestJson(json);
    }

    /**
     * 对收到的请求json解密,验签, 验签成功则返回data,否则返回null
     * @param json
     * @return
     * @throws UnsupportedEncodingException
     * @throws JSONException
     */
    @Override
    public String verifyRequestJson(String json) throws UnsupportedEncodingException, JSONException {
        //解密
        byte[] key = Base64.decodeBase64(dESCORPKey);
        byte[] decryptBytes = decrypt(key, Base64.decodeBase64(json));
        String orig = new String(decryptBytes, "UTF-8");
        logger.info("【收到的报文请求】接收报文:" + orig);

        // 验签
        JSONObject obj = JSONObject.parseObject(orig, Feature.OrderedField);
        String reqStr = String.valueOf(JSONObject.toJSON(obj));
        if(reqStr.indexOf("\"comm\"") >= 0)
        {
            CommRequest req = JSON.parseObject(reqStr, CommRequest.class, Feature.OrderedField);
            String signtx = req.getComm().getSigntx();

            logger.info("报文中的签名:" + signtx);
            boolean nRet = verifyData(req.getData(), signtx);
            if(nRet)
            {
                req.getComm().setSigntx("");
                return JSON.toJSONString(req);
            }
            else
            {
                return null;
            }
        }
        else if(reqStr.indexOf("\"head\"") >= 0)
        {
            CommReq req = JSON.parseObject(reqStr, CommReq.class, Feature.OrderedField);
            String signData = req.getHead().getSignData();

            logger.info("报文中的签名:" + signData);
            boolean nRet = verifyData(req.getRequest(), signData);
            if(nRet)
            {
                req.getHead().setSignData("");
                return JSON.toJSONString(req);
            }
            else
            {
                return null;
            }
        }
        else
        {
            return null;
        }
    }

    /**
     * 对响应的报文json解密,验签, 验签成功则返回data,否则返回null
     * @param json
     * @return
     * @throws UnsupportedEncodingException
     * @throws JSONException
     */
    @Override
    public String verifyResponseJson(String json) throws UnsupportedEncodingException, JSONException {
        //解密
        byte[] key = Base64.decodeBase64(dESCORPKey);
        byte[] decryptBytes = decrypt(key, Base64.decodeBase64(json));
        String orig = new String(decryptBytes, "UTF-8");
        logger.info("【收到的响应报文】报文:" + orig);

        // 验签
        JSONObject obj = JSONObject.parseObject(orig, Feature.OrderedField);
        String reqStr = String.valueOf(JSONObject.toJSON(obj));
        if(reqStr.indexOf("\"comm\"") >= 0)
        {
            CommResponse response = JSON.parseObject(reqStr, CommResponse.class, Feature.OrderedField);
            String signtx = response.getComm().getSigntx();

            logger.info("报文中的签名:" + signtx);
            boolean nRet = verifyData(response.getData(), signtx);
            if(nRet)
            {
                response.getComm().setSigntx("");
                return JSON.toJSONString(response);
            }
            else
            {
                return null;
            }
        }
        else if(reqStr.indexOf("\"head\"") >= 0)
        {
            CommRes response = JSON.parseObject(reqStr, CommRes.class, Feature.OrderedField);
            return JSON.toJSONString(response);
        }
        else
        {
            return null;
        }
    }

    public String extractJson(HttpServletRequest request) throws IOException {
        //用于接收对方的jsonString
        StringBuilder jsonString = new StringBuilder();
        BufferedReader reader = request.getReader();
        try {
            String line;
            while ((line = reader.readLine()) != null) {
                jsonString.append(line);
            }
        } finally {
            reader.close();
        }
        String data = jsonString.toString();
        return data;
    }

    /*  使用私钥签名,并返回密文
      * @param param  需要进行签名的数据
      * @return 签名
      */
    private String signData(String param)
    {
        InputStream inputStream = null;
        try {

            String store_password = keyPassWord;// 密钥库密码
            String password = keyPassWord;// 私钥密码
            String keyAlias = alias;// 别名
            // a. 创建针对jks文件的输入流

            inputStream = new FileInputStream(keyFilePath);// CA 文件名 如: D://tmp/encrypt.jks
            // input = getClass().getClassLoader().getResourceAsStream(keyFile);
            // 如果制定classpath下面的证书文件

            // b. 创建KeyStore实例 (store_password密钥库密码)
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(inputStream, store_password.toCharArray());

            // c. 获取私钥 (keyAlias 为私钥别名,password为私钥密码)
            PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password.toCharArray());

            // 实例化一个用SHA算法进行散列,用RSA算法进行加密的Signature.
            Signature dsa = Signature.getInstance("SHA1withRSA");
            // 加载加密散列码用的私钥
            dsa.initSign(privateKey);
            // 进行散列,对产生的散列码进行加密并返回
            byte[] p = param.getBytes();
            dsa.update(p);

            return Base64.encodeBase64String(dsa.sign());// 进行签名, 加密后的也是二进制的,但是返回给调用方是字符串,将byte[]转为base64编码

        } catch (Exception gse) {

            gse.printStackTrace();
            return null;

        } finally {

            try {
                if (inputStream != null)
                    inputStream.close();// 判断
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /*  用公钥证书对字符串进行签名验证
      * @param urlParam  需要进行签名验证的数据
      * @param signParam 编码后的签名
      * @return 校验签名,true为正确 false为错误
      */
    private boolean verifyData(String urlParam, String signParam)
    {
        boolean verifies = false;

        InputStream in = null;

        try {

            // a. 创建针对cer文件的输入流
            InputStream inputStream = new FileInputStream(cerFilePath);// CA 文件名 如: D://tmp/cerfile.p7b
            // input = getClass().getClassLoader().getResourceAsStream(keyFile);
            // 如果制定classpath下面的证书文件

            // b. 创建KeyStore实例 (store_password密钥库密码)
            X509Certificate cert = X509Certificate.getInstance(inputStream);

            // c. 获取公钥 (keyAlias 为公钥别名)
            PublicKey pubKey = cert.getPublicKey();

            if (pubKey != null) {
                // d. 公钥进行验签
                // 获取Signature实例,指定签名算法(与之前一致)
                Signature dsa = Signature.getInstance("SHA1withRSA");
                // 加载公钥
                dsa.initVerify(pubKey);
                // 更新原数据
                dsa.update(urlParam.getBytes());

                // 公钥验签(true-验签通过;false-验签失败)
                verifies = dsa.verify(Base64.decodeBase64(signParam));// 将签名数据从base64编码字符串转回字节数组
            }

        } catch (Exception gse) {
            gse.printStackTrace();
        } finally {

            try {
                if (in != null)
                    in.close();// 判断
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return verifies;

    }


    // DES,DESede,Blowfish
    /**
     * 使用3des加密明文
     *
     * @param byte[] key: 密钥
     * @param byte[] src: 明文
     *
     */
    private byte[] encrypt(byte[] key, byte[] src) {
        try {

            if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){

                System.out.println("security provider BC not found, will add provider");

                Security.addProvider(new BouncyCastleProvider());

            }

            // 生成密钥
            SecretKey deskey = new SecretKeySpec(key, AlGORITHM);
            // 加密
            Cipher c1 = Cipher.getInstance(AlGORITHM);
            c1.init(Cipher.ENCRYPT_MODE, deskey);
            return c1.doFinal(src);// 在单一方面的加密或解密
        } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (java.lang.Exception e3) {
            e3.printStackTrace();
        }
        return null;
    }

    /**
     * 使用3des解密密文
     *
     * @param byte[] key: 密钥
     * @param byte[] src: 密文
     *
     */
    private byte[] decrypt(byte[] keybyte, byte[] src) {
        try {

            if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){

                System.out.println("security provider BC not found, will add provider");

                Security.addProvider(new BouncyCastleProvider());

            }

            // 生成密钥
            SecretKey deskey = new SecretKeySpec(keybyte, AlGORITHM);
            // 解密
            Cipher c1 = Cipher.getInstance(AlGORITHM);
            c1.init(Cipher.DECRYPT_MODE, deskey);
            return c1.doFinal(src);
        } catch (java.security.NoSuchAlgorithmException e1) {
            e1.printStackTrace();
        } catch (javax.crypto.NoSuchPaddingException e2) {
            e2.printStackTrace();
        } catch (java.lang.Exception e3) {
            e3.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args) throws JSONException {

        InputStream inputStream = null;
        try {

            String store_password = "123456";// 密钥库密码
            String password = "123456";// 私钥密码
            String keyAlias = "www.lakala.com";// 别名
            // a. 创建针对jks文件的输入流

            inputStream = new FileInputStream("/Users/rinson/eclipse-workspace/lakala/lakala-server/asdc.keystore");// CA 文件名 如: D://tmp/encrypt.jks
            // input = getClass().getClassLoader().getResourceAsStream(keyFile);
            // 如果制定classpath下面的证书文件

            // b. 创建KeyStore实例 (store_password密钥库密码)
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(inputStream, store_password.toCharArray());

            // c. 获取私钥 (keyAlias 为私钥别名,password为私钥密码)
            PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password.toCharArray());

            // 实例化一个用SHA算法进行散列,用RSA算法进行加密的Signature.
            Signature dsa = Signature.getInstance("SHA1withRSA");
            // 加载加密散列码用的私钥
            dsa.initSign(privateKey);
            String param = "XXXXX";
            // 进行散列,对产生的散列码进行加密并返回
            dsa.update(param .getBytes());

            System.out.println(Base64.encodeBase64String(dsa.sign()));// 进行签名, 加密后的也是二进制的,但是返回给调用方是字符串,将byte[]转为base64编码

        } catch (Exception gse) {

            gse.printStackTrace();

        } finally {

            try {
                if (inputStream != null)
                    inputStream.close();// 判断
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

PHP

<?php

namespace Common\Lib\Lakala;

class SignService
{
    private $publicPemFilePath='';
    private $privatePemFilePath='';
    private $dESCORPKey = '';

    //初始化
    public function __construct()
    {
        $this->publicPemFilePath = C('lakala_cer_filePath');
        $this->privatePemFilePath=C('lakala_key_filePath');
        $this->dESCORPKey = C('lakala_encrypt_key');
    }

    /**
     * 加签并加密需要发送的请求报文
     * @param $head 是java类CommReqHead,需在调用时传入
     * @param $data 是具体的请求参数数组
     *
     */
    public function ToLakala($head,$data,$url){
        //CommReq
        $ret = [
            'head'=>$head,
            'request'=>$data,
        ];
        $ret = json_encode($ret);
        //会对整个请求body加签加密,返回字符串body体
        $params = $this->signRequestJsonToSend($ret);

        //http request
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json; charset=utf-8', 'Content-Length: ' . strlen($data)));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
        $result = curl_exec($ch);
        curl_close($ch);
        $result = $params;
        //验证返回结果
        $response = $this->verifyResponseJson($result);
        //结果返回
        return $response;
    }


    public function FromLakala(){
        $lakalaSign = new SignService();
        $params = I('');
        $params = $this->verifyRequestJson($params);
        return $params;
    }

    public function FromLakalaResponse($head,$response){
        $ret = [
            'head'=>$head,
            'response'=>$response,
        ];
        $res = $this->signResponseJsonToSend($ret);
        return $res;
    }

    /**
     * 加签并加密需要发送的请求报文
     * @param $jsonStr 接口报文(String类型)
     * @return 加签后可以发送的json密文
     */
    public function signRequestJsonToSend($jsonStr)
    {
        if(strpos($jsonStr,"\"head\"")!= false)
        {
            $req = json_decode(trim($jsonStr), true);
            ksort($req);
            // 对报文体签名
            $signData = $this->signData($req['request']);
            $req['head']['signData']= $signData;
            $req['request']=json_encode($req['request'],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
            ksort($req['head']);
            $resultObj = $req;

        }
        else if(strpos($jsonStr,"\"comm\"")!= false)
        {
            $req = json_decode(trim($jsonStr), true);
            ksort($req);
            // 对报文体签名
            $signData = $this->signData($req['data']);
            $req['comm']['signtx']=$signData;
            $req['data']=json_encode($req['data'],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
            ksort($req['head']);
            $resultObj = $req;
        }

        $signedReq = json_encode($resultObj,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);

        //logger.info("加签后的报文:" + signedReq);
        //此处直接放入要加密的数据
        $key = $this->dESCORPKey;
        $encryptData = self::encrypt($key,$signedReq);
        //logger.info("加密成功,密文:" + encryptStr);

        return $encryptData;
    }

    /**
     * 加签并加密需要发送的响应报文
     * @param $jsonStr 接口报文(String类型)
     * @return 加签后可以发送的json密文
     */
    public function signResponseJsonToSend($jsonStr) {
        if(strpos($jsonStr,"\"head\"")!= false)
        {
            $response = json_decode(trim($jsonStr), true);
            $resultObj = json_decode(json_encode($response));
        }
        else if(strpos($jsonStr,"\"comm\"")!= false)
        {
            $response = json_decode(trim($jsonStr), true);
            ksort($response);
            // 对报文体签名
            $signData = $this->signData($response['data']);

            //logger.info("加签成功,原始报文:" + jsonStr + ",报文体签名:" + signData);
            $response['comm']['signTx']=$signData;
               $response['data']=json_encode($response['data'],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
            ksort($response['comm']);
            $resultObj = $response;
        }

        $signedReq = json_encode($resultObj,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
        //logger.info("加签后的响应报文:" + signedReq);

        $key = $this->$dESCORPKey;
        $encryptData = self::encrypt($key, $signedReq);

        //logger.info("加密成功的响应报文,密文:" + encryptStr);
        return $encryptData;
    }

    /**
     * 对响应的报文json解密,验签, 验签成功则返回data,否则返回null
     * @param json (String)
     * @return (String)
     */
    public function verifyResponseJson($json) {
        //解密
        $key = $this->dESCORPKey;
        $decryptBytes = self::decrypt($key, $json);

        //logger.info("【收到的响应报文】报文:" + orig);

        // 验签
        $obj = json_decode($decryptBytes);
        $reqStr = json_encode($obj);
        if(strpos($reqStr,"\"comm\"")!= false)
        {
            $response = json_decode($reqStr,true);
            $signtx = $response['comm']['signtx'];

            //logger.info("报文中的签名:" + signtx);
            $nRet = $this->verifyData($response['data'], $signtx);
            if($nRet)
            {
                $response['comm']['signtx']="";
                return json_encode($response);
            }
            else
            {
                return null;
            }
        }
        else if(strpos($reqStr,"\"head\"")!= false)
        {
            return $reqStr;
        }
        else
        {
            return null;
        }
    }

    /**
     * 对收到的请求json解密,验签, 验签成功则返回data,否则返回null
     * @param json (String)
     * @return (String)
     */
    public function verifyRequestJson($json) {
        //解密
        $key = $this->dESCORPKey;
        $decryptBytes = self::decrypt($key, $json);

        // 验签
        $obj = json_decode($decryptBytes);
        $reqStr = json_encode($obj);
        if(strpos($reqStr,"\"comm\"")!= false)
        {
            $req = json_decode($reqStr,true);
            ksort($req);
            $signtx = $req['comm']['signtx'];

            //logger.info("报文中的签名:" + signtx);
            $nRet = $this->verifyData($req['data'], $signtx);
            if($nRet)
            {
                $req['comm']['signtx']="";
                return json_encode($req);
            }
            else
            {
                return null;
            }
        }
        else if(strpos($reqStr,"\"head\"")!= false)
        {
            $req = json_decode($reqStr,true);
            ksort($req);
            $signData = $req['head']['signData'];
            //logger.info("报文中的签名:" + signData);
            $nRet = $this->verifyData($req['request'], $signData);
            return $nRet;
            if($nRet)
            {
                $req['head']['signData']="";
                return json_encode($req);
            }
            else
            {
                return null;
            }
        }
        else
        {
            return null;
        }
    }
        /*  使用私钥签名,并返回密文
        * @param param  需要进行签名的数据(Array)
        * @return 签名(加签字符串String)
        */
    private function signData($param)
    {
        $content = json_encode($param,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
        $privateKey=openssl_pkey_get_private(file_get_contents($this->privatePemFilePath));
        openssl_sign($content, $signature, $privateKey);
        openssl_free_key($privateKey);
        return base64_encode($signature);

    }

    /*  用公钥证书对字符串进行签名验证
      * @param urlParam  需要进行签名验证的数据(直接从解密报文中取出的String)
      * @param signParam 编码后的签名(直接从解密报文中取出的String)
      * @return 校验签名,true为正确 false为错误
      */
    private function verifyData($urlParam,$signParam)
    {
        $signature = base64_decode($signParam);
        $pubkeyid = openssl_pkey_get_public(file_get_contents($publicPemFilePath));
        // state whether signature is okay or not
        $verifies = openssl_verify($urlParam, $signature, $pubkeyid);
        return $verifies;
    }

    /**
     * @param $key获取的密钥字符串(直接从配置文件中取出)
     * @param $data (字符串)
     * @return string
     */
    public function encrypt($key,$data){
        $decode_key = base64_decode($key);//此处需要BASE64解码(变为2进制)
        $encData = openssl_encrypt($data, 'DES-EDE3', $decode_key, OPENSSL_RAW_DATA);
        $encData = base64_encode($encData);
        return $encData;
    }

    /**
     * @param $key获取的密钥字符串(直接从配置文件中取出)
     * @param $data (字符串)
     * @return string
     */
    public function decrypt($key,$data){
        $decode_key = base64_decode($key);//此处需要BASE64解码(变为2进制)
        $data = base64_decode($data);//此处需要BASE64解码(变为2进制)
        $decData = openssl_decrypt($data, 'DES-EDE3', $decode_key, OPENSSL_RAW_DATA);
        return $decData;
    }

}
">