1 package org.apache.fulcrum.jce.crypto.cli;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.OutputStreamWriter;
28 import java.nio.charset.Charset;
29 import java.util.Arrays;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Optional;
33 import java.util.Set;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36 import java.util.stream.Collectors;
37
38 import org.apache.fulcrum.jce.crypto.HexConverter;
39 import org.apache.fulcrum.jce.crypto.StreamUtil;
40 import org.apache.fulcrum.jce.crypto.extended.CryptoParametersJ8;
41 import org.apache.fulcrum.jce.crypto.extended.CryptoParametersJ8.TYPES;
42 import org.apache.fulcrum.jce.crypto.extended.CryptoStreamFactoryJ8Template;
43 import org.apache.fulcrum.jce.crypto.extended.CryptoUtilJ8;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class CLI2 {
72
73
74 static boolean debug = false;
75
76
77
78
79
80 public static void main(String[] args) {
81 try {
82 if (args.length == 0) {
83 printHelp();
84 return;
85 }
86 String operationMode = args[0];
87
88 String msg = "No operationMode";
89 if (operationMode == null || operationMode.equals("")) {
90 throw new IllegalArgumentException(msg);
91 }
92
93 if (operationMode.equals("info")) {
94 printInfo();
95 return;
96 } else if (operationMode.equals("help")) {
97 printHelp();
98 return;
99 }
100
101 if (args.length < 3) {
102 printHelp();
103 throw new IllegalArgumentException("Invalid command line");
104 }
105
106 if (operationMode.equals("file")) {
107 processFiles(args);
108 } else if (operationMode.equals("string")) {
109 processString(args);
110 }
111 } catch (Exception e) {
112 System.out.println("Error: " + e.getMessage());
113 e.printStackTrace();
114 }
115 }
116
117 private static void printInfo() {
118 CryptoUtilJ8 cryptoUtilJ8 = CryptoUtilJ8.getInstance();
119 System.out.println("");
120 System.out.println("\t| Default Crypto factory class: \t" + cryptoUtilJ8.getCryptoStreamFactory().getClass());
121 System.out.println("\t|_Default Algorithm used: \t" + cryptoUtilJ8.getCryptoStreamFactory().getAlgorithm());
122
123 for (int i = 0; i < CryptoParametersJ8.LISTS.length; i++) {
124 List<String> list = CryptoParametersJ8.LISTS[i];
125 String type = CryptoParametersJ8.PROVIDER_TYPES[i];
126 System.out.println("\t|Algorithms (unchecked) supported: \t" + list);
127 List<String> result = CryptoParametersJ8.getSupportedAlgos(list, type, true);
128 Set<String> resultSet = new LinkedHashSet<String>(result);
129 resultSet.addAll( CryptoParametersJ8.getSupportedAlgos(list, type, false) );
130 System.out.println(
131 String.format("\t|_Matched supported %2$s:\t%1$s",
132 (resultSet), type));
133 }
134
135 List<String> supportedAlgos = CryptoParametersJ8.init();
136 System.out.println(
137 String.format("\t|Effectively supported from %2$ss:\t*** %1$s ***",
138 supportedAlgos, Arrays.toString( CryptoParametersJ8.PROVIDER_TYPES) ));
139
140 if (debug) {
141 Arrays.stream(CryptoParametersJ8.TYPES.values()).forEach(t -> {
142 CryptoUtilJ8 testcu = CryptoUtilJ8.getInstance(t);
143 System.out.println("\t| Crypto factory class: \t" + testcu.getCryptoStreamFactory().getClass());
144 System.out.println("\t|_Algorithm used: \t" + testcu.getCryptoStreamFactory().getAlgorithm());
145
146 });
147 }
148 System.out.println(
149 "\t|_ More Info: https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html\r\n");
150 }
151
152
153
154
155
156 public static void printHelp() {
157 System.out.println(
158 "\r\n\t*** Command line tool for encrypting/decrypting strings/files ***\r\n\t*** algorithm based on "
159 + CryptoParametersJ8.TYPES_IMPL.ALGORITHM_J8_PBE + "***\r\n");
160 System.out.println("\tjava -cp target\\classes " + CLI2.class.getName()
161 + " <operation mode> <coding mode> <password> <path|string> [target]\r\n");
162 System.out.println(
163 "\tjava -jar target/fulcrum-yaafi-crypto-1.0.8-SNAPSHOT.jar <operation mode> <coding mode> <password> <path|string> [target]\r\n");
164 System.out.println("\t-------------------");
165 System.out.println("\toperation mode: file|string|info");
166 System.out.println("\tcoding mode: enc|dec|enc:GCM. Default algorithm is " + CryptoParametersJ8.DEFAULT_TYPE);
167 System.out.println("\t<password: string or empty:''");
168 System.out.println("\tcode|coderef: path|string");
169 System.out.println("\ttarget: optional\r\n");
170 System.out.println("\t-------------------");
171 System.out.println("\t*** Usage: ***\r\n");
172 System.out.println("\t" + CLI2.class.getSimpleName() + " file [enc|dec] passwd source [target]");
173 System.out.println("\t" + CLI2.class.getSimpleName() + " string [enc|dec] passwd source");
174 System.out.println("\t" + CLI2.class.getSimpleName() + " info");
175 }
176
177
178
179
180
181
182
183 public static void processFiles(String[] args) throws Exception {
184 String cipherMode = args[1];
185 char[] password = args[2].toCharArray();
186 File sourceFile = new File(args[3]);
187 File targetFile = null;
188
189 if (args.length == 4) {
190 targetFile = sourceFile;
191 } else {
192 targetFile = new File(args[4]);
193 File parentFile = targetFile.getParentFile();
194
195 if (parentFile != null && (!parentFile.exists() || !parentFile.isDirectory())) {
196 boolean success = parentFile.mkdirs();
197 if (!success) {
198 System.err.println("Error, could not create directory to write parent file");
199 }
200 }
201 }
202
203 processFile(cipherMode, password, sourceFile, targetFile);
204 }
205
206
207
208
209
210
211
212
213
214
215 public static void processFile(String cipherMode, char[] password, File sourceFile, File targetFile)
216 throws Exception {
217
218 try (FileInputStream fis = new FileInputStream(sourceFile)) {
219 ByteArrayOutputStream baos = new ByteArrayOutputStream();
220 CryptoUtilJ8 cryptoUtilJ8 = createCryptoUtil(cipherMode);
221
222 if (cryptoUtilJ8 == null) {
223 System.out.println("Canceling ");
224 return;
225 }
226
227 if (cipherMode.startsWith("dec")) {
228 System.out.println("Decrypting " + sourceFile.getAbsolutePath());
229
230
231 StringBuffer stringBuffer = new StringBuffer();
232 int i;
233 while ((i = fis.read()) != -1) {
234 stringBuffer.append((char) i);
235 }
236
237 String value = stringBuffer.toString();
238 if (isHexadecimal(value)) {
239 byte[] buffer = HexConverter.toBytes(value);
240 cryptoUtilJ8.decrypt(buffer, baos, password);
241 } else {
242 try (FileInputStream fis2 = new FileInputStream(sourceFile)) {
243 cryptoUtilJ8.decrypt(fis2, baos, password);
244 }
245 }
246
247 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
248 FileOutputStream fos = new FileOutputStream(targetFile);
249 StreamUtil.copy(bais, fos);
250 bais.close();
251 fos.close();
252 } else if (cipherMode.startsWith("enc")) {
253 System.out.println("Encrypting " + sourceFile.getAbsolutePath());
254 cryptoUtilJ8.encrypt(fis, baos, password);
255 fis.close();
256
257 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
258 FileOutputStream fos = new FileOutputStream(targetFile);
259 StreamUtil.copy(bais, fos);
260 bais.close();
261 fos.close();
262 } else {
263 String msg = "Don't know what to do with : " + cipherMode;
264 throw new IllegalArgumentException(msg);
265 }
266 }
267 }
268
269 private static CryptoUtilJ8 createCryptoUtil(String cipherMode) throws Exception {
270 CryptoUtilJ8 cryptoUtilJ8 = null;
271 List<String> supportedTypes = CryptoParametersJ8.init();
272
273 if (cipherMode.substring("enc".length()).equals("")) {
274 if (supportedTypes.stream().anyMatch(x-> x.equals(CryptoParametersJ8.DEFAULT_TYPE.toString()) )) {
275 System.err.println("using default type:"+ CryptoParametersJ8.DEFAULT_TYPE);
276 cryptoUtilJ8 = CryptoUtilJ8.getInstance();
277 } else {
278 System.err.println("Could not use default type:"+ TYPES.PBE + ".You have to set explicit type, e.g. enc:"+supportedTypes.get(0) );
279 }
280 } else {
281 System.err.println("checking supported types:"+ supportedTypes);
282 List<String> matchedType = supportedTypes.stream().filter(x-> cipherMode.endsWith(x) ).collect(Collectors.toList());
283 System.err.println("matched type:"+ matchedType);
284 Optional<TYPES> algoShortcut = Arrays.stream(CryptoParametersJ8.TYPES.values())
285 .filter(a -> matchedType.get(0).equals(a.toString())).findFirst();
286 if (algoShortcut.isPresent()) {
287 System.err.println("initializing type:"+ algoShortcut);
288 cryptoUtilJ8 = CryptoUtilJ8.getInstance(algoShortcut.get());
289 }
290 }
291
292 if (cryptoUtilJ8 == null) {
293 throw new Exception("Could not use algorithm. Check debug output and JDK provided algo shortcuts with CLI2 info!");
294 }
295
296 if (debug) {
297 CryptoStreamFactoryJ8Template crt = ((CryptoStreamFactoryJ8Template)cryptoUtilJ8.getCryptoStreamFactory());
298 System.err.println(String.format("using crypto factory instance %s for algo %s and type %s with salt length: %s and count %s",
299 crt.getClass().getSimpleName(), crt.getType(),
300 crt.getAlgorithm(), crt.getSalt().length, crt.getCount()));
301 }
302 return cryptoUtilJ8;
303 }
304
305
306
307
308
309
310
311 public static void processString(String[] args) throws Exception {
312 final String cipherMode;
313 final char[] password;
314 final String value;
315 File targetFile = null;
316 if (args.length > 3) {
317 cipherMode = args[1];
318 password = args[2].toCharArray();
319 value = args[3];
320 } else {
321 value = null;
322 cipherMode = null;
323 password = null;
324 }
325 if (args.length == 5) {
326 targetFile = new File(args[4]);
327 File parentFile = targetFile.getParentFile();
328
329 if (parentFile != null && (!parentFile.exists() || !parentFile.isDirectory())) {
330 boolean success = parentFile.mkdirs();
331 if (!success) {
332 System.err.println("Error, could not create directory to write parent file");
333 }
334 }
335 }
336
337 if (value != null && !value.equals("")) {
338
339 String result = processString(cipherMode, password, value);
340
341 if (targetFile != null) {
342
343 try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(targetFile),
344 Charset.forName("UTF-8").newEncoder())) {
345 osw.write(result);
346 }
347 } else {
348 System.out.println(result);
349 }
350 }
351 }
352
353
354
355
356
357
358
359
360
361
362
363
364 public static String processString(String cipherMode, char[] password, String value) throws Exception {
365 if (value != null && !value.equals("")) {
366 CryptoUtilJ8 cryptoUtilJ8 = createCryptoUtil(cipherMode);
367
368 String result = null;
369 if (cipherMode.startsWith("dec")) {
370 result = cryptoUtilJ8.decryptString(value, password);
371 } else if (cipherMode.startsWith("enc")) {
372 result = cryptoUtilJ8.encryptString(value, password);
373 }
374 return result;
375 } else {
376 return null;
377 }
378 }
379
380 private static final Pattern HEXADECIMAL_PATTERN = Pattern.compile("\\p{XDigit}+");
381
382 public static boolean isHexadecimal(String input) {
383 final Matcher matcher = HEXADECIMAL_PATTERN.matcher(input);
384 return matcher.matches();
385 }
386 }