1 package org.apache.fulcrum.jce.crypto.extended;
2
3 import java.security.Provider;
4 import java.security.Security;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collections;
8 import java.util.LinkedHashSet;
9 import java.util.List;
10 import java.util.Set;
11 import java.util.stream.Collectors;
12
13 /*
14 * Licensed to the Apache Software Foundation (ASF) under one
15 * or more contributor license agreements. See the NOTICE file
16 * distributed with this work for additional information
17 * regarding copyright ownership. The ASF licenses this file
18 * to you under the Apache License, Version 2.0 (the
19 * "License"); you may not use this file except in compliance
20 * with the License. You may obtain a copy of the License at
21 *
22 * http://www.apache.org/licenses/LICENSE-2.0
23 *
24 * Unless required by applicable law or agreed to in writing,
25 * software distributed under the License is distributed on an
26 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
27 * KIND, either express or implied. See the License for the
28 * specific language governing permissions and limitations
29 * under the License.
30 */
31
32 /**
33 * CryptoParameters used for encryption/decryption.
34 *
35 * @author <a href="mailto:gk@apache.org">Georg Kallidis</a>
36 */
37
38 public interface CryptoParametersJ8 {
39
40 /**
41 *
42 * Implementing classes are either using
43 *
44 * <ul>
45 * <li>PBEWith <digest>And<encryption> - the password-based encryption algorithm defined in PKCS #5: PBEWithHmacSHA256AndAES_256/CBC/PKCS5Padding in {@link #ALGORITHM_J8_PBE}</li>
46 * </ul>
47 *
48 * or
49 *
50 * <ul>
51 * <li>AES/GCM/NoPadding in {@link #ALGORITHM_J8_GCM} (Cipher Algorithm Names/Cipher Algorithm Modes/Cipher Algorithm Padding). Cipher is Galois/Counter Mode, as defined in NIST Special Publication SP 800-38D: </li>
52 * </ul>
53 *
54 *
55 * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SunJCEProvider">The Oracle Security SunJCE Provider</a>
56 *
57 * Algo/mode/padding for cipher transformation:
58 *
59 * Java 8: <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">The Oracle Security Standard Names Cipher Algorithms</a>
60 *
61 * Java 14: <a href="https://docs.oracle.com/en/java/javase/14/docs/specs/security/standard-names.html#security-algorithm-implementation-requirements">Security Algorithm Implementation Requirements</a>
62 *
63 */
64 public enum TYPES_IMPL {
65
66 // key size 256
67 ALGORITHM_J8_PBE("PBEWithHmacSHA256AndAES_256"),
68 // key size 128
69 ALGORITHM_J8_GCM("AES_128/GCM/NoPadding");
70
71 private final String algorithm;
72
73 private TYPES_IMPL(String algo) {
74 algorithm = algo;
75 }
76
77 @Override
78 public String toString() {
79 return this.algorithm;
80 }
81
82 public String getAlgorithm() {
83 return algorithm;
84 }
85
86 /**
87 * clear code depending on algorithm AES size return <pre>J8AESAES_<size>;</pre>.
88 * {@link CryptoParametersJ8#CLEAR_CODE_DEFAULT}
89 *
90 * @return clear code J8AES_<size>; with three digit size.
91 */
92 public String getClearCode() {
93
94 return String.format("J8%1$s;",
95 algorithm.subSequence(algorithm.indexOf("AES_"),algorithm.indexOf("AES_")+7));
96 }
97 }
98
99 /**
100 *
101 * short names, exact names @see {@link TYPES_IMPL}.
102 *
103 */
104 public enum TYPES {
105
106 /**
107 * PBE algorithm is kind of meta algorithm, uses AES, see above.
108 */
109 PBE,
110 /**
111 * AES algorithm, but GCM is is actually the algorithm mode, but nevertheless used as a short name.
112 */
113 GCM;
114
115 /**
116 * Clear code should be always 10 bytes.
117 *
118 * {@link CryptoParametersJ8#CLEAR_CODE_DEFAULT}
119 *
120 * @return clear code
121 *
122 */
123 public String getClearCode() {
124 return this.equals(TYPES.PBE)?
125 TYPES_IMPL.ALGORITHM_J8_PBE.getClearCode():
126 TYPES_IMPL.ALGORITHM_J8_GCM.getClearCode();
127 }
128 }
129
130 /**
131 * Prefix to decrypted hex hash to get a clue, what to use and what it is; should be always 10 bytes.
132 */
133 public String CLEAR_CODE_DEFAULT = "J8_AES064;";
134
135 public TYPES DEFAULT_TYPE = TYPES.PBE;
136
137
138 /**
139 * Checks Java provider with <b>type</b> has exact type or contains any of the strings in algoList.
140 * <pre>Types</pre> may be Cipher, AlgorithmParameters, KeyGenerator, Alg, Mac, SecretKeyFactory.
141 *
142 * @param algoList the types to be checked
143 * @param type the type is ignored if not exact, instead uses the two types: "AlgorithmParameters", "Cipher".
144 * @param exact if exact does a exact match
145 * @return the matched results as a list or emtpy list
146 */
147 public static List<String> getSupportedAlgos(List<String> algoList, String type, boolean exact) {
148 List<String> result = new ArrayList<String>();
149 Provider p[] = Security.getProviders();
150 List<Provider> providerList = Arrays.asList(p);
151
152 for (Provider provider : providerList) {
153 //System.out.println(provider);
154 result.addAll(Collections.list(provider.keys()).stream().map(t -> t.toString())
155 .filter(x->
156 (exact)?
157 (x.startsWith(type) && algoList.contains(x.replaceAll(type + ".", ""))):
158 (x.matches("(" +String.join("|", PROVIDER_TYPES) + ").*$") &&
159 algoList.stream().anyMatch(y -> y.contains(x.replaceAll(type + ".", "")))
160 )
161 )
162 .map( x ->
163 (exact)?
164 x.replaceAll(type + ".", ""):
165 x.replaceAll("(" +String.join("|", PROVIDER_TYPES) + ")" + ".", "")
166 )
167 .collect(Collectors.toList()));
168 }
169 return result;
170 }
171
172 public static List[] LISTS = { Arrays.stream(CryptoParametersJ8.TYPES.values()).map(t -> t.toString())
173 .collect(Collectors.toList()),
174 Arrays.stream(CryptoParametersJ8.TYPES_IMPL.values()).map(t -> t.toString())
175 .collect(Collectors.toList()) };
176
177 public static String[] PROVIDER_TYPES = { "AlgorithmParameters", "Cipher" };
178
179 /**
180 * initializes supported parameters by filtering {@link TYPES} against <i>AlgorithmParameters</i> in system supported cipher suites:
181 * first by an exact match with type <i>AlgorithmParameters</i>, then by inexact matching.
182 *
183 * {@link #getSupportedAlgos(List, String, boolean)}
184 * @return list of supported algo short codes, if nothing is found, the list is empty.
185 */
186 static List<String> init() {
187 List<String> result = new ArrayList<String>();
188 List<String> defaultSupportedTypes = LISTS[0];
189 String providerType = PROVIDER_TYPES[0];
190 result = getSupportedAlgos(defaultSupportedTypes, providerType, true);
191 // no duplicates
192 Set<String> resultSet = new LinkedHashSet<String>(result);
193 resultSet.addAll( getSupportedAlgos(defaultSupportedTypes, providerType, false));
194
195 List<String> algoList = LISTS[1];
196 String type = PROVIDER_TYPES[1];
197 List<String> result3 = CryptoParametersJ8.getSupportedAlgos(algoList, type, true);
198 defaultSupportedTypes.stream().forEach(c-> {
199 if (result3.stream().anyMatch(x -> x.contains(c))) {
200 //System.out.println("adding " + c);
201 resultSet.add(c);
202 }
203 });
204 return new ArrayList<>(resultSet);
205 }
206
207 }