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 ,作者也是文中我请教的大佬。