商米签名 Java代码实现

package admin.sunmi;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.UUID;

public class SunMiHttpClient {
    private final Log logger = LogFactory.getLog(SunMiHttpClient.class);
    private final String appId;
    private final String appKey;
    private final String appPrivateKey;
    private final String sunmiPublicKey;

    public SunMiHttpClient(String appId, String appkey, String appPrivateKey, String sunmiPublicKey) {
        this.appId = appId;
        this.appKey = appkey;
        this.appPrivateKey = appPrivateKey;
        this.sunmiPublicKey = sunmiPublicKey;
    }

    /**
     * Http request
     * @param url: api url
     * @param params: json string parameters
     * @param signType: RSA|hmac
     * @return response body
     * @throws Exception
     */
    public String request(String url, String params, String signType) throws Exception {
        String respBody = "";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        StringEntity s = new StringEntity(params, "utf-8");
        httpPost.setEntity(s);
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonce = UUID.randomUUID().toString();
        String sign;
        if ("RSA".equals(signType)) {
            sign = generateRsa2048Sign(appId, appPrivateKey, timestamp, nonce, params);
        } else {
            sign = generateHmac256Sign(appId, appKey, timestamp, nonce, params);
        }
        httpPost.setHeader("Sunmi-Sign", sign);
        httpPost.setHeader("Sunmi-Timestamp", timestamp);
        httpPost.setHeader("Sunmi-Nonce", nonce);
        httpPost.setHeader("Sunmi-Appid", appId);
        httpPost.setHeader(HTTP.CONTENT_TYPE, "application/json;charset=utf-8");
        CloseableHttpResponse response = httpClient.execute(httpPost);
        if (response.getStatusLine().getStatusCode() != 200) {
            throw new Exception("Server error");
        }
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            respBody = EntityUtils.toString(entity, "UTF-8");
        }
        EntityUtils.consume(entity);
        response.close();
        logger.info("Response body: " + respBody);
        return respBody;
    }

    
    /**
     * 生成Hmac-sha256签名
     * @param appId: appId
     * @param appKey: appKey
     * @param timestamp: timestamp
     * @param nonce: random string
     * @param params: json string parameters
     * @return signature
     * @throws Exception
     */
    public String generateHmac256Sign(String appId, String appKey, String timestamp, String nonce, String params) throws Exception {
        String content = params + appId + timestamp + nonce;
        Mac hmacSHA256 = Mac.getInstance("HmacSHA256");
        SecretKeySpec key = new SecretKeySpec(appKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        hmacSHA256.init(key);
        byte[] array = hmacSHA256.doFinal(content.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString();
    }

    /**
     * 生成Rsa 2048签名
     * @param appId
     * @param privateKey: rsa private key
     * @param timestamp
     * @param nonce
     * @param params
     * @return signature
     * @throws Exception
     */
    public static String generateRsa2048Sign(String appId, String privateKey, String timestamp, String nonce, String params) throws Exception {
        String content = params + appId + timestamp + nonce;
        byte[] keyBytes = Base64.decodeBase64(privateKey.replaceAll("(\\s)|(--.*--)", ""));
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(priKey);
        signature.update(content.getBytes());
        return Base64.encodeBase64String(signature.sign());
    }

    /**
     * 用公钥验证签名
     * @param appId
     * @param publicKey: rsa public key
     * @param sign
     * @param timestamp
     * @param nonce: random string
     * @param params
     * @return
     */
    public static boolean verifyRsa2048Sign(String appId, String publicKey, String sign, String timestamp, String nonce, String params) {
        try {
            String content = params + appId + timestamp + nonce;
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey.replaceAll("(\\s)|(--.*--)", "")));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.decodeBase64(sign));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

近期 对接了商米云端支付  这是我从github上找到的类(但报错)  于是我对其进行了修改 现开源出来。

其中 verifyRsa2048Sign 方法 在支付回调时验签不好使,但经测试 我使用上述代码生成密钥再使用该方法进行验签是没问题的。也不知道是那里的问题  没去研究。采用时自行辨认吧。

侵权请联系我删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋迟言说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值