即时通讯(腾讯)流程

1.controller层

// 拼接用户id
        String imId = String.format("u_%s",finalUser.getId());
        // 获取腾讯云用户信息
        Map imUserInfo = this.tencentService.getImUserInfo(imId);
        // 如果没有腾讯云用户信息
        if(imUserInfo == null){
            // 注册腾讯云用户
            this.tencentService.registerImUser(imId,finalUser.getNickname(),finalUser.getHead());
        }else{
            // 已经有腾讯云账户,更新账户信息
            this.tencentService.updateImUserInfo(imId,finalUser.getNickname(),finalUser.getHead());
        }
        // 获取腾讯云通信签名
        String imSig = this.tencentService.getImUserSig(imId);
        return new Message(new HashMap(){{
            this.put("token",subject.getSession().getId());
            this.put("user", new HashMap(){{
                this.put("id", finalUser.getId());
                this.put("head", finalUser.getHead());
                this.put("role", User.Role.values()[finalUser.getRole() == null ? 0 : finalUser.getRole()].name());
            }});
            this.put("im",new HashMap(){{
                // 腾讯云用户id
                this.put("id",imId);
                // 腾讯云通信签名
                this.put("token",imSig);
            }});
        }});

2.TencentService


import com.fasterxml.jackson.databind.ObjectMapper;
import net.pedicure.exception.SystemException;
import net.pedicure.module.Configuration;
import net.pedicure.module.HttpClient;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.Arrays;
import org.json.JSONObject;
import org.springframework.stereotype.Service;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.security.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

@Service
public class TencentService {

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final HttpClient httpClient = new HttpClient();

    private String imManagerSig;

