· 8 years ago · Jan 30, 2018, 12:44 PM
1/************************* pin code ********************************************/
2 public void setPinModeEnableUnable() {
3 pinCodeMode = isPinSet ? PIN_DISABLE : PIN_INITIAL_SET;
4 }
5
6 public void putIsPinCodeSet(boolean isPinCodeSet) {
7 isPinSet = isPinCodeSet;
8 settingPresenter.putIsPinCodeSet(isPinCodeSet);
9 settingPresenter.putIsPinDisplayed();
10
11 switchFragment("SettingFragment");
12 }
13
14 public void pinLaunchCheckSucceeded() {
15 MyApplication.isPinEntered = true;
16 finish();
17 }
18
19 public void showPinPeriodDialog(boolean doDisplay) {
20 if (doDisplay) {
21 checkRadioButton();
22 }
23 viewTransparent.setVisibility(doDisplay ? View.VISIBLE : View.GONE);
24 cardPinPeriod.setVisibility(doDisplay ? View.VISIBLE : View.GONE);
25 }
26
27
28 private void checkRadioButton() {
29 int pinPeriodInt = (int) settingPresenter.retrieveSecurePeriod();
30 Timber.d("SettingActivity:checkRadioButton: pinPeriodInt %s", pinPeriodInt);
31 if (pinPeriodInt < TimeUtility.MILLISECONDS_IN_MINUTE) {
32 rBtnNow.setChecked(true);
33 } else if (pinPeriodInt < TimeUtility.MILLISECONDS_IN_MINUTE * 5) {
34 rBtn1min.setChecked(true);
35 } else if (pinPeriodInt < TimeUtility.MILLISECONDS_IN_MINUTE * 15) {
36 rBtn5min.setChecked(true);
37 } else if (pinPeriodInt < TimeUtility.MILLISECONDS_IN_HOUR) {
38 rBtn15min.setChecked(true);
39 } else if (pinPeriodInt < TimeUtility.MILLISECONDS_IN_HOUR * 3) {
40 rBtn1h.setChecked(true);
41 } else {
42 rBtn3h.setChecked(true);
43 }
44 }
45
46 public void onRadioPinPeriodClicked(View view) {
47 // Is the button now checked?
48 boolean checked = ((RadioButton) view).isChecked();
49
50 // Check which radio button was clicked
51 switch (view.getId()) {
52 case R.id.radio_now_setting:
53 if (checked) {
54 pinPeriod = 0L;
55 break;
56 }
57 case R.id.radio_1_minute_setting:
58 if (checked) {
59 pinPeriod = TimeUtility.MILLISECONDS_IN_MINUTE;
60 break;
61 }
62 case R.id.radio_5_minute_setting:
63 if (checked) {
64 pinPeriod = TimeUtility.MILLISECONDS_IN_MINUTE * 5;
65 break;
66 }
67 case R.id.radio_15_minute_setting:
68 if (checked) {
69 pinPeriod = TimeUtility.MILLISECONDS_IN_MINUTE * 15;
70 break;
71 }
72 case R.id.radio_1_hour_setting:
73 if (checked) {
74 pinPeriod = TimeUtility.MILLISECONDS_IN_HOUR;
75 break;
76 }
77 case R.id.radio_3_hour_setting:
78 if (checked) {
79 pinPeriod = TimeUtility.MILLISECONDS_IN_HOUR * 3;
80 break;
81 }
82 }
83 Timber.d("SettingActivity:onRadioPinPeriodClicked: pinPeriod %s", pinPeriod);
84 }
85
86
87 @OnClick(R.id.tv_pin_period_cancel_setting)
88 void cancelSettingPinPeriod() {
89 closeDialog();
90 }
91
92 @OnClick(R.id.tv_pin_period_ok_setting)
93 void okSettingPinPeriod() {
94
95 settingPresenter.putSecurePausedPeriod(pinPeriod);
96 closeDialog();
97 }
98
99
100 @OnClick(R.id.view_transparent_setting)
101 void closeDialog() {
102 // BackupDialog ã®å ´åˆã¯ã€ãƒã‚°ã‚¢ã‚¦ãƒˆå‡¦ç†ã¨ã‹ã‚ã‚‹ã‹ã‚‰æ¶ˆã•ãªã„
103 if (!isBackupDialogVisible) {
104 showPinPeriodDialog(false);
105 }
106 }
107
108 public void putPinCodeOnInternal(int pinCode) {
109 putEncryptedPinCode(pinCode);
110 }
111
112 public int retrievePinCodeFromInternal(int pinCode) {
113 return retrieveDecryptedData(pinCode);
114 }
115
116 /************************* æš—å·åŒ–/復å·åŒ– ********************************************/
117
118 // pinCode â†’ç§˜å¯†éµ + æš—å·åŒ–data →書ãè¾¼ã¿
119 private void putEncryptedPinCode(int pinCode) {
120 String pinCodeStr = String.valueOf(pinCode);
121 // pinCodeStr ã‹ã‚‰ 秘密éµã‚’生æˆ
122 SecretKey secureKey = generateSecureKeyByPassword(pinCodeStr, KEY_SIZE);
123
124 // pinCode ã‚’ byte[] ã«å¤‰æ›
125 byte[] intInByteArray = ByteBuffer.allocate(4).putInt(pinCode).array();
126 // ãã—ã¦æš—å·åŒ–
127 byte[] encryptedPin = encryptData(intInByteArray, retrieveIv(), secureKey);
128
129 // å†…éƒ¨ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã¸æ›¸ãè¾¼ã¿
130 writeToFile(ENCRYPTED_FILE_NAME, encryptedPin);
131 Timber.d("SettingActivity:putEncryptedPinCode: intInByteArray %s, encryptedPin %s secureKey %s",
132 intInByteArray, encryptedPin, secureKey);
133 }
134
135
136 // pinCode →秘密éµç”Ÿæˆ →複åˆã«ä½¿ç”¨
137 private int retrieveDecryptedData(int pinCode) {
138
139 String pinCodeStr = String.valueOf(pinCode);
140
141 // 秘密éµã‚’å–å¾—
142 SecretKey secureKey = generateSecureKeyByPassword(pinCodeStr, KEY_SIZE);
143
144 // 本アプリ専用ä¿å˜å ´æ‰€ãƒ‘スã¨ãƒ•ァイルåã¨ã§ã€ãƒ•ァイルをå–å¾—ã—ã€ãã“ã‹ã‚‰ byte サイズをå–å¾—
145 File file = new File(context.getFilesDir(), ENCRYPTED_FILE_NAME);
146 int size = (int) file.length();
147 byte[] bytes = new byte[size];
148
149 // 内部ストレージã‹ã‚‰ãƒ•ァイルをå–å¾—(æš—å·åŒ–状態)
150 readFileForPinCode(ENCRYPTED_FILE_NAME, bytes);
151 // データを復å·åŒ–
152 byte[] decryptedData = decryptData(bytes, retrieveIv(), secureKey);
153
154 int returnedInt;
155 // 復å·åŒ–ã•れ㟠byte[] ã‚’ int ã«å¤‰æ›
156 if (decryptedData == null) {
157 // 複åˆã‚’試ã¿ãŸå…¥åŠ›ãŒé–“é•ã£ã¦ã„ãŸå ´åˆã€null ãŒè¿”ã•れる
158 returnedInt = 9999999;// 4æ¡ä»¥ä¸Šã®å€¤ã‚’è¿”ã™
159 } else {
160 ByteBuffer wrapped = ByteBuffer.wrap(decryptedData); // big-endian by default
161 returnedInt = wrapped.getInt();
162 }
163 return returnedInt;
164 }
165
166 /**
167 * 入力ã•れãŸãƒ‘スワードã‹ã‚‰ã€ç§˜å¯†éµã‚’å†ç”Ÿæˆã—ã¦è¿”ã™
168 */
169 private SecretKey generateSecureKeyByPassword(String password, int keySizeInBytes) {
170 int iterationCount = 1000;
171 // Use this to derive the key from the password:
172 KeySpec keySpec = new PBEKeySpec(password.toCharArray(), retrieveSalt(),
173 iterationCount, keySizeInBytes * 8);
174 try {
175 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
176 byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
177 return new SecretKeySpec(keyBytes, "AES");
178 } catch (Exception e) {
179 Timber.e("SettingActivity:generateSecureKeyByPassword: %s", e.getMessage());
180 throw new RuntimeException("Deal with exceptions properly!", e);
181 }
182 }
183
184 /********************** encrypt Or Decrypt ã®å®Ÿè¡Œ ***************************/
185 private static byte[] encryptOrDecrypt(
186 byte[] data, SecretKey key, byte[] iv, boolean isEncrypt) {
187 try {
188 //Authenticated Encryption -> CBC(Cipher block chaining )
189 // CBC 㨠PKCS5Padding ã®çµ„ã¿åˆã‚ã›ã¯æ”»æ’ƒã‚’å—ã‘るらã—ã„
190 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
191
192 cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
193 new IvParameterSpec(iv));
194 return cipher.doFinal(data);
195 } catch (GeneralSecurityException e) {
196 Timber.e("SettingActivity:encryptOrDecrypt: %s", e.getMessage());
197 return null;
198 }
199 }
200
201 private static byte[] encryptData(byte[] data, byte[] iv, SecretKey key) {
202 return encryptOrDecrypt(data, key, iv, true);
203 }
204
205 private static byte[] decryptData(byte[] data, byte[] iv, SecretKey key) {
206 return encryptOrDecrypt(data, key, iv, false);
207 }
208
209 /********************** ソルト ******************************/
210 // ソルトã®å–å¾—ã‚‚ã—ãã¯ã€åˆå›žã§ã‚れã°ç”Ÿæˆã—ã¦ä¿å˜
211 private byte[] retrieveSalt() {
212 // Salt must be at least the same size as the key.
213 byte[] salt = new byte[KEY_SIZE];
214 // Create a random salt if encrypting for the first time, and save it for future use.
215 readFromFileOrCreateRandom("salt", salt);
216 return salt;
217 }
218
219 /********************** IV ******************************/
220 // initialization vector (IV)
221 // æš—å·åŒ–ã™ã‚‹å€¤ ã®é ã«ã¤ã‘ã¦ã€æ¯Žåº¦ç•°ãªã‚‹ cipher text ãŒç”Ÿæˆã•れるよã†ã«ã™ã‚‹
222 private byte[] retrieveIv() {
223 byte[] iv = new byte[IV_SIZE];
224 // Ideally your data should have been encrypted with a random iv. This creates a random iv
225 // if not present, in order to encrypt our mock data.
226 readFromFileOrCreateRandom("iv", iv);
227 return iv;
228 }
229
230 /********************** CSPRNG ã‚»ã‚ュアãªãƒ©ãƒ³ãƒ€ãƒžã‚¤ã‚¶ãƒ¼ ***********************/
231 /**
232 * 内部ストレージã«ãƒ•ァイル "salt/iv" ãŒã‚ã‚‹ã‹ã©ã†ã‹ç¢ºèªã—ã¦ã€ãªã‘れã°ç”Ÿæˆã—ã¦ä¿å˜
233 */
234 private void readFromFileOrCreateRandom(String fileName, byte[] bytes) {
235 if (fileExists(fileName)) {
236 readBytesFromFile(fileName, bytes);
237 return;
238 }
239 SecureRandom sr = new SecureRandom();
240 sr.nextBytes(bytes);
241 writeToFile(fileName, bytes);
242 }
243
244 private boolean fileExists(String fileName) {
245 File file = new File(getFilesDir(), fileName);
246 return file.exists();
247 }
248
249 /********************** 内部ストレージã¸ã® 書ã込㿠******************************/
250 private void writeToFile(String fileName, byte[] bytes) {
251 FileOutputStream outputStream = null;
252 try {
253 outputStream = openFileOutput(fileName, Context.MODE_PRIVATE);
254 outputStream.write(bytes);
255 } catch (IOException e) {
256 Timber.e("SettingActivity:writeToFile: Couldn't write to %s, %s", fileName, e.getMessage());
257 } finally {
258 try {
259 if (outputStream != null) {
260 outputStream.flush();
261 outputStream.close();
262 }
263 } catch (IOException e) {
264 Timber.e("SettingActivity:writeToFile: %s", e.getMessage());
265 }
266 }
267 }
268
269
270 /********************** 内部ストレージã‹ã‚‰ã® èªã¿å–り ******************************/
271 private void readBytesFromFile(String fileName, byte[] bytes) {
272
273 FileInputStream inputStream = null;
274 try {
275 inputStream = openFileInput(fileName);
276 int numBytes = 0;
277 while (numBytes < bytes.length) {
278 int n = inputStream.read(bytes, numBytes, bytes.length - numBytes);
279 if (n <= 0) {
280 Timber.e("SettingActivity:readBytesFromFile: Couldn't read from %s", fileName);
281 }
282 numBytes += n;
283 }
284 } catch (IOException e) {
285 Timber.e("SettingActivity:readBytesFromFile: %s", e.getMessage());
286 } finally {
287 try {
288 if (inputStream != null) {
289 inputStream.close();
290 }
291 } catch (IOException e) {
292 Timber.e("SettingActivity:readBytesFromFile: %s", e.getMessage());
293 }
294 }
295 }
296
297 private void readFileForPinCode(String fileName, byte[] bytes) {
298
299 FileInputStream inputStream = null;
300 try {
301 inputStream = openFileInput(fileName);
302 int numBytes = 0;
303 while (numBytes < bytes.length) {
304 int n = inputStream.read(bytes, numBytes, bytes.length - numBytes);
305 if (n <= 0) {
306 Timber.e("SettingActivity:readBytesFromFile: Couldn't read from %s", fileName);
307 }
308 numBytes += n;
309 }
310 } catch (IOException e) {
311 Timber.e("SettingActivity:readBytesFromFile: %s", e.getMessage());
312 } finally {
313 try {
314 if (inputStream != null) {
315 inputStream.close();
316 }
317 } catch (IOException e) {
318 Timber.e("SettingActivity:readBytesFromFile: %s", e.getMessage());
319 }
320 }
321 }