很久沒在博客園寫隨筆了,來講說我最近在作的人臉支付使用國密算法加密時遇到的一些坑。java
SM4加密第一步,生成"BC"provider,"SM4"算法的keyandroid
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 }
運行這個方法,我遇到的第一個坑:git
java.security.NoSuchProviderException No such provider: BCgithub
此異常我經過各類博客,github問一些開源做者,也都是給出這樣的解決辦法算法
java的話在static塊中添加dom
1 static { 2 Security.addProvider(new BouncyCastleProvider()); 3 }
android的話在加密方法調用前使用這句話便可。ide
然而我再次運行,拋出另外一個algorithmexception:no such algorithm: SM4 for provider BCui
研究了兩天,看了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,可是版本略低呢!spa
果不其然,我打印了在我添加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 ,做者也是文中我請教的大佬。