    /**
     * 更新用户信息
     * @param identifier 用户id
     * @param nick 昵称
     * @param faceUrl 头像
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws KeyManagementException
     */
    public void updateImUserInfo(String identifier,String nick,String faceUrl) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        Map response = this.objectMapper.readValue(this.httpClient.doPost(String.format("https://console.tim.qq.com/v4/profile/portrait_set?usersig=%s&identifier=%s&sdkappid=%s&random=%s&contenttype=json",this.getImManagerSig(),Configuration.property(Configuration.TENCENT_IM_ACCOUNT_MANAGER),Configuration.property(Configuration.TENCENT_IM_SDK_APP_ID),new Date().getTime()),this.objectMapper.writeValueAsString(new HashMap(){{
            this.put("From_Account",identifier);
            this.put("ProfileItem",new ArrayList(){{
                this.add(new HashMap(){{
                    this.put("Tag","Tag_Profile_IM_Nick");
                    this.put("Value",nick);
                }});
                this.add(new HashMap(){{
                    this.put("Tag","Tag_Profile_IM_Image");
                    this.put("Value",faceUrl);
                }});
            }});
        }})),HashMap.class);
        if(!response.get("ActionStatus").equals("OK")){
            throw new SystemException("腾讯云通信获取账号资料异常,ErrorCode:[ " + response.get("ErrorCode") + " ]" + ",ErrorInfo: [" + response.get("ErrorInfo") + " ]");
        }
    }

    /**
     * 获取用户信息
     * @param identifier 用户id
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws KeyManagementException
     */
    public Map getImUserInfo(String identifier) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        Map response = this.objectMapper.readValue(this.httpClient.doPost(String.format("https://console.tim.qq.com/v4/profile/portrait_get?usersig=%s&identifier=%s&sdkappid=%s&random=%s&contenttype=json",this.getImManagerSig(),Configuration.property(Configuration.TENCENT_IM_ACCOUNT_MANAGER),Configuration.property(Configuration.TENCENT_IM_SDK_APP_ID),new Date().getTime()),this.objectMapper.writeValueAsString(new HashMap(){{
            this.put("To_Account",new ArrayList(){{
                this.add(identifier);
            }});
            this.put("TagList",new ArrayList(){{
                this.add("Tag_Profile_IM_Nick");
                this.add("Tag_Profile_IM_Image");
            }});
        }})),HashMap.class);
        if(!response.get("ActionStatus").equals("OK")){
            if(((int)response.get("ErrorCode")) == 40003){
                return null;
            }
            throw new SystemException("腾讯云通信获取账号资料异常,ErrorCode:[ " + response.get("ErrorCode") + " ]" + ",ErrorInfo: [" + response.get("ErrorInfo") + " ]");
        }
        return response;
    }

    /**
     * QQ登录
     * @param accessToken
     * @return
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws IOException
     */
    public Auth getOpenId(String accessToken) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
        String response = this.httpClient.doGet(String.format("https://graph.qq.com/oauth2.0/me?access_token=%s",accessToken));
        response = response.substring(response.indexOf("(") + 1,response.lastIndexOf(")"));
        Map result = this.objectMapper.readValue(response,HashMap.class);
        if(result.get("error") != null){
            throw new SystemException("QQ授权异常,error [ " + result.get("error") + " ] error_description [ " + result.get("error_description") + " ]");
        }
        if(!result.get("client_id").equals(Configuration.property(Configuration.TENCENT_QQ_APP_ID))){
            throw new SystemException("nonsupport appid");
        }
        Auth auth = new Auth();
        auth.openid = (String)result.get("openid");
        auth.client_id = (String)result.get("client_id");
        return auth;
    }

    /**
     * QQ登录
     * @param openid
     * @param accessToken
     * @return
     * @throws KeyManagementException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws IOException
     */
    public User getUserInfo(String openid, String accessToken) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
        Map response = this.objectMapper.readValue(this.httpClient.doGet(String.format("https://graph.qq.com/user/get_user_info?access_token=%s&openid=%s&oauth_consumer_key=%s",accessToken,openid,Configuration.property(Configuration.TENCENT_QQ_APP_ID))),HashMap.class);
        if(((int)response.get("ret")) != 0){
            throw new SystemException("QQ授权异常,ret [ " + response.get("ret") + " ] msg [ " + response.get("msg") +" ]");
        }
        User user = new User();
        user.nickname = (String)response.get("nickname");
        user.figureurl_qq_1 = (String)response.get("figureurl_qq_1");
        user.figureurl_qq_2 = (String)response.get("figureurl_qq_2");
        return user;
    }

    /**
     * 通过用户id获取签名
     * @param identifier
     * @return
     * @throws IOException
     */
    public String getImUserSig(String identifier) throws IOException {
        GenTLSSignatureResult genTLSSignatureResult = GenTLSSignatureEx(Long.valueOf(Configuration.property(Configuration.TENCENT_IM_SDK_APP_ID)), identifier, Configuration.property(Configuration.TENCENT_IM_PRIVATE_KEY));
        return genTLSSignatureResult.urlSig;
    }

    /**
     * 获取管理员账号签名
     * @return
     * @throws IOException
     */
    public String getImManagerSig() throws IOException {
        if(StringUtils.isBlank(this.imManagerSig)){
            this.imManagerSig = this.getImUserSig(Configuration.property(Configuration.TENCENT_IM_ACCOUNT_MANAGER));
        }
        return this.imManagerSig;
    }

    /**
     * 注册用户
     * @param identifier
     * @param nick
     * @param faceUrl
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws KeyManagementException
     */
    public void registerImUser(String identifier,String nick,String faceUrl) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        Map response = this.objectMapper.readValue(this.httpClient.doPost(String.format("https://console.tim.qq.com/v4/im_open_login_svc/account_import?usersig=%s&identifier=%s&sdkappid=%s&random=%s&contenttype=json",this.getImManagerSig(),Configuration.property(Configuration.TENCENT_IM_ACCOUNT_MANAGER),Configuration.property(Configuration.TENCENT_IM_SDK_APP_ID),new Date().getTime()),this.objectMapper.writeValueAsString(new HashMap(){{
            this.put("Identifier",identifier);
            this.put("Nick",nick);
            this.put("FaceUrl",faceUrl);
            this.put("Type",0);
        }})),HashMap.class);
        if(!response.get("ActionStatus").equals("OK")){
            throw new SystemException("腾讯云通信注册账号异常,ErrorCode:[ " + response.get("ErrorCode") + " ]" + ",ErrorInfo: [" + response.get("ErrorInfo") + " ]");
        }
    }

    /**
     * 发送消息
     * @param fromAccount
     * @param toAccount
     * @param text
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws KeyManagementException
     */
    public void imSendTIMTextElemMsg(String fromAccount,String toAccount,String text) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        Map response = this.objectMapper.readValue(this.httpClient.doPost(String.format("https://console.tim.qq.com/v4/openim/sendmsg?usersig=%s&identifier=%s&sdkappid=%s&random=%s&contenttype=json",this.getImManagerSig(),Configuration.property(Configuration.TENCENT_IM_ACCOUNT_MANAGER),Configuration.property(Configuration.TENCENT_IM_SDK_APP_ID),new Date().getTime()),this.objectMapper.writeValueAsString(new HashMap(){{
            this.put("SyncOtherMachine",1);
            if(!StringUtils.isBlank(fromAccount)){
                this.put("From_Account",fromAccount);
            }
            this.put("To_Account",toAccount);
            this.put("MsgRandom",new Date().getTime() / 1000);
            this.put("MsgBody",new ArrayList(){{
                this.add(new HashMap(){{
                    this.put("MsgType","TIMTextElem");
                    this.put("MsgContent",new HashMap(){{
                        this.put("Text",text);
                    }});
                }});
            }});
        }})),HashMap.class);
        if(!response.get("ActionStatus").equals("OK")){
            throw new SystemException("腾讯云通信发送消息异常,ErrorCode:[ " + response.get("ErrorCode") + " ]" + ",ErrorInfo: [" + response.get("ErrorInfo") + " ]");
        }
    }

    public final class Auth {
        String openid;
        String client_id;
    }

    public final class User{
        String nickname;
        String figureurl_qq_1;
        String figureurl_qq_2;
    }




    public static class GenTLSSignatureResult
    {
        public String errMessage;
        public String urlSig;
        public int expireTime;
        public int initTime;
        public GenTLSSignatureResult()
        {
            errMessage = "";
            urlSig = "";
        }
    }

    public static class CheckTLSSignatureResult
    {
        public String errMessage;
        public boolean verifyResult;
        public int expireTime;
        public int initTime;
        public CheckTLSSignatureResult()
        {
            errMessage = "";
            verifyResult = false;
        }
    }

    /*public static void main(String[] args) {
        try{
            //Use pemfile keys to test
            String privStr = "-----BEGIN PRIVATE KEY-----\n" +
                    "MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgiBPYMVTjspLfqoq46oZd\n" +
                    "j9A0C8p7aK3Fi6/4zLugCkehRANCAATU49QhsAEVfIVJUmB6SpUC6BPaku1g/dzn\n" +
                    "0Nl7iIY7W7g2FoANWnoF51eEUb6lcZ3gzfgg8VFGTpJriwHQWf5T\n" +
                    "-----END PRIVATE KEY-----";

            //change public pem string to public string
            String pubStr = "-----BEGIN PUBLIC KEY-----\n"+
                    "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1OPUIbABFXyFSVJgekqVAugT2pLtYP3c\n"+
                    "59DZe4iGO1u4NhaADVp6BedXhFG+pXGd4M34IPFRRk6Sa4sB0Fn+Uw==\n"+
                    "-----END PUBLIC KEY-----";

            // generate signature
            GenTLSSignatureResult result = GenTLSSignatureEx(1400000955, "xiaojun", privStr);
            if (0 == result.urlSig.length()) {
                System.out.println("GenTLSSignatureEx failed: " + result.errMessage);
                return;
            }

            System.out.println("---\ngenerate sig:\n" + result.urlSig + "\n---\n");

            // check signature
            CheckTLSSignatureResult checkResult = CheckTLSSignatureEx(result.urlSig, 1400000955, "xiaojun", pubStr);
            if(checkResult.verifyResult == false) {
                System.out.println("CheckTLSSignature failed: " + result.errMessage);
                return;
            }

            System.out.println("\n---\ncheck sig ok -- expire time " + checkResult.expireTime + " -- init time " + checkResult.initTime + "\n---\n");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }*/


    /**
     * @brief 生成 tls 票据
     * @param expire 有效期,单位是秒,推荐一个月
     * @param strAppid3rd 填写与 sdkAppid 一致字符串形式的值
     * @param skdAppid 应用的 appid
     * @param identifier 用户 id
     * @param accountType 创建应用后在配置页面上展示的 acctype
     * @param privStr 生成 tls 票据使用的私钥内容
     * @return 如果出错,GenTLSSignatureResult 中的 urlSig为空,errMsg 为出错信息,成功返回有效的票据
     * @throws IOException
     */
    @Deprecated
    public static GenTLSSignatureResult GenTLSSignature(long expire,
                                                        String strAppid3rd, long skdAppid,
                                                        String identifier, long accountType,
                                                        String privStr ) throws IOException
    {

        GenTLSSignatureResult result = new GenTLSSignatureResult();

        Security.addProvider(new BouncyCastleProvider());
        Reader reader = new CharArrayReader(privStr.toCharArray());
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PEMParser parser = new PEMParser(reader);
        Object obj = parser.readObject();
        parser.close();
        PrivateKey privKeyStruct = converter.getPrivateKey((PrivateKeyInfo) obj);

        //Create Json string and serialization String
        String jsonString = "{"
                + "\"TLS.account_type\":\"" + accountType +"\","
                +"\"TLS.identifier\":\"" + identifier +"\","
                +"\"TLS.appid_at_3rd\":\"" + strAppid3rd +"\","
                +"\"TLS.sdk_appid\":\"" + skdAppid +"\","
                +"\"TLS.expire_after\":\"" + expire +"\""
                +"}";
        //System.out.println("#jsonString : \n" + jsonString);

        String time = String.valueOf(System.currentTimeMillis()/1000);
        String SerialString =
                "TLS.appid_at_3rd:" + strAppid3rd + "\n" +
                        "TLS.account_type:" + accountType + "\n" +
                        "TLS.identifier:" + identifier + "\n" +
                        "TLS.sdk_appid:" + skdAppid + "\n" +
                        "TLS.time:" + time + "\n" +
                        "TLS.expire_after:" + expire +"\n";


        //System.out.println("#SerialString : \n" + SerialString);
        //System.out.println("#SerialString Hex: \n" + Hex.encodeHexString(SerialString.getBytes()));

        try{
            //Create Signature by SerialString
            Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
            signature.initSign(privKeyStruct);
            signature.update(SerialString.getBytes(Charset.forName("UTF-8")));
            byte[] signatureBytes = signature.sign();

            String sigTLS = Base64.encodeBase64String(signatureBytes);
            //System.out.println("#sigTLS : " + sigTLS);

            //Add TlsSig to jsonString
            JSONObject jsonObject= new JSONObject(jsonString);
            jsonObject.put("TLS.sig", sigTLS);
            jsonObject.put("TLS.time", time);
            jsonString = jsonObject.toString();

            // System.out.println("#jsonString : \n" + jsonString);

            //compression
            Deflater compresser = new Deflater();
            compresser.setInput(jsonString.getBytes(Charset.forName("UTF-8")));

            compresser.finish();
            byte [] compressBytes = new byte [512];
            int compressBytesLength = compresser.deflate(compressBytes);
            compresser.end();
            //System.out.println("#compressBytes "+ compressBytesLength+": " + Hex.encodeHexString(Arrays.copyOfRange(compressBytes,0,compressBytesLength)));

            //String userSig = Base64.encodeBase64URLSafeString(Arrays.copyOfRange(compressBytes,0,compressBytesLength));
            String userSig = new String(base64_url.base64EncodeUrl(Arrays.copyOfRange(compressBytes,0,compressBytesLength)));

            result.urlSig = userSig;
            //System.out.println("urlSig: "+ userSig);
        }
        catch(Exception e)
        {
            e.printStackTrace();
            result.errMessage = "generate usersig failed";
        }

        return result;
    }

    /**
     * @brief 校验 tls 票据
     * @param urlSig 返回 tls 票据
     * @param strAppid3rd 填写与 sdkAppid 一致的字符串形式的值
     * @param skdAppid 应的 appid
     * @param identifier 用户 id
     * @param accountType 创建应用后在配置页面上展示的 acctype
     * @param publicKey 用于校验 tls 票据的公钥内容,但是需要先将公钥文件转换为 java 原生 api 使用的格式,下面是推荐的命令
     *         openssl pkcs8 -topk8 -in ec_key.pem -outform PEM -out p8_priv.pem -nocrypt
     * @return 如果出错 CheckTLSSignatureResult 中的 verifyResult 为 false,错误信息在 errMsg,校验成功为 true
     * @throws DataFormatException
     */
    @Deprecated
    public static CheckTLSSignatureResult CheckTLSSignature( String urlSig,
                                                             String strAppid3rd, long skdAppid,
                                                             String identifier, long accountType,
                                                             String publicKey ) throws DataFormatException
    {
        CheckTLSSignatureResult result = new CheckTLSSignatureResult();
        Security.addProvider(new BouncyCastleProvider());

        //DeBaseUrl64 urlSig to json
        Base64 decoder = new Base64();

        //byte [] compressBytes = decoder.decode(urlSig.getBytes());
        byte [] compressBytes = base64_url.base64DecodeUrl(urlSig.getBytes(Charset.forName("UTF-8")));

        //System.out.println("#compressBytes Passing in[" + compressBytes.length + "] " + Hex.encodeHexString(compressBytes));

        //Decompression
        Inflater decompression =  new Inflater();
        decompression.setInput(compressBytes, 0, compressBytes.length);
        byte [] decompressBytes = new byte [1024];
        int decompressLength = decompression.inflate(decompressBytes);
        decompression.end();

        String jsonString = new String(Arrays.copyOfRange(decompressBytes, 0, decompressLength));

        //System.out.println("#Json String passing in : \n" + jsonString);

        //Get TLS.Sig from json
        JSONObject jsonObject= new JSONObject(jsonString);
        String sigTLS = jsonObject.getString("TLS.sig");

        //debase64 TLS.Sig to get serailString
        byte[] signatureBytes = decoder.decode(sigTLS.getBytes(Charset.forName("UTF-8")));

        try{

            String sigTime = jsonObject.getString("TLS.time");
            String sigExpire = jsonObject.getString("TLS.expire_after");

            //checkTime
            //System.out.println("#time check: "+ System.currentTimeMillis()/1000 + "-"
            //+ Long.parseLong(sigTime) + "-" + Long.parseLong(sigExpire));
            if( System.currentTimeMillis()/1000 - Long.parseLong(sigTime) > Long.parseLong(sigExpire))
            {
                result.errMessage = new String("TLS sig is out of date ");
                System.out.println("Timeout");
                return result;
            }

            //Get Serial String from json
            String SerialString =
                    "TLS.appid_at_3rd:" + strAppid3rd + "\n" +
                            "TLS.account_type:" + accountType + "\n" +
                            "TLS.identifier:" + identifier + "\n" +
                            "TLS.sdk_appid:" + skdAppid + "\n" +
                            "TLS.time:" + sigTime + "\n" +
                            "TLS.expire_after:" + sigExpire + "\n";

            //System.out.println("#SerialString : \n" + SerialString);

            Reader reader = new CharArrayReader(publicKey.toCharArray());
            PEMParser  parser = new PEMParser(reader);
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
            Object obj = parser.readObject();
            parser.close();
            PublicKey pubKeyStruct  = converter.getPublicKey((SubjectPublicKeyInfo) obj);

            Signature signature = Signature.getInstance("SHA256withECDSA","BC");
            signature.initVerify(pubKeyStruct);
            signature.update(SerialString.getBytes(Charset.forName("UTF-8")));
            boolean bool = signature.verify(signatureBytes);
            //System.out.println("#jdk ecdsa verify : " + bool);
            result.verifyResult = bool;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            result.errMessage = "Failed in checking sig";
        }

        return result;
    }

    /**
     * @brief 生成 tls 票据,精简参数列表,有效期默认为 180 天
     * @param skdAppid 应用的 sdkappid
     * @param identifier 用户 id
     * @param privStr 私钥文件内容
     * @return
     * @throws IOException
     */
    public static GenTLSSignatureResult GenTLSSignatureEx(
            long skdAppid,
            String identifier,
            String privStr) throws IOException {
        return GenTLSSignatureEx(skdAppid, identifier, privStr, 3600*24*180);
    }

    /**
     * @brief 生成 tls 票据,精简参数列表
     * @param skdAppid 应用的 sdkappid
     * @param identifier 用户 id
     * @param privStr 私钥文件内容
     * @param expire 有效期,以秒为单位,推荐时长一个月
     * @return
     * @throws IOException
     */
    public static GenTLSSignatureResult GenTLSSignatureEx(
            long skdAppid,
            String identifier,
            String privStr,
            long expire) throws IOException {

        GenTLSSignatureResult result = new GenTLSSignatureResult();

        Security.addProvider(new BouncyCastleProvider());
        Reader reader = new CharArrayReader(privStr.toCharArray());
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        PEMParser parser = new PEMParser(reader);
        Object obj = parser.readObject();
        parser.close();
        PrivateKey privKeyStruct = converter.getPrivateKey((PrivateKeyInfo) obj);

        String jsonString = "{"
                + "\"TLS.account_type\":\"" + 0 +"\","
                +"\"TLS.identifier\":\"" + identifier +"\","
                +"\"TLS.appid_at_3rd\":\"" + 0 +"\","
                +"\"TLS.sdk_appid\":\"" + skdAppid +"\","
                +"\"TLS.expire_after\":\"" + expire +"\","
                +"\"TLS.version\": \"201512300000\""
                +"}";

        String time = String.valueOf(System.currentTimeMillis()/1000);
        String SerialString =
                "TLS.appid_at_3rd:" + 0 + "\n" +
                        "TLS.account_type:" + 0 + "\n" +
                        "TLS.identifier:" + identifier + "\n" +
                        "TLS.sdk_appid:" + skdAppid + "\n" +
                        "TLS.time:" + time + "\n" +
                        "TLS.expire_after:" + expire +"\n";

        try {
            //Create Signature by SerialString
            Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
            signature.initSign(privKeyStruct);
            signature.update(SerialString.getBytes(Charset.forName("UTF-8")));
            byte[] signatureBytes = signature.sign();

            String sigTLS = Base64.encodeBase64String(signatureBytes);

            //Add TlsSig to jsonString
            JSONObject jsonObject= new JSONObject(jsonString);
            jsonObject.put("TLS.sig", sigTLS);
            jsonObject.put("TLS.time", time);
            jsonString = jsonObject.toString();

            //compression
            Deflater compresser = new Deflater();
            compresser.setInput(jsonString.getBytes(Charset.forName("UTF-8")));

            compresser.finish();
            byte [] compressBytes = new byte [512];
            int compressBytesLength = compresser.deflate(compressBytes);
            compresser.end();
            String userSig = new String(base64_url.base64EncodeUrl(Arrays.copyOfRange(compressBytes,0,compressBytesLength)));

            result.urlSig = userSig;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            result.errMessage = "generate usersig failed";
        }

        return result;
    }

    public static CheckTLSSignatureResult CheckTLSSignatureEx(
            String urlSig,
            long sdkAppid,
            String identifier,
            String publicKey ) throws DataFormatException {

        CheckTLSSignatureResult result = new CheckTLSSignatureResult();
        Security.addProvider(new BouncyCastleProvider());

        //DeBaseUrl64 urlSig to json
        Base64 decoder = new Base64();
        byte [] compressBytes = base64_url.base64DecodeUrl(urlSig.getBytes(Charset.forName("UTF-8")));

        //Decompression
        Inflater decompression =  new Inflater();
        decompression.setInput(compressBytes, 0, compressBytes.length);
        byte [] decompressBytes = new byte [1024];
        int decompressLength = decompression.inflate(decompressBytes);
        decompression.end();

        String jsonString = new String(Arrays.copyOfRange(decompressBytes, 0, decompressLength));

        //Get TLS.Sig from json
        JSONObject jsonObject= new JSONObject(jsonString);
        String sigTLS = jsonObject.getString("TLS.sig");

        //debase64 TLS.Sig to get serailString
        byte[] signatureBytes = decoder.decode(sigTLS.getBytes(Charset.forName("UTF-8")));

        try {
            String strSdkAppid = jsonObject.getString("TLS.sdk_appid");
            String sigTime = jsonObject.getString("TLS.time");
            String sigExpire = jsonObject.getString("TLS.expire_after");

            if (Integer.parseInt(strSdkAppid) != sdkAppid)
            {
                result.errMessage = new String( "sdkappid "
                        + strSdkAppid
                        + " in tls sig not equal sdkappid "
                        + sdkAppid
                        + " in request");
                return result;
            }

            if ( System.currentTimeMillis()/1000 - Long.parseLong(sigTime) > Long.parseLong(sigExpire)) {
                result.errMessage = new String("TLS sig is out of date");
                return result;
            }

            //Get Serial String from json
            String SerialString =
                    "TLS.appid_at_3rd:" + 0 + "\n" +
                            "TLS.account_type:" + 0 + "\n" +
                            "TLS.identifier:" + identifier + "\n" +
                            "TLS.sdk_appid:" + sdkAppid + "\n" +
                            "TLS.time:" + sigTime + "\n" +
                            "TLS.expire_after:" + sigExpire + "\n";

            Reader reader = new CharArrayReader(publicKey.toCharArray());
            PEMParser  parser = new PEMParser(reader);
            JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
            Object obj = parser.readObject();
            parser.close();
            PublicKey pubKeyStruct  = converter.getPublicKey((SubjectPublicKeyInfo) obj);

            Signature signature = Signature.getInstance("SHA256withECDSA","BC");
            signature.initVerify(pubKeyStruct);
            signature.update(SerialString.getBytes(Charset.forName("UTF-8")));
            boolean bool = signature.verify(signatureBytes);
            result.expireTime = Integer.parseInt(sigExpire);
            result.initTime = Integer.parseInt(sigTime);
            result.verifyResult = bool;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            result.errMessage = "Failed in checking sig";
        }

        return result;
    }

    public static class base64_url {
        static  byte base64_table_url[] =
                { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '-', '\0'};

        static  byte base64_pad_url = '_';

        static  short base64_reverse_table_url[] = {
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 63, -1, -1,
                52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
                -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
                15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
                -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
                41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
        };

        public static int unsignedToBytes(int b)
        {
            return b & 0xFF;
        }

        //int base64_encode_url(const unsigned char *in_str, int length, char *out_str,int *ret_length)
        public static byte [] base64EncodeUrl(byte [] in_str)
        {
            byte [] out_str = new byte [1024] ;

            int out_current = 0;
            int current = 0;
            int length = in_str.length;

            while (length > 2) { /* keep going until we have less than 24 bits */

                out_str[out_current++] = base64_table_url[unsignedToBytes((unsignedToBytes(in_str[current]) >>> 2))];
                out_str[out_current++] = base64_table_url[unsignedToBytes(unsignedToBytes(unsignedToBytes(in_str[current]) & 0x03) << 4) + unsignedToBytes((unsignedToBytes(in_str[current+1]) >>> 4))];
                out_str[out_current++] = base64_table_url[(unsignedToBytes((unsignedToBytes(in_str[current+1]) & 0x0f)) << 2) + unsignedToBytes((unsignedToBytes(in_str[current+2]) >>> 6))];
                out_str[out_current++] = base64_table_url[unsignedToBytes((unsignedToBytes(in_str[current+2]) & 0x3f))];
                current += 3;
                length -= 3; /* we just handle 3 octets of data */
            }

            /* now deal with the tail end of things */
            if (length != 0) {
                out_str[out_current++] = base64_table_url[unsignedToBytes(in_str[current]) >>> 2];
                if (length > 1) {
                    out_str[out_current++] = base64_table_url[unsignedToBytes((unsignedToBytes(in_str[current]) & 0x03) << 4) + unsignedToBytes(unsignedToBytes(in_str[current+1]) >>> 4)];
                    out_str[out_current++] = base64_table_url[unsignedToBytes((unsignedToBytes(in_str[current + 1]) & 0x0f) << 2)];
                    out_str[out_current++] = base64_pad_url;
                } else {
                    out_str[out_current++] = base64_table_url[unsignedToBytes((unsignedToBytes(in_str[current]) & 0x03) << 4)];
                    out_str[out_current++] = base64_pad_url;
                    out_str[out_current++] = base64_pad_url;
                }
            }

            //System.out.println("length in base64EncodeUrl: " + out_current );
            byte [] out_bytes = new String(out_str).getBytes();
            return Arrays.copyOfRange(out_bytes, 0, out_current);
        }

        //int base64_decode_url(const unsigned char *in_str, int length, char *out_str, int *ret_length)
        public static byte [] base64DecodeUrl(byte [] in_str)
        {
            //        const unsigned char *current = in_str;
            int ch, i = 0, j = 0, k;

            int current  = 0;
            int result = 0;
            byte [] out_str = new byte [1024] ;
            int length = in_str.length;
            /* this sucks for threaded environments */

            /* run through the whole string, converting as we go */
            //while ((ch = in_str[current++]) != '\0' && length-- > 0) {
            ch = in_str[0];
            while(length-- > 0){
                ch = in_str[current++];
                if (ch == base64_pad_url) break;
                    /* When Base64 gets POSTed, all pluses are interpreted as spaces.
                       This line changes them back.  It's not exactly the Base64 spec,
                       but it is completely compatible with it (the spec says that
                       spaces are invalid).  This will also save many people considerable
                       headache.  - Turadg Aleahmad <turadg@wise.berkeley.edu>
                */
                if (ch == ' ') ch = '*'; //never using '+'

                ch = base64_reverse_table_url[ch];
                if (ch < 0) continue;

                switch(i % 4) {
                    case 0:
                        out_str[j] = (byte) unsignedToBytes( unsignedToBytes(ch) << 2);
                        break;
                    case 1:
                        out_str[j++] |= (byte) unsignedToBytes(unsignedToBytes(ch) >>> 4);
                        out_str[j] = (byte) unsignedToBytes(unsignedToBytes(unsignedToBytes(ch) & 0x0f) << 4);
                        break;
                    case 2:
                        out_str[j++] |= (byte) unsignedToBytes(unsignedToBytes(ch) >>> 2);
                        out_str[j] = (byte) unsignedToBytes(unsignedToBytes(unsignedToBytes(ch) & 0x03) << 6);
                        break;
                    case 3:
                        out_str[j++] |= (byte) unsignedToBytes(ch);
                        break;
                }
                i++;
            }
            k = j;
            /* mop things up if we ended on a boundary */
            if (ch == base64_pad_url) {
                switch(i % 4) {
                    case 0:
                    case 1:
                        byte [] error =  new byte [1];
                        error[0] = '\0';
                        return error;
                    case 2:
                        k++;
                    case 3:
                        out_str[k++] = 0;
                }
            }
            return Arrays.copyOfRange(out_str, 0, j);
        }
    }
}

参数补充

public static final String TENCENT_IM_ACCOUNT_MANAGER = "tencent.im.accountManager";
tencent.im.accountManager=spring
public static final String TENCENT_IM_SDK_APP_ID = "tencent.im.sdkAppId";
tencent.im.sdkAppId=***
public static final String TENCENT_QQ_APP_ID = "tencent.qq.appId";
tencent.qq.appId=***
public static final String TENCENT_IM_SDK_APP_ID = "tencent.im.sdkAppId";
tencent.im.sdkAppId=***
public static final String TENCENT_IM_PRIVATE_KEY = "tencent.im.privateKey";
//注意私钥书写格式,换行要用\n
tencent.im.privateKey=-----BEGIN PRIVATE KEY-----\n***\n***\n***\n-----END PRIVATE KEY-----
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值