android(java) SM3,SM4国密算法踩坑总结

好久没在博客园写随笔了,来说说我最近在做的人脸支付使用国密算法加密时遇到的一些坑。

SM4加密第一步,生成"BC"provider,"SM4"算法的key

 1 public static String generateKey() {
 2         try {
 3             //获取到当前系统中的 提供者 和提供者支持的算法。
 4             /*Provider[] providers = Security.getProviders();
 5             for (Provider provider2 : providers) {
 6                 System.err.println(provider2);
 7                 Set<Map.Entry<Object, Object>> entrySet = provider2.entrySet();
 8                 for (Map.Entry<Object, Object> entry : entrySet) {
 9                     System.out.println(entry.getKey() +"  "+ entry.getValue());
10                 }
11             }*/
12             KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
13             kg.init(64, new SecureRandom());
14             return new String(Hex.encodeHex(kg.generateKey().getEncoded())).toUpperCase();
15         } catch (Exception e) {
16             e.printStackTrace();
17         }
18         return null;
19     }

运行这个方法,我遇到的第一个坑:

java.security.NoSuchProviderException No such provider: BC

此异常我通过各种博客,github问一些开源作者,也都是给出这样的解决办法

java的话在static块中添加

1     static {
2         Security.addProvider(new BouncyCastleProvider());
3     }

android的话在加密方法调用前使用这句话即可。

然而我再次运行,抛出另一个algorithmexception:no such algorithm: SM4 for provider BC

研究了两天,看了BCProvider类的源码,无果,误打误撞想着从当前系统remove掉BCProvider会怎么样,居然解决了,代码如下:

1     Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
2         if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){
3             Log.i("sys","运行环境没有BouncyCastleProvider");
4             Security.addProvider(new BouncyCastleProvider());
5         }
6         String semKey = SM4Util.generateKey();

然后就想为啥这样子能解决,会不会在我android应用启动的时候已经加载了BCProvider,但是版本略低呢!

果不其然,我打印了在我添加BCProvider之前那个假“BCprovider”的版本

1     if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) != null){
2             double version = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME).getVersion();
3             Log.i("sys","原有version="+version);
4         }

而我使用的下载的jar包是jdk_15on_160的

这便证实了我的想法,后来我去请教了大佬,结论如下

好了,问题至此解决,下面贴出android和java国密算法的代码

