1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.prometheus.security;
18
19 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
20 import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory;
21 import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory.GordianFactoryLock;
22 import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactoryType;
23 import io.github.tonywasher.joceanus.gordianknot.api.keypair.GordianKeyPair;
24 import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianBadCredentialsException;
25 import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
26 import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianKeyPairLock;
27 import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianKeySetLock;
28 import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianLockFactory;
29 import io.github.tonywasher.joceanus.gordianknot.api.lock.spec.GordianPasswordLockSpec;
30 import io.github.tonywasher.joceanus.gordianknot.api.zip.GordianZipLock;
31 import io.github.tonywasher.joceanus.gordianknot.util.GordianGenerator;
32 import io.github.tonywasher.joceanus.gordianknot.util.GordianUtilities;
33 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
34 import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
35 import io.github.tonywasher.joceanus.prometheus.exc.PrometheusLogicException;
36 import io.github.tonywasher.joceanus.prometheus.exc.PrometheusSecurityException;
37
38 import java.nio.ByteBuffer;
39 import java.util.Arrays;
40
41
42
43
44
45
46 public class PrometheusSecurityPasswordManager {
47
48
49
50 private static final String NLS_ERRORPASS = PrometheusSecurityResource.SECURITY_BAD_PASSWORD.getValue();
51
52
53
54
55 private final GordianFactory theFactory;
56
57
58
59
60 private final GordianLockFactory theLockFactory;
61
62
63
64
65 private final GordianPasswordLockSpec theLockSpec;
66
67
68
69
70 private final PrometheusSecurityPasswordCache theCache;
71
72
73
74
75 private PrometheusSecurityDialogController theDialog;
76
77
78
79
80
81
82
83
84 public PrometheusSecurityPasswordManager(final GordianFactory pFactory,
85 final PrometheusSecurityDialogController pDialog) throws OceanusException {
86 this(pFactory, GordianUtilities.newPasswordLockSpecBuilder().passwordLock(), pDialog);
87 }
88
89
90
91
92
93
94
95
96
97 public PrometheusSecurityPasswordManager(final GordianFactory pFactory,
98 final GordianPasswordLockSpec pLockSpec,
99 final PrometheusSecurityDialogController pDialog) throws OceanusException {
100
101 theFactory = pFactory;
102 theLockFactory = theFactory.getLockFactory();
103 theDialog = pDialog;
104 theLockSpec = pLockSpec;
105
106
107 theCache = new PrometheusSecurityPasswordCache(this, theLockSpec);
108 }
109
110
111
112
113
114
115 public GordianFactory getSecurityFactory() {
116 return theFactory;
117 }
118
119
120
121
122
123
124 public GordianPasswordLockSpec getLockSpec() {
125 return theLockSpec;
126 }
127
128
129
130
131
132
133 public void setDialogController(final PrometheusSecurityDialogController pDialog) {
134 theDialog = pDialog;
135 }
136
137
138
139
140
141
142
143
144 public GordianFactoryLock newFactoryLock(final String pSource) throws OceanusException {
145
146 try {
147 final GordianFactory myFactory = GordianGenerator.createRandomFactory(GordianFactoryType.BC);
148 return (GordianFactoryLock) requestPassword(pSource, true, p -> createFactoryLock(myFactory, p));
149
150 } catch (GordianException e) {
151 throw new PrometheusSecurityException(e);
152 }
153 }
154
155
156
157
158
159
160
161
162
163 public GordianFactoryLock newFactoryLock(final GordianFactory pFactory,
164 final String pSource) throws OceanusException {
165 return (GordianFactoryLock) requestPassword(pSource, true, p -> createFactoryLock(pFactory, p));
166 }
167
168
169
170
171
172
173
174
175
176 public GordianFactoryLock resolveFactoryLock(final byte[] pLockBytes,
177 final String pSource) throws OceanusException {
178
179 GordianFactoryLock myFactory = theCache.lookUpResolvedFactoryLock(pLockBytes);
180
181
182 if (myFactory == null) {
183 myFactory = theCache.attemptKnownPasswordsForFactoryLock(pLockBytes);
184 }
185
186
187 if (myFactory == null) {
188 myFactory = (GordianFactoryLock) requestPassword(pSource, false, p -> resolveFactoryLock(pLockBytes, p));
189 }
190
191
192 return myFactory;
193 }
194
195
196
197
198
199
200
201
202 public GordianFactoryLock similarFactoryLock(final Object pReference) throws OceanusException {
203
204 try {
205
206 final GordianFactory myFactory = GordianGenerator.createRandomFactory(GordianFactoryType.BC);
207
208
209 final ByteBuffer myPassword = theCache.lookUpResolvedPassword(pReference);
210
211
212 return theCache.createSimilarFactoryLock(myFactory, myPassword);
213
214 } catch (GordianException e) {
215 throw new PrometheusSecurityException(e);
216 }
217 }
218
219
220
221
222
223
224
225
226 public GordianKeySetLock newKeySetLock(final String pSource) throws OceanusException {
227
228 try {
229 final GordianKeySet myKeySet = theFactory.getKeySetFactory().generateKeySet(theLockSpec.getKeySetSpec());
230 return (GordianKeySetLock) requestPassword(pSource, true, p -> createKeySetLock(myKeySet, p));
231
232 } catch (GordianException e) {
233 throw new PrometheusSecurityException(e);
234 }
235 }
236
237
238
239
240
241
242
243
244
245 public GordianKeySetLock newKeySetLock(final GordianKeySet pKeySet,
246 final String pSource) throws OceanusException {
247 return (GordianKeySetLock) requestPassword(pSource, true, p -> createKeySetLock(pKeySet, p));
248 }
249
250
251
252
253
254
255
256
257
258 public GordianKeySetLock resolveKeySetLock(final byte[] pLockBytes,
259 final String pSource) throws OceanusException {
260
261 GordianKeySetLock myKeySet = theCache.lookUpResolvedKeySetLock(pLockBytes);
262
263
264 if (myKeySet == null) {
265 myKeySet = theCache.attemptKnownPasswordsForKeySetLock(pLockBytes);
266 }
267
268
269 if (myKeySet == null) {
270 myKeySet = (GordianKeySetLock) requestPassword(pSource, false, p -> resolveKeySetLock(pLockBytes, p));
271 }
272
273
274 return myKeySet;
275 }
276
277
278
279
280
281
282
283
284 public GordianKeySetLock similarKeySetLock(final Object pReference) throws OceanusException {
285
286 try {
287
288 final GordianKeySet myKeySet = theFactory.getKeySetFactory().generateKeySet(theLockSpec.getKeySetSpec());
289
290
291 final ByteBuffer myPassword = theCache.lookUpResolvedPassword(pReference);
292
293
294 return theCache.createSimilarKeySetLock(myKeySet, myPassword);
295
296 } catch (GordianException e) {
297 throw new PrometheusSecurityException(e);
298 }
299 }
300
301
302
303
304
305
306
307
308
309 public GordianKeyPairLock newKeyPairLock(final GordianKeyPair pKeyPair,
310 final String pSource) throws OceanusException {
311 return (GordianKeyPairLock) requestPassword(pSource, true, p -> createKeyPairLock(pKeyPair, p));
312 }
313
314
315
316
317
318
319
320
321
322
323 public GordianKeyPairLock resolveKeyPairLock(final byte[] pLockBytes,
324 final GordianKeyPair pKeyPair,
325 final String pSource) throws OceanusException {
326
327 GordianKeyPairLock myKeyPair = theCache.lookUpResolvedKeyPairLock(pLockBytes, pKeyPair);
328
329
330 if (myKeyPair == null) {
331 myKeyPair = theCache.attemptKnownPasswordsForKeyPairLock(pLockBytes, pKeyPair);
332 }
333
334
335 if (myKeyPair == null) {
336 myKeyPair = (GordianKeyPairLock) requestPassword(pSource, false, p -> resolveKeyPairLock(pLockBytes, pKeyPair, p));
337 }
338
339
340 return myKeyPair;
341 }
342
343
344
345
346
347
348
349
350
351 public GordianKeyPairLock similarKeyPairLock(final GordianKeyPair pKeyPair,
352 final Object pReference) throws OceanusException {
353
354 final ByteBuffer myPassword = theCache.lookUpResolvedPassword(pReference);
355
356
357 return theCache.createSimilarKeyPairLock(pKeyPair, myPassword);
358 }
359
360
361
362
363
364
365
366
367 public void resolveZipLock(final GordianZipLock pZipLock,
368 final String pSource) throws OceanusException {
369 switch (pZipLock.getLockType()) {
370 case KEYSET_PASSWORD:
371 resolveKeySetZipLock(pZipLock, pSource);
372 break;
373 case FACTORY_PASSWORD:
374 resolveFactoryZipLock(pZipLock, pSource);
375 break;
376 case KEYPAIR_PASSWORD:
377 default:
378 throw new PrometheusLogicException("KeyPair zipLock not supported yet");
379 }
380 }
381
382
383
384
385
386
387
388
389 private void resolveKeySetZipLock(final GordianZipLock pZipLock,
390 final String pSource) throws OceanusException {
391
392 try {
393
394 final byte[] myLockBytes = pZipLock.getLockBytes();
395
396
397 final GordianKeySetLock myLock = resolveKeySetLock(myLockBytes, pSource);
398
399
400 if (myLock != null) {
401 pZipLock.unlock(myLock);
402 }
403
404 } catch (GordianException e) {
405 throw new PrometheusSecurityException(e);
406 }
407 }
408
409
410
411
412
413
414
415
416 private void resolveFactoryZipLock(final GordianZipLock pZipLock,
417 final String pSource) throws OceanusException {
418
419 try {
420
421 final byte[] myLockBytes = pZipLock.getLockBytes();
422
423
424 final GordianFactoryLock myLock = resolveFactoryLock(myLockBytes, pSource);
425
426
427 if (myLock != null) {
428 pZipLock.unlock(myLock);
429 }
430
431 } catch (GordianException e) {
432 throw new PrometheusSecurityException(e);
433 }
434 }
435
436
437
438
439
440
441
442
443
444
445 public Object requestPassword(final String pSource,
446 final boolean pNeedConfirm,
447 final PrometheusProcessPassword pProcessor) throws OceanusException {
448
449 Object myResult = null;
450
451
452 theDialog.createTheDialog(pSource, pNeedConfirm);
453
454
455 boolean isPasswordOk = false;
456 char[] myPassword = null;
457 while (theDialog.showTheDialog()) {
458 try {
459
460 myPassword = theDialog.getPassword();
461
462
463 final String myError = PrometheusPassCheck.validatePassword(myPassword);
464 if (myError != null) {
465 theDialog.reportBadPassword(myError);
466 continue;
467 }
468
469
470 theDialog.showTheSpinner(true);
471 myResult = pProcessor.processPassword(myPassword);
472
473
474 isPasswordOk = true;
475 break;
476
477 } catch (GordianBadCredentialsException e) {
478 theDialog.reportBadPassword(NLS_ERRORPASS);
479 if (myPassword != null) {
480 Arrays.fill(myPassword, (char) 0);
481 }
482
483 } finally {
484 if (myPassword != null) {
485 Arrays.fill(myPassword, (char) 0);
486 myPassword = null;
487 }
488 }
489 }
490
491
492 theDialog.releaseDialog();
493
494
495 if (!isPasswordOk) {
496
497 throw new PrometheusDataException(NLS_ERRORPASS);
498 }
499
500
501 return myResult;
502 }
503
504
505
506
507 @FunctionalInterface
508 public interface PrometheusProcessPassword {
509
510
511
512
513
514
515
516
517 Object processPassword(char[] pPassword) throws OceanusException;
518 }
519
520
521
522
523
524
525
526
527
528 private GordianFactoryLock createFactoryLock(final GordianFactory pFactory,
529 final char[] pPassword) throws OceanusException {
530
531 try {
532 final GordianFactoryLock myLock = theFactory.newFactoryLock(pFactory, theLockSpec, pPassword);
533 theCache.addResolvedFactory(myLock, pPassword);
534 return myLock;
535
536 } catch (GordianException e) {
537 throw new PrometheusSecurityException(e);
538 }
539 }
540
541
542
543
544
545
546
547
548
549 private GordianFactoryLock resolveFactoryLock(final byte[] pLockBytes,
550 final char[] pPassword) throws OceanusException {
551
552 try {
553 final GordianFactoryLock myFactory = theFactory.resolveFactoryLock(pLockBytes, pPassword);
554 theCache.addResolvedFactory(myFactory, pPassword);
555 return myFactory;
556
557 } catch (GordianException e) {
558 throw new PrometheusSecurityException(e);
559 }
560 }
561
562
563
564
565
566
567
568
569
570 private GordianKeySetLock createKeySetLock(final GordianKeySet pKeySet,
571 final char[] pPassword) throws OceanusException {
572
573 try {
574 final GordianKeySetLock myLock = theLockFactory.newKeySetLock(pKeySet, theLockSpec, pPassword);
575 theCache.addResolvedKeySet(myLock, pPassword);
576 return myLock;
577
578 } catch (GordianException e) {
579 throw new PrometheusSecurityException(e);
580 }
581 }
582
583
584
585
586
587
588
589
590
591 private GordianKeySetLock resolveKeySetLock(final byte[] pLockBytes,
592 final char[] pPassword) throws OceanusException {
593
594 try {
595 final GordianKeySetLock myKeySet = theLockFactory.resolveKeySetLock(pLockBytes, pPassword);
596 theCache.addResolvedKeySet(myKeySet, pPassword);
597 return myKeySet;
598
599 } catch (GordianException e) {
600 throw new PrometheusSecurityException(e);
601 }
602 }
603
604
605
606
607
608
609
610
611
612 private GordianKeyPairLock createKeyPairLock(final GordianKeyPair pKeyPair,
613 final char[] pPassword) throws OceanusException {
614
615 try {
616 final GordianKeyPairLock myLock = theLockFactory.newKeyPairLock(theLockSpec, pKeyPair, pPassword);
617 theCache.addResolvedKeyPair(myLock, pPassword);
618 return myLock;
619
620 } catch (GordianException e) {
621 throw new PrometheusSecurityException(e);
622 }
623 }
624
625
626
627
628
629
630
631
632
633
634 private GordianKeyPairLock resolveKeyPairLock(final byte[] pLockBytes,
635 final GordianKeyPair pKeyPair,
636 final char[] pPassword) throws OceanusException {
637
638 try {
639 final GordianKeyPairLock myKeyPair = theLockFactory.resolveKeyPairLock(pLockBytes, pKeyPair, pPassword);
640 theCache.addResolvedKeyPair(myKeyPair, pPassword);
641 return myKeyPair;
642
643 } catch (GordianException e) {
644 throw new PrometheusSecurityException(e);
645 }
646 }
647
648
649
650
651 private enum PrometheusPassCheck {
652
653
654
655 NUMERIC(1),
656
657
658
659
660 LOWERCASE(2),
661
662
663
664
665 UPPERCASE(4),
666
667
668
669
670 SPECIAL(8);
671
672
673
674
675 private static final String NLS_BADLENGTH = PrometheusSecurityResource.SECURITY_BAD_PASSLEN.getValue();
676
677
678
679
680 private static final String NLS_BADCHAR = PrometheusSecurityResource.SECURITY_INVALID_CHARS.getValue();
681
682
683
684
685 private static final String SPECIAL_CHARS = "%$^!@-_+~#&*";
686
687
688
689
690 private static final int MINPASSLEN = 8;
691
692
693
694
695 private final int theFlag;
696
697
698
699
700
701
702 PrometheusPassCheck(final int pFlag) {
703 theFlag = pFlag;
704 }
705
706
707
708
709
710
711 private int getFlag() {
712 return theFlag;
713 }
714
715
716
717
718
719
720
721 static String validatePassword(final char[] pPassword) {
722
723 if (pPassword.length < MINPASSLEN) {
724 return NLS_BADLENGTH;
725 }
726
727
728 int myResult = 0;
729 for (char c : pPassword) {
730 if (Character.isDigit(c)) {
731 myResult |= NUMERIC.getFlag();
732 } else if (Character.isLowerCase(c)) {
733 myResult |= LOWERCASE.getFlag();
734 } else if (Character.isUpperCase(c)) {
735 myResult |= UPPERCASE.getFlag();
736 } else if (SPECIAL_CHARS.indexOf(c) != -1) {
737 myResult |= SPECIAL.getFlag();
738 }
739 }
740
741
742 if (myResult != getExpectedResult()) {
743 return NLS_BADCHAR;
744 }
745 return null;
746 }
747
748
749
750
751
752
753 private static int getExpectedResult() {
754 int myResult = 0;
755 for (PrometheusPassCheck myCheck : values()) {
756 myResult |= myCheck.getFlag();
757 }
758 return myResult;
759 }
760 }
761 }