• 吐血记录微信小程序授权获取Unionid及linux下使用bouncycastle解密用户数据 遇到的坑


    背景

    公司小程序上线了,发现系统无法拿到一些用户的UniondID。但是上线前的测试一切都是正常的。

    坑1

    经排查,发现一些用户通过下面的接口无法得到unionid

    https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

    阅读https://developers.weixin.qq.com/miniprogram/dev/api/uinionID.html 得知,从未在关联公众号或小程序进行授权过的用户,是不会直接返回unionid的。要拿到这些用户的unionid需要以下3个数据

    1.https://api.weixin.qq.com/sns/jscode2session返回的session_key

    2. wx.getUserInfo且用户同意后返回的encryptedData和iv

    使用以下代码可以解密出用户的信息

    
    
    import org.apache.commons.codec.binary.Base64;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import java.io.UnsupportedEncodingException;
    import java.security.AlgorithmParameters;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.Key;
    import java.security.NoSuchProviderException;
    import java.security.Security;
    
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    
    
    public class AES {
        public static boolean initialized = false;
    
        public static void main(String[] args) throws InvalidAlgorithmParameterException, UnsupportedEncodingException {
            String encryptedData = "";
            String iv = "";
            String sessionKey = "";
    
            byte[] resultByte = AES.decrypt(Base64.decodeBase64(encryptedData),
                    Base64.decodeBase64(sessionKey),
                    Base64.decodeBase64(iv));
            System.out.println(new String(resultByte,"utf-8"));
        }
    
        /**
         * AES解密
         *
         * @param content 密文
         * @return
         * @throws InvalidAlgorithmParameterException
         * @throws NoSuchProviderException
         */
        public static byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
            initialize();
            try {
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    
                Key sKeySpec = new SecretKeySpec(keyByte, "AES");
    
    
                cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
    
                byte[] result = cipher.doFinal(content);
    
                return result;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static void initialize() {
            if (initialized) return;
            Security.addProvider(new BouncyCastleProvider());
            initialized = true;
        }
    
        //生成iv      
        public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
            AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
            params.init(new IvParameterSpec(iv));
            return params;
        }
    
    
    }  

    坑2,真正吐血的地方

    使用以上的方法在我本机上面进行测试是没问题的,但是将项目部署上Linux上之后又出了一个问题

    解密的时候抛异常:

    java.security.NoSuchAlgorithmException - Cannot find any provider supporting AES/CBC/PKCS7Padding

    我回头看代码,要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现,解密代码中确实也设置了。且本机也是可以解密的,本机和linux上的jdk均是官网下载的1.8版本,为什么Linux上就不行呢

    //使用BouncyCastleProvider组件填充
    Security.addProvider(new BouncyCastleProvider());

    maven中也引用了bouncycastle

    
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15on</artifactId>
                <version>1.59</version>
            </dependency>

    排除代码问题的话很明显是环境问题了,linux上使用Security.addProvider(new BouncyCastleProvider());  不起效果。。。

    那没办法了,只能手动改jre。步骤如下

    1.把bcprov-jdk15on-1.59.jar 复制到jre目录中的/lib/ext

    2.编辑/lib/security/java.security 

    ....
    security.provider.1=sun.security.provider.Sun
    security.provider.2=sun.security.rsa.SunRsaSign
    security.provider.3=sun.security.ec.SunEC
    security.provider.4=com.sun.net.ssl.internal.ssl.Provider
    security.provider.5=com.sun.crypto.provider.SunJCE
    security.provider.6=sun.security.jgss.SunProvider
    security.provider.7=com.sun.security.sasl.Provider
    security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
    security.provider.9=sun.security.smartcardio.SunPCSC
    
    #在此加上这句代码
    security.provider.x=org.bouncycastle.jce.provider.BouncyCastleProvider
    
    
    .....

    3.重启tomcat,解密成功了。。。

    附一份检测是否支持bouncycastle的代码。方法:

    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import javax.crypto.Cipher;
    import java.security.Security;
    public class TestB {
    
        public static void main(String[] args) throws Exception {
            Security.addProvider(new BouncyCastleProvider());
    
            System.out.println("Attemptingto get a Cipher and encrypt...");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            System.out.println("OK");
        }
    }

    如果想用纯Js解密也是可以的,这里有一篇文章:https://www.cnblogs.com/cai-rd/p/6816849.html

  • 相关阅读:
    Block编程
    自己写Web服务器(续)
    C# 2.0对现有语法的改进
    使用CDN
    优化网站设计(一):减少请求数
    MongoDB Shell的使用
    memcache 和appfabric
    go语言中几个有趣的特性以及对go的看法
    bpm流程平台
    Socket编程 (异步通讯) (Tcp,Udp)Part2
  • 原文地址:https://www.cnblogs.com/valu/p/10699760.html
Copyright © 2020-2023  润新知