android:

 1 public class SM3Util {
 2 
 3 
 4     public static String hash(String data, String encoding) throws Exception {
 5         SM3Digest digest = new SM3Digest();
 6         byte[] source = data.getBytes(encoding);
 7         digest.update(source, 0, source.length);
 8         byte[] update = new byte[digest.getDigestSize()];
 9         digest.doFinal(update, 0);
10         return ByteUtils.toHexString(update).toUpperCase();
11     }
12 
13     public static byte[] byteHash(String data, String encoding) throws Exception {
14         SM3Digest digest = new SM3Digest();
15         byte[] source = data.getBytes(encoding);
16         digest.update(source, 0, source.length);
17         byte[] update = new byte[digest.getDigestSize()];
18         digest.doFinal(update, 0);
19         return update;
20     }
21 
22 }
  1 public class SM4Util {
  2 
  3     public static final int KEY_SIZE = 128;
  4 
  5     public static final String ALGORITHM = "SM4";
  6 
  7     public static final String IV = "根据自己的项目定义";
  8 
  9     public static final String ALGORITHM_ECB_PADDING = "SM4/ECB/PKCS7Padding";
 10 
 11     public static final String ALGORITHM_CBC_PADDING = "SM4/CBC/PKCS7Padding";
 12 
 13 
 14     public static byte[] encryptECBToByte(byte[] data, String keyStr) {
 15         try {
 16             Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
 17             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
 18             cipher.init(Cipher.ENCRYPT_MODE, key);
 19             byte[] enBytes = cipher.doFinal(data);
 20             return enBytes;
 21         } catch (Exception e) {
 22             e.printStackTrace();
 23         }
 24         return null;
 25     }
 26 
 27     public static String encryptECB(String data, String keyStr, String encoding) {
 28         try {
 29             Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
 30             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
 31             cipher.init(Cipher.ENCRYPT_MODE, key);
 32             byte[] enBytes = cipher.doFinal(data.getBytes(encoding));
 33             Base64Encoder base64Encoder = new Base64Encoder();
 34             return base64Encoder.encode(enBytes);
 35         } catch (Exception e) {
 36             e.printStackTrace();
 37         }
 38         return null;
 39     }
 40 
 41     public static byte[] decryptECBToByte(byte[] data, String keyStr) {
 42         try {
 43             Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
 44             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
 45             cipher.init(Cipher.DECRYPT_MODE, key);
 46             byte[] deBytes = cipher.doFinal(data);
 47             return deBytes;
 48         } catch (Exception e) {
 49             e.printStackTrace();
 50         }
 51         return null;
 52     }
 53 
 54     public static String decryptECB(String data, String keyStr, String encoding) {
 55         try {
 56             Cipher cipher = Cipher.getInstance(ALGORITHM_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
 57             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
 58             cipher.init(Cipher.DECRYPT_MODE, key);
 59 //            byte[] deBytes = cipher.doFinal(Base64.decodeBase64(data));
 60             Base64Encoder base64Encoder = new Base64Encoder();
 61             byte[] deBytes = cipher.doFinal(base64Encoder.decode(data));
 62             return new String(deBytes, encoding);
 63         } catch (Exception e) {
 64             e.printStackTrace();
 65         }
 66         return null;
 67     }
 68 
 69     public static byte[] encryptCBCToByte(byte[] data, String keyStr) {
 70         try {
 71             Cipher cipher = Cipher.getInstance(ALGORITHM_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
 72             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
 73             IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
 74             AlgorithmParameterSpec paramSpec = ivSpec;
 75             cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
 76             byte[] enBytes = cipher.doFinal(data);
 77             return enBytes;
 78         } catch (Exception e) {
 79             e.printStackTrace();
 80         }
 81         return null;
 82     }
 83 
 84     public static String encryptCBC(String data, String keyStr, String encoding) {
 85         try {
 86             Cipher cipher = Cipher.getInstance(ALGORITHM_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
 87             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
 88             IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
 89             AlgorithmParameterSpec paramSpec = ivSpec;
 90             cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
 91             byte[] enBytes = cipher.doFinal(data.getBytes(encoding));
 92             Base64Encoder base64Encoder = new Base64Encoder();
 93             return base64Encoder.encode(enBytes);
 94         } catch (Exception e) {
 95             e.printStackTrace();
 96         }
 97         return null;
 98     }
 99 
100     public static byte[] decryptCBCToByte(byte[] data, String keyStr) {
101         try {
102             Cipher cipher = Cipher.getInstance(ALGORITHM_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
103             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
104             IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
105             AlgorithmParameterSpec paramSpec = ivSpec;
106             cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
107             byte[] deBytes = cipher.doFinal(data);
108             return deBytes;
109         } catch (Exception e) {
110             e.printStackTrace();
111         }
112         return null;
113     }
114 
115     public static String decryptCBC(String data, String keyStr, String encoding) {
116         try {
117             Cipher cipher = Cipher.getInstance(ALGORITHM_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
118             Key key = new SecretKeySpec(keyStr.getBytes(), ALGORITHM);
119             IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());
120             AlgorithmParameterSpec paramSpec = ivSpec;
121             cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
122 //            byte[] deBytes = cipher.doFinal(Base64.decodeBase64(data));
123             Base64Encoder base64Encoder = new Base64Encoder();
124             byte[] deBytes = cipher.doFinal(base64Encoder.decode(data));
125             return new String(deBytes, encoding);
126         } catch (Exception e) {
127             e.printStackTrace();
128         }
129         return null;
130     }
131 
132     public static String generateKey() {
133         try {
134             //获取到当前系统中的 提供者 和提供者支持的算法。
135             /*Provider[] providers = Security.getProviders();
136             for (Provider provider2 : providers) {
137                 System.err.println(provider2);
138                 Set<Map.Entry<Object, Object>> entrySet = provider2.entrySet();
139                 for (Map.Entry<Object, Object> entry : entrySet) {
140                     System.out.println(entry.getKey() +"  "+ entry.getValue());
141                 }
142             }*/
143             KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
144             kg.init(64, new SecureRandom());
145             return new String(Hex.encodeHex(kg.generateKey().getEncoded())).toUpperCase();
146         } catch (Exception e) {
147             e.printStackTrace();
148         }
149         return null;
150     }
151 
152 }

java请参考 https://github.com/ZZMarquis/gmhelper ,作者也是文中我请教的大佬。

 

 

 

 
posted @ 2019-04-11 11:16  程序员大亨  阅读(8301)  评论(2编辑  收藏  举报