001 /*
002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003 * Copyright (C) 2011 NightLabs Consulting GmbH
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program. If not, see <http://www.gnu.org/licenses/>.
017 */
018 package org.cumulus4j.crypto;
019
020 import java.io.IOException;
021 import java.lang.reflect.Constructor;
022 import java.security.NoSuchAlgorithmException;
023 import java.util.Arrays;
024 import java.util.Collections;
025 import java.util.HashMap;
026 import java.util.HashSet;
027 import java.util.Locale;
028 import java.util.Map;
029 import java.util.Set;
030 import java.util.SortedSet;
031 import java.util.TreeSet;
032
033 import javax.crypto.NoSuchPaddingException;
034
035 import org.bouncycastle.asn1.DERNull;
036 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
037 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
038 import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
039 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
040 import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
041 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
042 import org.bouncycastle.crypto.AsymmetricBlockCipher;
043 import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
044 import org.bouncycastle.crypto.BlockCipher;
045 import org.bouncycastle.crypto.BufferedAsymmetricBlockCipher;
046 import org.bouncycastle.crypto.BufferedBlockCipher;
047 import org.bouncycastle.crypto.CipherParameters;
048 import org.bouncycastle.crypto.StreamCipher;
049 import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
050 import org.bouncycastle.crypto.encodings.OAEPEncoding;
051 import org.bouncycastle.crypto.encodings.PKCS1Encoding;
052 import org.bouncycastle.crypto.engines.AESEngine;
053 import org.bouncycastle.crypto.engines.AESFastEngine;
054 import org.bouncycastle.crypto.engines.AESLightEngine;
055 import org.bouncycastle.crypto.engines.BlowfishEngine;
056 import org.bouncycastle.crypto.engines.CAST5Engine;
057 import org.bouncycastle.crypto.engines.CAST6Engine;
058 import org.bouncycastle.crypto.engines.CamelliaEngine;
059 import org.bouncycastle.crypto.engines.CamelliaLightEngine;
060 import org.bouncycastle.crypto.engines.DESEngine;
061 import org.bouncycastle.crypto.engines.DESedeEngine;
062 import org.bouncycastle.crypto.engines.ElGamalEngine;
063 import org.bouncycastle.crypto.engines.GOST28147Engine;
064 import org.bouncycastle.crypto.engines.Grain128Engine;
065 import org.bouncycastle.crypto.engines.Grainv1Engine;
066 import org.bouncycastle.crypto.engines.HC128Engine;
067 import org.bouncycastle.crypto.engines.HC256Engine;
068 import org.bouncycastle.crypto.engines.ISAACEngine;
069 import org.bouncycastle.crypto.engines.NaccacheSternEngine;
070 import org.bouncycastle.crypto.engines.NoekeonEngine;
071 import org.bouncycastle.crypto.engines.NullEngine;
072 import org.bouncycastle.crypto.engines.RC2Engine;
073 import org.bouncycastle.crypto.engines.RC4Engine;
074 import org.bouncycastle.crypto.engines.RC532Engine;
075 import org.bouncycastle.crypto.engines.RC564Engine;
076 import org.bouncycastle.crypto.engines.RC6Engine;
077 import org.bouncycastle.crypto.engines.RSABlindedEngine;
078 import org.bouncycastle.crypto.engines.RijndaelEngine;
079 import org.bouncycastle.crypto.engines.SEEDEngine;
080 import org.bouncycastle.crypto.engines.Salsa20Engine;
081 import org.bouncycastle.crypto.engines.SerpentEngine;
082 import org.bouncycastle.crypto.engines.SkipjackEngine;
083 import org.bouncycastle.crypto.engines.TEAEngine;
084 import org.bouncycastle.crypto.engines.TwofishEngine;
085 import org.bouncycastle.crypto.engines.XTEAEngine;
086 import org.bouncycastle.crypto.modes.AEADBlockCipher;
087 import org.bouncycastle.crypto.modes.CBCBlockCipher;
088 import org.bouncycastle.crypto.modes.CCMBlockCipher;
089 import org.bouncycastle.crypto.modes.CTSBlockCipher;
090 import org.bouncycastle.crypto.modes.EAXBlockCipher;
091 import org.bouncycastle.crypto.modes.GCMBlockCipher;
092 import org.bouncycastle.crypto.modes.GOFBBlockCipher;
093 import org.bouncycastle.crypto.modes.SICBlockCipher;
094 import org.bouncycastle.crypto.paddings.BlockCipherPadding;
095 import org.bouncycastle.crypto.paddings.ISO10126d2Padding;
096 import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
097 import org.bouncycastle.crypto.paddings.PKCS7Padding;
098 import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
099 import org.bouncycastle.crypto.paddings.TBCPadding;
100 import org.bouncycastle.crypto.paddings.X923Padding;
101 import org.bouncycastle.crypto.paddings.ZeroBytePadding;
102 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
103 import org.bouncycastle.crypto.params.RSAKeyParameters;
104 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
105 import org.bouncycastle.crypto.util.PrivateKeyFactory;
106 import org.bouncycastle.crypto.util.PublicKeyFactory;
107 import org.cumulus4j.crypto.internal.asymmetric.AsymmetricBlockCipherImpl;
108 import org.cumulus4j.crypto.internal.asymmetric.keypairgenerator.DHBasicKeyPairGeneratorFactory;
109 import org.cumulus4j.crypto.internal.asymmetric.keypairgenerator.DSAKeyPairGeneratorFactory;
110 import org.cumulus4j.crypto.internal.asymmetric.keypairgenerator.ElGamalKeyPairGeneratorFactory;
111 import org.cumulus4j.crypto.internal.asymmetric.keypairgenerator.GOST3410KeyPairGeneratorFactory;
112 import org.cumulus4j.crypto.internal.asymmetric.keypairgenerator.NaccacheSternKeyPairGeneratorFactory;
113 import org.cumulus4j.crypto.internal.asymmetric.keypairgenerator.RSAKeyPairGeneratorFactory;
114 import org.cumulus4j.crypto.internal.mac.MACCalculatorFactoryImpl;
115 import org.cumulus4j.crypto.internal.symmetric.AEADBlockCipherImpl;
116 import org.cumulus4j.crypto.internal.symmetric.BufferedBlockCipherImpl;
117 import org.cumulus4j.crypto.internal.symmetric.SecretKeyGeneratorImpl;
118 import org.cumulus4j.crypto.internal.symmetric.StreamCipherImpl;
119 import org.cumulus4j.crypto.internal.symmetric.mode.C4jCBCCTSBlockCipher;
120 import org.cumulus4j.crypto.internal.symmetric.mode.C4jCFBBlockCipher;
121 import org.cumulus4j.crypto.internal.symmetric.mode.C4jOFBBlockCipher;
122 import org.slf4j.Logger;
123 import org.slf4j.LoggerFactory;
124
125 /**
126 * <p>
127 * Entry to the unified crypto API.
128 * </p>
129 * <p>
130 * This registry can be used for various cryptography-related tasks. For example to {@link #createCipher(String) create a cipher}
131 * or to {@link #createKeyPairGenerator(String, boolean) create a key-pair-generator}.
132 * </p>
133 *
134 * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
135 */
136 public final class CryptoRegistry
137 {
138 private static final Logger logger = LoggerFactory.getLogger(CryptoRegistry.class);
139 private static CryptoRegistry sharedInstance = new CryptoRegistry();
140
141 /**
142 * Get the shared instance of this registry.
143 * @return the shared instance.
144 */
145 public static CryptoRegistry sharedInstance()
146 {
147 return sharedInstance;
148 }
149
150 //////////////////// BEGIN cipher engines ////////////////////
151 private Map<String, Class<? extends AsymmetricBlockCipher>> algorithmName2asymmetricBlockCipherEngineClass = new HashMap<String, Class<? extends AsymmetricBlockCipher>>();
152 private Map<String, Class<? extends BlockCipher>> algorithmName2blockCipherEngineClass = new HashMap<String, Class<? extends BlockCipher>>();
153 private Map<String, Class<? extends StreamCipher>> algorithmName2streamCipherEngineClass = new HashMap<String, Class<? extends StreamCipher>>();
154
155 private void registerBlockCipherEngineClass(Class<? extends BlockCipher> engineClass)
156 {
157 BlockCipher engine = newInstance(engineClass);
158 String algorithmName = engine.getAlgorithmName();
159 logger.trace("registerSymmetricEngineClass: algorithmName=\"{}\" engineClass=\"{}\"", algorithmName, engineClass.getName());
160 algorithmName2blockCipherEngineClass.put(algorithmName.toUpperCase(Locale.ENGLISH), engineClass);
161 }
162
163 private void registerBlockCipherEngineClass(String algorithmName, Class<? extends BlockCipher> engineClass)
164 {
165 newInstance(engineClass); // for testing, if the default constructor can be used, only - instance is not used
166 logger.trace("registerSymmetricEngineClass: algorithmName=\"{}\" engineClass=\"{}\"", algorithmName, engineClass.getName());
167 algorithmName2blockCipherEngineClass.put(algorithmName.toUpperCase(Locale.ENGLISH), engineClass);
168 }
169
170 private void registerAsymmetricBlockCipherEngineClass(String algorithmName, Class<? extends AsymmetricBlockCipher> engineClass)
171 {
172 newInstance(engineClass); // for testing to be sure there is a default constructor and we can call it.
173 logger.trace("registerAsymmetricEngineClass: algorithmName=\"{}\" engineClass=\"{}\"", algorithmName, engineClass.getName());
174 algorithmName2asymmetricBlockCipherEngineClass.put(algorithmName.toUpperCase(Locale.ENGLISH), engineClass);
175 }
176
177 private void registerStreamCipherEngineClass(Class<? extends StreamCipher> engineClass)
178 {
179 StreamCipher engine = newInstance(engineClass);
180 String algorithmName = engine.getAlgorithmName();
181 _registerStreamCipherEngineClass(algorithmName, engineClass);
182 }
183
184 private void registerStreamCipherEngineClass(String algorithmName, Class<? extends StreamCipher> engineClass)
185 {
186 newInstance(engineClass); // for testing to be sure there is a default constructor and we can call it.
187 _registerStreamCipherEngineClass(algorithmName, engineClass);
188 }
189
190 private void _registerStreamCipherEngineClass(String algorithmName, Class<? extends StreamCipher> engineClass)
191 {
192 if (algorithmName == null)
193 throw new IllegalArgumentException("algorithmName == null");
194
195 if (engineClass == null)
196 throw new IllegalArgumentException("engineClass == null");
197
198 logger.trace("registerSymmetricEngineClass: algorithmName=\"{}\" engineClass=\"{}\"", algorithmName, engineClass.getName());
199 algorithmName2streamCipherEngineClass.put(algorithmName.toUpperCase(Locale.ENGLISH), engineClass);
200 }
201 //////////////////// END cipher engines ////////////////////
202
203
204 //////////////////// BEGIN block cipher modes ////////////////////
205 private Map<String, Class<? extends BlockCipher>> modeName2blockCipherModeClass = new HashMap<String, Class<? extends BlockCipher>>();
206 private Map<String, Class<? extends BufferedBlockCipher>> modeName2bufferedBlockCipherModeClass = new HashMap<String, Class<? extends BufferedBlockCipher>>();
207 private Map<String, Class<? extends AEADBlockCipher>> modeName2aeadBlockCipherModeClass = new HashMap<String, Class<? extends AEADBlockCipher>>();
208
209 private void registerBlockCipherMode(String modeName, Class<? extends BlockCipher> modeClass)
210 {
211 logger.trace("registerBlockCipherMode: modeName=\"{}\" modeClass=\"{}\"", modeName, modeClass.getName());
212 modeName2blockCipherModeClass.put(modeName.toUpperCase(Locale.ENGLISH), modeClass);
213 }
214 private void registerBufferedBlockCipherMode(String modeName, Class<? extends BufferedBlockCipher> modeClass)
215 {
216 logger.trace("registerBufferedBlockCipherMode: modeName=\"{}\" modeClass=\"{}\"", modeName, modeClass.getName());
217 modeName2bufferedBlockCipherModeClass.put(modeName.toUpperCase(Locale.ENGLISH), modeClass);
218 }
219 private void registerAEADBlockCipherMode(String modeName, Class<? extends AEADBlockCipher> modeClass)
220 {
221 logger.trace("registerAEADBlockCipherMode: modeName=\"{}\" modeClass=\"{}\"", modeName, modeClass.getName());
222 modeName2aeadBlockCipherModeClass.put(modeName.toUpperCase(Locale.ENGLISH), modeClass);
223 }
224 //////////////////// END block cipher modes ////////////////////
225
226
227 //////////////////// BEGIN block cipher paddings ////////////////////
228 private Map<String, Class<? extends BlockCipherPadding>> paddingName2blockCipherPaddingClass = new HashMap<String, Class<? extends BlockCipherPadding>>();
229 private void registerBlockCipherPadding(Class<? extends BlockCipherPadding> paddingClass)
230 {
231 BlockCipherPadding padding = newInstance(paddingClass);
232 String paddingName = padding.getPaddingName();
233 logger.debug("registerBlockCipherPadding: paddingName=\"{}\" paddingClass=\"{}\"", paddingName, paddingClass.getName());
234 paddingName2blockCipherPaddingClass.put(paddingName.toUpperCase(Locale.ENGLISH), paddingClass);
235 paddingName2blockCipherPaddingClass.put((paddingName + "Padding").toUpperCase(Locale.ENGLISH), paddingClass);
236 }
237 private void registerBlockCipherPadding(String paddingName, Class<? extends BlockCipherPadding> paddingClass)
238 {
239 newInstance(paddingClass); // for testing to be sure there is a default constructor and we can call it.
240 logger.trace("registerBlockCipherPadding: paddingName=\"{}\" paddingClass=\"{}\"", paddingName, paddingClass.getName());
241 paddingName2blockCipherPaddingClass.put(paddingName.toUpperCase(Locale.ENGLISH), paddingClass);
242 paddingName2blockCipherPaddingClass.put((paddingName + "Padding").toUpperCase(Locale.ENGLISH), paddingClass);
243 }
244 //////////////////// END block cipher paddings ////////////////////
245
246
247 //////////////////// BEGIN asymmetric paddings ////////////////////
248 private Map<String, Class<? extends AsymmetricBlockCipher>> paddingName2asymmetricBlockCipherPaddingClass = new HashMap<String, Class<? extends AsymmetricBlockCipher>>();
249 private void registerAsymmetricBlockCipherPadding(String paddingName, Class<? extends AsymmetricBlockCipher> paddingClass)
250 {
251 logger.trace("registerAsymmetricBlockCipherPadding: paddingName=\"{}\" paddingClass=\"{}\"", paddingName, paddingClass.getName());
252 paddingName2asymmetricBlockCipherPaddingClass.put(paddingName.toUpperCase(Locale.ENGLISH), paddingClass);
253 paddingName2asymmetricBlockCipherPaddingClass.put((paddingName + "Padding").toUpperCase(Locale.ENGLISH), paddingClass);
254 }
255 //////////////////// END asymmetric paddings ////////////////////
256
257
258 //////////////////// BEGIN asymmetric key generators ////////////////////
259 private Map<String, AsymmetricCipherKeyPairGeneratorFactory> algorithmName2asymmetricCipherKeyPairGeneratorFactory = new HashMap<String, AsymmetricCipherKeyPairGeneratorFactory>();
260 private void registerAsymmetricCipherKeyPairGeneratorFactory(AsymmetricCipherKeyPairGeneratorFactory factory)
261 {
262 if (factory == null)
263 throw new IllegalArgumentException("factory == null");
264
265 if (factory.getAlgorithmName() == null)
266 throw new IllegalArgumentException("factory.getAlgorithmName() == null");
267
268 logger.trace("registerAsymmetricCipherKeyPairGeneratorFactory: algorithmName=\"{}\" factoryClass=\"{}\"", factory.getAlgorithmName(), factory.getClass().getName());
269 algorithmName2asymmetricCipherKeyPairGeneratorFactory.put(factory.getAlgorithmName(), factory);
270 }
271 //////////////////// END asymmetric key generators ////////////////////
272
273
274 private CryptoRegistry() {
275 // *** BEGIN AsymmetricBlockCipher engines ***
276 registerAsymmetricBlockCipherEngineClass("ElGamal", ElGamalEngine.class);
277 registerAsymmetricBlockCipherEngineClass("NaccacheStern", NaccacheSternEngine.class);
278
279 // According to the JCERSACipher class, the RSABlindedEngine is used for RSA in the JCE, thus commenting out the other two.
280 registerAsymmetricBlockCipherEngineClass("RSA", RSABlindedEngine.class);
281 // registerAsymmetricBlockCipherEngineClass("RSA", RSABlindingEngine.class);
282 // registerAsymmetricBlockCipherEngineClass("RSA", RSAEngine.class);
283 // *** END AsymmetricBlockCipher engines ***
284
285 // *** BEGIN BlockCipher engines ***
286 registerBlockCipherEngineClass(AESEngine.class);
287 // We register the other two AES implementations under alternative names.
288 registerBlockCipherEngineClass("AES.fast", AESFastEngine.class);
289 registerBlockCipherEngineClass("AES.light", AESLightEngine.class);
290
291 registerBlockCipherEngineClass(BlowfishEngine.class);
292 registerBlockCipherEngineClass(CamelliaEngine.class);
293 // Registering the alternative implementation under an alternative name.
294 registerBlockCipherEngineClass("Camellia.light", CamelliaLightEngine.class);
295
296 registerBlockCipherEngineClass(CAST5Engine.class);
297 registerBlockCipherEngineClass(CAST6Engine.class);
298 registerBlockCipherEngineClass(DESedeEngine.class);
299 registerBlockCipherEngineClass(DESEngine.class);
300 registerBlockCipherEngineClass(GOST28147Engine.class);
301 // IDEA is only in the "ext" BouncyCastle lib - not in the normal one. I think it's not needed, anyway. ...at least for now...
302 // registerBlockCipherEngineClass(IDEAEngine.class);
303 registerBlockCipherEngineClass(NoekeonEngine.class);
304 registerBlockCipherEngineClass(NullEngine.class);
305 registerBlockCipherEngineClass(RC2Engine.class);
306 registerBlockCipherEngineClass(RC532Engine.class);
307 registerBlockCipherEngineClass(RC564Engine.class);
308 registerBlockCipherEngineClass(RC6Engine.class);
309 registerBlockCipherEngineClass(RijndaelEngine.class);
310 registerBlockCipherEngineClass(SEEDEngine.class);
311 registerBlockCipherEngineClass(SerpentEngine.class);
312 registerBlockCipherEngineClass(SkipjackEngine.class);
313 registerBlockCipherEngineClass(TEAEngine.class);
314 registerBlockCipherEngineClass(TwofishEngine.class);
315 // registerSymmetricEngineClass(VMPCEngine.class);
316 // registerSymmetricEngineClass(VMPCKSA3Engine.class);
317 registerBlockCipherEngineClass(XTEAEngine.class);
318 // *** END BlockCipher engines ***
319
320
321 // *** BEGIN StreamCipher engines ***
322 registerStreamCipherEngineClass(Grain128Engine.class);
323 registerStreamCipherEngineClass("GRAIN-V1", Grainv1Engine.class);
324 registerStreamCipherEngineClass(HC128Engine.class);
325 registerStreamCipherEngineClass(HC256Engine.class);
326 registerStreamCipherEngineClass(ISAACEngine.class);
327 registerStreamCipherEngineClass(RC4Engine.class);
328 registerStreamCipherEngineClass(Salsa20Engine.class);
329 // *** END StreamCipher engines ***
330
331
332 // *** Wrap engines ***
333 // register___(AESWrapEngine.class);
334 // register___(CamelliaWrapEngine.class);
335 // register___(DESedeWrapEngine.class);
336 // register___(RC2WrapEngine.class);
337 // register___(RFC3211WrapEngine.class);
338 // register___(RFC3394WrapEngine.class);
339 // register___(SEEDWrapEngine.class);
340
341 // *** Other stuff ***
342 // register___(IESEngine.class);
343
344
345
346 // *** BEGIN block cipher modes ***
347 registerBlockCipherMode("CBC", CBCBlockCipher.class);
348 registerAEADBlockCipherMode("CCM", CCMBlockCipher.class);
349
350 registerBlockCipherMode("CFB", C4jCFBBlockCipher.class);
351 for (int i = 1; i <= 32; ++i)
352 registerBlockCipherMode("CFB" + (i * 8), C4jCFBBlockCipher.class);
353
354 registerBufferedBlockCipherMode("CTS", CTSBlockCipher.class);
355 registerBufferedBlockCipherMode("CBC-CTS", C4jCBCCTSBlockCipher.class);
356
357 registerAEADBlockCipherMode("EAX", EAXBlockCipher.class);
358 registerAEADBlockCipherMode("GCM", GCMBlockCipher.class);
359 registerBlockCipherMode("GOFB", GOFBBlockCipher.class);
360
361 registerBlockCipherMode("OFB", C4jOFBBlockCipher.class);
362 for (int i = 1; i <= 32; ++i)
363 registerBlockCipherMode("OFB" + (i * 8), C4jOFBBlockCipher.class);
364
365 // registerBlockCipherMode("OpenPGPCFB", OpenPGPCFBBlockCipher.class);
366 // registerBlockCipherMode("PGPCFB", PGPCFBBlockCipher.class);
367 registerBlockCipherMode("SIC", SICBlockCipher.class);
368
369 // Test all registered BlockCipherModes - MUST BE HERE AFTER THEIR REGISTRATION
370 testBlockCipherModes();
371 // *** END block cipher modes ***
372
373 // *** BEGIN block cipher paddings ***
374 registerBlockCipherPadding(ISO10126d2Padding.class);
375 registerBlockCipherPadding("ISO10126", ISO10126d2Padding.class);
376 registerBlockCipherPadding(ISO7816d4Padding.class);
377 registerBlockCipherPadding(PKCS7Padding.class);
378 registerBlockCipherPadding("PKCS5", PKCS7Padding.class);
379 registerBlockCipherPadding(TBCPadding.class);
380 registerBlockCipherPadding(X923Padding.class);
381 registerBlockCipherPadding(ZeroBytePadding.class);
382 // *** END block cipher paddings ***
383
384
385 // *** BEGIN asymmetric paddings ***
386 registerAsymmetricBlockCipherPadding("ISO9796-1", ISO9796d1Encoding.class);
387 registerAsymmetricBlockCipherPadding("OAEP", OAEPEncoding.class);
388 registerAsymmetricBlockCipherPadding("OAEPWITHSHA1ANDMGF1", OAEPEncoding.class); // JCE name for compatibility.
389 registerAsymmetricBlockCipherPadding("PKCS1", PKCS1Encoding.class);
390
391
392 // Test all registered asymmetric paddings - MUST BE HERE AFTER THEIR REGISTRATION
393 testAsymmetricBlockCipherPaddings();
394 // *** END asymmetric paddings ***
395
396 // *** BEGIN asymmetric key pair generators ***
397 registerAsymmetricCipherKeyPairGeneratorFactory(new DHBasicKeyPairGeneratorFactory());
398 registerAsymmetricCipherKeyPairGeneratorFactory(new DSAKeyPairGeneratorFactory());
399 registerAsymmetricCipherKeyPairGeneratorFactory(new ElGamalKeyPairGeneratorFactory());
400 registerAsymmetricCipherKeyPairGeneratorFactory(new GOST3410KeyPairGeneratorFactory());
401 registerAsymmetricCipherKeyPairGeneratorFactory(new NaccacheSternKeyPairGeneratorFactory());
402 registerAsymmetricCipherKeyPairGeneratorFactory(new RSAKeyPairGeneratorFactory());
403 // *** END asymmetric key pair generators ***
404 }
405
406 private void testAsymmetricBlockCipherPaddings()
407 {
408 AsymmetricBlockCipher engine = createAsymmetricBlockCipherEngine("RSA");
409 if (engine == null)
410 throw new IllegalStateException("No engine!");
411
412 for (String paddingName : paddingName2asymmetricBlockCipherPaddingClass.keySet())
413 createAsymmetricBlockCipherPadding(paddingName, engine);
414 }
415
416 private void testBlockCipherModes()
417 {
418 BlockCipher engine8 = createBlockCipherEngine("Blowfish".toUpperCase(Locale.ENGLISH));
419 if (engine8 == null)
420 throw new IllegalStateException("No 'Blowfish' engine!");
421
422 BlockCipher engine16 = createBlockCipherEngine("AES".toUpperCase(Locale.ENGLISH));
423 if (engine16 == null)
424 throw new IllegalStateException("No 'AES' engine!");
425
426 for (String modeName : modeName2blockCipherModeClass.keySet())
427 createBlockCipherMode(modeName, engine8);
428
429 for (String modeName : modeName2bufferedBlockCipherModeClass.keySet())
430 createBufferedBlockCipherMode(modeName, engine8);
431
432 for (String modeName : modeName2aeadBlockCipherModeClass.keySet())
433 createAEADBlockCipherMode(modeName, engine16); // Most of these modes require a block-size of 16!
434 }
435
436 private <T> T newInstance(Class<T> clazz)
437 {
438 try {
439 return clazz.newInstance();
440 } catch (InstantiationException e) {
441 throw new RuntimeException(e);
442 } catch (IllegalAccessException e) {
443 throw new RuntimeException(e);
444 }
445 }
446
447 /**
448 * @param algorithmName the simple encryption algorithm name (e.g. "AES" or "Twofish") and <b>not</b> the complete transformation.
449 * @return
450 */
451 private BlockCipher createBlockCipherEngine(String algorithmName)
452 {
453 Class<? extends BlockCipher> engineClass = algorithmName2blockCipherEngineClass.get(algorithmName);
454 if (engineClass == null)
455 return null;
456
457 return newInstance(engineClass);
458 }
459
460 private AsymmetricBlockCipher createAsymmetricBlockCipherEngine(String algorithmName)
461 {
462 Class<? extends AsymmetricBlockCipher> engineClass = algorithmName2asymmetricBlockCipherEngineClass.get(algorithmName);
463 if (engineClass == null)
464 return null;
465
466 return newInstance(engineClass);
467 }
468
469 private StreamCipher createStreamCipherEngine(String algorithmName)
470 throws NoSuchAlgorithmException
471 {
472 Class<? extends StreamCipher> engineClass = algorithmName2streamCipherEngineClass.get(algorithmName);
473 if (engineClass == null)
474 return null;
475
476 return newInstance(engineClass);
477 }
478
479 private BlockCipher createBlockCipherMode(String modeName, BlockCipher engine)
480 {
481 Class<? extends BlockCipher> modeClass = modeName2blockCipherModeClass.get(modeName);
482 if (modeClass == null)
483 return null;
484
485 try {
486 Constructor<? extends BlockCipher> c = modeClass.getConstructor(BlockCipher.class, String.class);
487 return c.newInstance(engine, modeName);
488 } catch (NoSuchMethodException x) {
489 silentlyIgnore(); // We'll try it with the constructor without mode.
490 } catch (Exception e) {
491 throw new RuntimeException(e);
492 }
493
494 try {
495 Constructor<? extends BlockCipher> c = modeClass.getConstructor(BlockCipher.class);
496 return c.newInstance(engine);
497 } catch (Exception e) {
498 throw new RuntimeException(e);
499 }
500 }
501
502 private static void silentlyIgnore() { } // this method does not need to be marked 'final', because the class is.
503
504 private AEADBlockCipher createAEADBlockCipherMode(String modeName, BlockCipher engine)
505 {
506 Class<? extends AEADBlockCipher> modeClass = modeName2aeadBlockCipherModeClass.get(modeName);
507 if (modeClass == null)
508 return null;
509
510 try {
511 Constructor<? extends AEADBlockCipher> c = modeClass.getConstructor(BlockCipher.class);
512 return c.newInstance(engine);
513 } catch (Exception e) {
514 throw new RuntimeException(e);
515 }
516 }
517
518 private BufferedBlockCipher createBufferedBlockCipherMode(String modeName, BlockCipher engine)
519 {
520 Class<? extends BufferedBlockCipher> modeClass = modeName2bufferedBlockCipherModeClass.get(modeName);
521 if (modeClass == null)
522 return null;
523
524 try {
525 Constructor<? extends BufferedBlockCipher> c = modeClass.getConstructor(BlockCipher.class);
526 return c.newInstance(engine);
527 } catch (Exception e) {
528 throw new RuntimeException(e);
529 }
530 }
531
532 private BlockCipherPadding createBlockCipherPadding(String paddingName)
533 {
534 Class<? extends BlockCipherPadding> paddingClass = paddingName2blockCipherPaddingClass.get(paddingName);
535 if (paddingClass == null)
536 return null;
537
538 return newInstance(paddingClass);
539 }
540
541 private Cipher createCipherForBlockCipherMode(String transformation, BlockCipher modeWithEngine, String engineName, String modeName, String paddingName)
542 throws NoSuchPaddingException
543 {
544 if (paddingName.isEmpty() || "NOPADDING".equals(paddingName))
545 return new BufferedBlockCipherImpl(transformation, new BufferedBlockCipher(modeWithEngine));
546
547 BlockCipherPadding padding = createBlockCipherPadding(paddingName);
548 if (padding == null)
549 throw new NoSuchPaddingException("There is no block-cipher-padding class registed with the name \"" + paddingName + "\"!");
550
551 return new BufferedBlockCipherImpl(transformation, new PaddedBufferedBlockCipher(modeWithEngine, padding));
552 }
553
554 private Cipher createCipherForBlockCipherMode(String transformation, AEADBlockCipher modeWithEngine, String engineName, String modeName, String paddingName)
555 throws NoSuchPaddingException
556 {
557 if (paddingName.isEmpty() || "NOPADDING".equals(paddingName))
558 return new AEADBlockCipherImpl(transformation, modeWithEngine);
559
560 throw new NoSuchPaddingException("The AEAD-mode \"" + modeName + "\" does not support the padding \"" + paddingName + "\"! Padding must be \"NoPadding\" or an empty string!");
561 }
562
563 private Cipher createCipherForBlockCipherMode(String transformation, BufferedBlockCipher modeWithEngine, String engineName, String modeName, String paddingName)
564 throws NoSuchPaddingException
565 {
566 if (paddingName.isEmpty() || "NOPADDING".equals(paddingName))
567 return new BufferedBlockCipherImpl(transformation, modeWithEngine);
568
569 throw new NoSuchPaddingException("The block-cipher-mode \"" + modeName + "\" does not support the padding \"" + paddingName + "\"! Padding must be \"NoPadding\" or an empty string!");
570 }
571
572 private Cipher createCipherForBlockCipherEngine(String transformation, BlockCipher engine, String engineName, String modeName, String paddingName)
573 throws NoSuchAlgorithmException, NoSuchPaddingException
574 {
575 if (modeName.isEmpty() || "ECB".equals(modeName))
576 return createCipherForBlockCipherMode(transformation, engine, engineName, modeName, paddingName);
577
578 {
579 BlockCipher mode = createBlockCipherMode(modeName, engine);
580 if (mode != null)
581 return createCipherForBlockCipherMode(transformation, mode, engineName, modeName, paddingName);
582 }
583
584 {
585 BufferedBlockCipher mode = createBufferedBlockCipherMode(modeName, engine);
586 if (mode != null)
587 return createCipherForBlockCipherMode(transformation, mode, engineName, modeName, paddingName);
588 }
589
590 {
591 AEADBlockCipher mode = createAEADBlockCipherMode(modeName, engine);
592 if (mode != null)
593 return createCipherForBlockCipherMode(transformation, mode, engineName, modeName, paddingName);
594 }
595
596 throw new NoSuchAlgorithmException("There is no block-cipher-mode-class registered with the modeName \"" + modeName + "\"!");
597 }
598
599 private Cipher createCipherForStreamCipherMode(String transformation, StreamCipher modeWithEngine, String engineName, String modeName, String paddingName)
600 throws NoSuchPaddingException
601 {
602 if (paddingName.isEmpty() || "NOPADDING".equals(paddingName))
603 return new StreamCipherImpl(transformation, modeWithEngine);
604
605 throw new NoSuchPaddingException("The stream-cipher-mode \"" + modeName + "\" does not support the padding \"" + paddingName + "\"! Padding must be \"NoPadding\" or an empty string!");
606 }
607
608 private Cipher createCipherForStreamCipherEngine(String transformation, StreamCipher engine, String engineName, String modeName, String paddingName)
609 throws NoSuchAlgorithmException, NoSuchPaddingException
610 {
611 if (modeName.isEmpty() || "ECB".equals(modeName))
612 return createCipherForStreamCipherMode(transformation, engine, engineName, modeName, paddingName);
613
614 throw new NoSuchAlgorithmException("The stream-cipher does not support the mode \"" + modeName + "\"! Only \"ECB\" or an empty string are allowed as mode!");
615 }
616
617 private AsymmetricBlockCipher createAsymmetricBlockCipherPadding(String paddingName, AsymmetricBlockCipher engine)
618 {
619 Class<? extends AsymmetricBlockCipher> paddingClass = paddingName2asymmetricBlockCipherPaddingClass.get(paddingName);
620 if (paddingClass == null)
621 return null;
622
623 try {
624 Constructor<? extends AsymmetricBlockCipher> c = paddingClass.getConstructor(AsymmetricBlockCipher.class);
625 return c.newInstance(engine);
626 } catch (Exception e) {
627 throw new RuntimeException(e);
628 }
629 }
630
631 private Cipher createCipherForAsymmetricBlockCipherMode(String transformation, AsymmetricBlockCipher modeWithEngine, String engineName, String modeName, String paddingName)
632 throws NoSuchPaddingException
633 {
634 AsymmetricBlockCipher padding;
635 if (paddingName.isEmpty() || "NOPADDING".equals(paddingName))
636 padding = modeWithEngine;
637 else {
638 padding = createAsymmetricBlockCipherPadding(paddingName, modeWithEngine);
639 if (padding == null)
640 throw new NoSuchPaddingException("There is no asymmetric-block-cipher-padding registered with name \"" + paddingName + "\"!");
641 }
642
643 return new AsymmetricBlockCipherImpl(
644 transformation,
645 new BufferedAsymmetricBlockCipher(padding)
646 );
647 }
648
649 private Cipher createCipherForAsymmetricBlockCipherEngine(String transformation, AsymmetricBlockCipher engine, String engineName, String modeName, String paddingName)
650 throws NoSuchAlgorithmException, NoSuchPaddingException
651 {
652 if (modeName.isEmpty() || "ECB".equals(modeName))
653 return createCipherForAsymmetricBlockCipherMode(transformation, engine, engineName, modeName, paddingName);
654
655 throw new NoSuchAlgorithmException("The asymmetric-block-cipher does not support the mode \"" + modeName + "\"! Only \"ECB\" or an empty string are allowed as mode!");
656 }
657
658 /**
659 * Get all supported cipher engines. A cipher engine implements a raw
660 * <a target="_blank" href="http://en.wikipedia.org/wiki/Encryption_algorithm">encryption algorithm</a>;
661 * 'raw' means without any additional transformation like block mode or padding.
662 *
663 * @param cipherEngineType the type of the cipher engine or <code>null</code> to list all.
664 * @return all supported cipher engines for the (optionally) given criteria.
665 * @see #createCipher(String)
666 */
667 public Set<String> getSupportedCipherEngines(CipherEngineType cipherEngineType)
668 {
669 SortedSet<String> result = new TreeSet<String>();
670
671 if (cipherEngineType == null || cipherEngineType == CipherEngineType.symmetricBlock)
672 result.addAll(algorithmName2blockCipherEngineClass.keySet());
673
674 if (cipherEngineType == null || cipherEngineType == CipherEngineType.symmetricStream)
675 result.addAll(algorithmName2streamCipherEngineClass.keySet());
676
677 if (cipherEngineType == null || cipherEngineType == CipherEngineType.asymmetricBlock)
678 result.addAll(algorithmName2asymmetricBlockCipherEngineClass.keySet());
679
680 return Collections.unmodifiableSortedSet(result);
681 }
682
683 /**
684 * <p>
685 * Get all supported <a target="_blank" href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">modes</a> for
686 * the given cipher engine (a raw
687 * <a target="_blank" href="http://en.wikipedia.org/wiki/Encryption_algorithm">encryption algorithm</a>). The
688 * <code>cipherEngine</code> can be <code>null</code> to not restrict the result by this criterion.
689 * </p>
690 * <p>
691 * See <a target="_blank" href="http://cumulus4j.org/1.1.0/documentation/supported-algorithms.html">Supported algorithms</a>
692 * for a list of supported algorithms or use {@link #getSupportedCipherEngines(CipherEngineType)} to
693 * query them.
694 * </p>
695 *
696 * @param cipherEngine the name of the encryption algorithm for which to look up supported
697 * modes or <code>null</code> to list all.
698 * @return all supported modes for the (optionally) given criteria.
699 * @see #createCipher(String)
700 */
701 public Set<String> getSupportedCipherModes(String cipherEngine)
702 {
703 if (cipherEngine != null)
704 cipherEngine = cipherEngine.toUpperCase(Locale.ENGLISH);
705
706 SortedSet<String> result = new TreeSet<String>();
707
708 if (cipherEngine == null || algorithmName2blockCipherEngineClass.containsKey(cipherEngine)) {
709 // Engine is a block cipher => return all modes
710 result.add(""); result.add("ECB"); // both are synonymous
711 result.addAll(modeName2aeadBlockCipherModeClass.keySet());
712 result.addAll(modeName2blockCipherModeClass.keySet());
713 result.addAll(modeName2bufferedBlockCipherModeClass.keySet());
714 }
715
716 if (cipherEngine == null || algorithmName2streamCipherEngineClass.containsKey(cipherEngine)) {
717 // Engine is a stream cipher => no modes supported besides ECB (either named "ECB" or empty String).
718 result.add(""); result.add("ECB"); // both are synonymous
719 }
720
721 if (cipherEngine == null || algorithmName2asymmetricBlockCipherEngineClass.containsKey(cipherEngine)) {
722 // Engine is an asymmetric cipher => no modes supported besides ECB (either named "ECB" or empty String).
723 result.add(""); result.add("ECB"); // both are synonymous
724 }
725
726 Set<String> blackListedModes = blackListedCipherEngine2Modes.get(cipherEngine);
727 if (blackListedModes != null)
728 result.removeAll(blackListedModes);
729
730 return Collections.unmodifiableSortedSet(result);
731 }
732
733 private Map<String, Set<String>> blackListedCipherEngine2Modes = new HashMap<String, Set<String>>();
734 {
735 // Created this by trial and error. Needs to be updated, if the CipherTest fails.
736 {
737 Set<String> modes = new HashSet<String>();
738 blackListedCipherEngine2Modes.put("AES.FAST", modes);
739 modes.add("GOFB");
740 }
741
742 {
743 Set<String> modes = new HashSet<String>();
744 blackListedCipherEngine2Modes.put("AES.LIGHT", modes);
745 modes.add("GOFB");
746 }
747
748 {
749 Set<String> modes = new HashSet<String>();
750 blackListedCipherEngine2Modes.put("AES", modes);
751 modes.add("GOFB");
752 }
753
754 {
755 Set<String> modes = new HashSet<String>();
756 blackListedCipherEngine2Modes.put("BLOWFISH", modes);
757 modes.add("CCM");
758 modes.add("GCM");
759 }
760
761 {
762 Set<String> modes = new HashSet<String>();
763 blackListedCipherEngine2Modes.put("CAMELLIA.LIGHT", modes);
764 modes.add("GOFB");
765 }
766
767 {
768 Set<String> modes = new HashSet<String>();
769 blackListedCipherEngine2Modes.put("CAMELLIA", modes);
770 modes.add("GOFB");
771 }
772
773 {
774 Set<String> modes = new HashSet<String>();
775 blackListedCipherEngine2Modes.put("CAST5", modes);
776 modes.add("CCM");
777 modes.add("GCM");
778 }
779
780 {
781 Set<String> modes = new HashSet<String>();
782 blackListedCipherEngine2Modes.put("CAST6", modes);
783 modes.add("GOFB");
784 }
785
786 {
787 Set<String> modes = new HashSet<String>();
788 blackListedCipherEngine2Modes.put("DES", modes);
789 modes.add("CCM");
790 modes.add("GCM");
791 }
792
793 {
794 Set<String> modes = new HashSet<String>();
795 blackListedCipherEngine2Modes.put("DESEDE", modes);
796 modes.add("CCM");
797 modes.add("GCM");
798 }
799
800 {
801 Set<String> modes = new HashSet<String>();
802 blackListedCipherEngine2Modes.put("GOST28147", modes);
803 modes.add("CCM");
804 modes.add("GCM");
805 }
806
807 {
808 Set<String> modes = new HashSet<String>();
809 blackListedCipherEngine2Modes.put("NOEKEON", modes);
810 modes.add("GOFB");
811 }
812
813 {
814 Set<String> modes = new HashSet<String>();
815 blackListedCipherEngine2Modes.put("NULL", modes);
816 modes.add("CCM");
817 modes.add("GCM");
818 modes.add("EAX");
819 modes.add("GOFB");
820 }
821
822 {
823 Set<String> modes = new HashSet<String>();
824 blackListedCipherEngine2Modes.put("RC2", modes);
825 modes.add("CCM");
826 modes.add("GCM");
827 }
828
829 {
830 Set<String> modes = new HashSet<String>();
831 blackListedCipherEngine2Modes.put("RC5-32", modes);
832 modes.add("CCM");
833 modes.add("GCM");
834 }
835
836 {
837 Set<String> modes = new HashSet<String>();
838 blackListedCipherEngine2Modes.put("RC5-64", modes);
839 modes.add("GOFB");
840 }
841
842 {
843 Set<String> modes = new HashSet<String>();
844 blackListedCipherEngine2Modes.put("RC6", modes);
845 modes.add("GOFB");
846 }
847
848 {
849 Set<String> modes = new HashSet<String>();
850 blackListedCipherEngine2Modes.put("RIJNDAEL", modes);
851 modes.add("GOFB");
852 }
853
854 {
855 Set<String> modes = new HashSet<String>();
856 blackListedCipherEngine2Modes.put("SEED", modes);
857 modes.add("GOFB");
858 }
859
860 {
861 Set<String> modes = new HashSet<String>();
862 blackListedCipherEngine2Modes.put("SERPENT", modes);
863 modes.add("GOFB");
864 }
865
866 {
867 Set<String> modes = new HashSet<String>();
868 blackListedCipherEngine2Modes.put("SKIPJACK", modes);
869 modes.add("CCM");
870 modes.add("GCM");
871 }
872
873 {
874 Set<String> modes = new HashSet<String>();
875 blackListedCipherEngine2Modes.put("TEA", modes);
876 modes.add("CCM");
877 modes.add("GCM");
878 }
879
880 {
881 Set<String> modes = new HashSet<String>();
882 blackListedCipherEngine2Modes.put("TWOFISH", modes);
883 modes.add("GOFB");
884 }
885
886 {
887 Set<String> modes = new HashSet<String>();
888 blackListedCipherEngine2Modes.put("XTEA", modes);
889 modes.add("CCM");
890 modes.add("GCM");
891 }
892 }
893
894 /**
895 * Get all supported paddings for the given {@link CipherEngineType}. If there is
896 * no cipher-engine-type given, all supported paddings for all engine types are returned.
897 * @param cipherEngineType the type of the cipher engine or <code>null</code> to ignore this criterion.
898 * @return all supported paddings for the (optionally) given criteria.
899 * @see #createCipher(String)
900 */
901 public Set<String> getSupportedCipherPaddings(CipherEngineType cipherEngineType)
902 {
903 return getSupportedCipherPaddings(cipherEngineType, null, null);
904 }
905
906 /**
907 * <p>
908 * Get all supported paddings for the given cipher engine (a raw
909 * <a target="_blank" href="http://en.wikipedia.org/wiki/Encryption_algorithm">encryption algorithm</a>) and
910 * <a target="_blank" href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">mode</a>. Each of the
911 * parameters can be <code>null</code> to not restrict the result by this criterion.
912 * </p>
913 * <p>
914 * See <a target="_blank" href="http://cumulus4j.org/1.1.0/documentation/supported-algorithms.html">Supported algorithms</a>
915 * for a list of supported algorithms or use {@link #getSupportedCipherEngines(CipherEngineType)}
916 * and {@link #getSupportedCipherModes(String)} to
917 * query them.
918 * </p>
919 *
920 * @param cipherEngine the cipher engine for which to get the supported paddings or <code>null</code>
921 * to list all.
922 * @param cipherMode the <a target="_blank" href="http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation">mode</a>
923 * to restrict the result or <code>null</code> to list all (for the given cipher-engine).
924 * @return all supported paddings for the (optionally) given criteria.
925 * @see #createCipher(String)
926 */
927 public Set<String> getSupportedCipherPaddings(String cipherEngine, String cipherMode)
928 {
929 return getSupportedCipherPaddings(null, cipherEngine, cipherMode);
930 }
931
932 private Set<String> getSupportedCipherPaddings(CipherEngineType cipherEngineType, String cipherEngine, String cipherMode)
933 {
934 if (cipherEngine != null)
935 cipherEngine = cipherEngine.toUpperCase(Locale.ENGLISH);
936
937 if (cipherMode != null)
938 cipherMode = cipherMode.toUpperCase(Locale.ENGLISH);
939
940 SortedSet<String> result = new TreeSet<String>();
941
942 if ((cipherEngineType == null || cipherEngineType == CipherEngineType.symmetricBlock) &&
943 (cipherEngine == null || algorithmName2blockCipherEngineClass.containsKey(cipherEngine)))
944 {
945 // Engine is a block cipher
946 result.add(""); result.add("NOPADDING"); // both are synonymous
947
948 if (cipherMode == null || modeName2blockCipherModeClass.containsKey(cipherMode))
949 result.addAll(paddingName2blockCipherPaddingClass.keySet());
950 }
951
952 if ((cipherEngineType == null || cipherEngineType == CipherEngineType.symmetricStream) &&
953 (cipherEngine == null || algorithmName2streamCipherEngineClass.containsKey(cipherEngine)))
954 {
955 // Engine is a stream cipher
956 result.add(""); result.add("NOPADDING"); // both are synonymous
957 }
958
959 if ((cipherEngineType == null || cipherEngineType == CipherEngineType.asymmetricBlock) &&
960 (cipherEngine == null || algorithmName2asymmetricBlockCipherEngineClass.containsKey(cipherEngine)))
961 {
962 // Engine is an asymmetric block cipher
963 result.add(""); result.add("NOPADDING"); // both are synonymous
964 result.addAll(paddingName2asymmetricBlockCipherPaddingClass.keySet());
965 }
966
967 return Collections.unmodifiableSortedSet(result);
968 }
969
970 /**
971 * <p>
972 * Get all supported cipher transformations.
973 * </p>
974 * <p>
975 * Every element of the resulting <code>Set</code> can be passed to {@link #createCipher(String)} and will
976 * return a usable {@link Cipher} instance. However, not everything that is supported makes sense! It might
977 * not even be secure in certain situations! This is just a listing of what you theoretically could pass to
978 * {@link #createCipher(String)}.
979 * </p>
980 *
981 * @param cipherEngineType the type of the cipher engine or <code>null</code> to list all.
982 * @return all supported cipher transformations for the (optionally) given criteria.
983 * @see #createCipher(String)
984 */
985 public Set<String> getSupportedCipherTransformations(CipherEngineType cipherEngineType)
986 {
987 SortedSet<String> result = new TreeSet<String>();
988
989 for (String cipherEngine : getSupportedCipherEngines(cipherEngineType)) {
990 for (String cipherMode : getSupportedCipherModes(cipherEngine)) {
991 for (String cipherPadding : getSupportedCipherPaddings(cipherEngine, cipherMode))
992 result.add(cipherEngine + '/' + cipherMode + '/' + cipherPadding);
993 }
994 }
995
996 return Collections.unmodifiableSortedSet(result);
997 }
998
999 /**
1000 * <p>
1001 * Create a {@link Cipher} instance according to the given transformation.
1002 * The transformation is a chain of algorithms containing 1 to 3 elements:
1003 * </p>
1004 * <ul>
1005 * <li>encryption algorithm (required)</li>
1006 * <li>mode (optional)</li>
1007 * <li>padding (optional)</li>
1008 * </ul>
1009 * <p>
1010 * For example:
1011 * </p>
1012 * <ul>
1013 * <li>"AES"</li>
1014 * <li>"AES/CBC/PKCS5Padding"</li>
1015 * <li>"Twofish/CFB/NoPadding"</li>
1016 * <li>"RSA"</li>
1017 * <li>"RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING"</li>
1018 * <li>"RSA//OAEPWITHSHA1ANDMGF1PADDING"</li>
1019 * </ul>
1020 * <p>
1021 * "ECB" and "NoPadding" are equivalent to an empty <code>String</code>.
1022 * </p>
1023 * <p>
1024 * See <a target="_blank" href="http://cumulus4j.org/1.1.0/documentation/supported-algorithms.html">Supported algorithms</a>
1025 * for a list of supported algorithms or use {@link #getSupportedCipherTransformations(CipherEngineType)}
1026 * to query them. Additionally, you can use {@link #getSupportedCipherEngines(CipherEngineType)},
1027 * {@link #getSupportedCipherModes(String)} and {@link #getSupportedCipherPaddings(String, String)}
1028 * to query the individual parts of the supported transformations.
1029 * </p>
1030 *
1031 * @param transformation the transformation. This is case-INsensitive. It must not be <code>null</code>.
1032 * @return a new <code>Cipher</code> instance.
1033 * @throws NoSuchAlgorithmException if there is no encryption engine or no mode registered to suit the given transformation.
1034 * @throws NoSuchPaddingException if there is no padding registered to suit the given transformation.
1035 * @see #getSupportedCipherTransformations(CipherEngineType)
1036 * @see #getSupportedCipherEngines(CipherEngineType)
1037 * @see #getSupportedCipherModes(String)
1038 * @see #getSupportedCipherPaddings(CipherEngineType)
1039 * @see #getSupportedCipherPaddings(String, String)
1040 */
1041 public Cipher createCipher(String transformation)
1042 throws NoSuchAlgorithmException, NoSuchPaddingException
1043 {
1044 String[] transformationParts = splitTransformation(transformation);
1045 String engineName = transformationParts[0].toUpperCase(Locale.ENGLISH);
1046 String modeName = transformationParts[1].toUpperCase(Locale.ENGLISH);
1047 String paddingName = transformationParts[2].toUpperCase(Locale.ENGLISH);
1048 transformationParts = null;
1049
1050 {
1051 BlockCipher engine = createBlockCipherEngine(engineName);
1052 if (engine != null)
1053 return createCipherForBlockCipherEngine(transformation, engine, engineName, modeName, paddingName);
1054 }
1055
1056 {
1057 AsymmetricBlockCipher engine = createAsymmetricBlockCipherEngine(engineName);
1058 if (engine != null)
1059 return createCipherForAsymmetricBlockCipherEngine(transformation, engine, engineName, modeName, paddingName);
1060 }
1061
1062 {
1063 StreamCipher engine = createStreamCipherEngine(engineName);
1064 if (engine != null)
1065 return createCipherForStreamCipherEngine(transformation, engine, engineName, modeName, paddingName);
1066 }
1067
1068 throw new NoSuchAlgorithmException("There is no cipher-engine-class registered with the algorithmName \"" + engineName + "\"!");
1069 }
1070
1071 /**
1072 * Split the transformation-<code>String</code> into its parts. The transformation is what you would
1073 * normally pass to {@link #createCipher(String)}, i.e. a chain of operations usually starting with
1074 * an encryption algorithm and then optionally followed by a block-cipher-mode (e.g. "CBC") and a
1075 * padding (e.g. "PKCS5Padding").
1076 * @param transformation the transformation-<code>String</code>.
1077 * @return a <code>String</code>-array with exactly 3 elements. None of these is ever <code>null</code>.
1078 * If parts are missing in the transformation, the corresponding elements are an empty string.
1079 * @throws IllegalArgumentException if the given transformation is <code>null</code> or contains
1080 * more than 3 parts (i.e. more than 2 slashes).
1081 */
1082 public static String[] splitTransformation(String transformation)
1083 throws IllegalArgumentException
1084 {
1085 if (transformation == null)
1086 throw new IllegalArgumentException("transformation == null");
1087
1088 String[] result = new String[3];
1089 Arrays.fill(result, "");
1090
1091 int lastSlashIdx = -1;
1092 int resultIdx = -1;
1093 while (true) {
1094 int slashIdx = transformation.indexOf('/', lastSlashIdx + 1);
1095 if (slashIdx < 0)
1096 slashIdx = transformation.length();
1097
1098 if (++resultIdx > result.length - 1)
1099 throw new IllegalArgumentException("transformation=\"" + transformation + "\" contains more than " + (result.length - 1) + " slashes!");
1100
1101 result[resultIdx] = transformation.substring(lastSlashIdx + 1, slashIdx).trim();
1102 lastSlashIdx = slashIdx;
1103
1104 if (slashIdx == transformation.length())
1105 break;
1106 }
1107
1108 return result;
1109 }
1110
1111 /**
1112 * Create a new {@link SecretKeyGenerator}.
1113 *
1114 * @param algorithmName the encryption algorithm for which the generated keys will be used.
1115 * This is the first element of a transformation, i.e.
1116 * you can pass a <code>transformation</code> to {@link #splitTransformation(String)} and use element 0 of its result.
1117 * See <a target="_blank" href="http://cumulus4j.org/1.1.0/documentation/supported-algorithms.html">Supported algorithms</a>
1118 * for a list of supported algorithms.
1119 * @param initWithDefaults whether to initialise the secret key generator with default values.
1120 * @return an instance of {@link SecretKeyGenerator}. If <code>initWithDefaults == true</code>, it can directly
1121 * be used to generate keys, i.e. it is already initialised with some default values. If <code>initWithDefaults == false</code>,
1122 * you still have to {@link SecretKeyGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters) initialise} the
1123 * key generator before you can use it.
1124 * @throws NoSuchAlgorithmException
1125 */
1126 public SecretKeyGenerator createSecretKeyGenerator(String algorithmName, boolean initWithDefaults)
1127 throws NoSuchAlgorithmException
1128 {
1129 if (algorithmName == null)
1130 throw new IllegalArgumentException("algorithmName == null");
1131
1132 algorithmName = algorithmName.toUpperCase(Locale.ENGLISH);
1133
1134 if (!algorithmName2blockCipherEngineClass.containsKey(algorithmName) && !algorithmName2streamCipherEngineClass.containsKey(algorithmName))
1135 throw new NoSuchAlgorithmException("There is no block/stream cipher registered for the algorithmName=\"" + algorithmName + "\"!");
1136
1137 SecretKeyGeneratorImpl secretKeyGeneratorImpl = new SecretKeyGeneratorImpl();
1138
1139 if (initWithDefaults)
1140 secretKeyGeneratorImpl.init(null);
1141
1142 return secretKeyGeneratorImpl;
1143 }
1144
1145 /**
1146 * Create a key pair generator for the given <b>asymmetric</b> encryption algorithm. If <code>initWithDefaults</code>
1147 * is specified with value <code>true</code>, the returned generator is ready to be used and doesn't require any
1148 * further initialisation.
1149 *
1150 * @param algorithmName the name of the <b>asymmetric</b> encryption algorithm. This is the first element of a transformation, i.e.
1151 * you can pass a <code>transformation</code> to {@link #splitTransformation(String)} and use element 0 of its result.
1152 * See <a target="_blank" href="http://cumulus4j.org/1.1.0/documentation/supported-algorithms.html">Supported algorithms</a>
1153 * for a list of supported algorithms.
1154 * @param initWithDefaults whether to initialise the key pair generator with default values.
1155 * @return an instance of {@link AsymmetricCipherKeyPairGenerator}. If <code>initWithDefaults == true</code>, it can directly
1156 * be used to generate key pairs, i.e. it is already initialised with some default values. If <code>initWithDefaults == false</code>,
1157 * you still have to {@link AsymmetricCipherKeyPairGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters) initialise} the
1158 * key pair generator before you can use it.
1159 * @throws NoSuchAlgorithmException if there is no generator available for the given <code>algorithmName</code>.
1160 */
1161 public AsymmetricCipherKeyPairGenerator createKeyPairGenerator(String algorithmName, boolean initWithDefaults)
1162 throws NoSuchAlgorithmException
1163 {
1164 if (algorithmName == null)
1165 throw new IllegalArgumentException("algorithmName == null");
1166
1167 AsymmetricCipherKeyPairGeneratorFactory factory = algorithmName2asymmetricCipherKeyPairGeneratorFactory.get(algorithmName);
1168 if (factory == null)
1169 throw new NoSuchAlgorithmException("There is no key-pair-generator-class registered for algorithmName \"" + algorithmName + "\"!");
1170
1171 AsymmetricCipherKeyPairGenerator generator = factory.createAsymmetricCipherKeyPairGenerator(initWithDefaults);
1172 return generator;
1173 }
1174
1175 /**
1176 * Decode (deserialise) a public key, that was previously encoded (serialised) by {@link #encodePublicKey(CipherParameters)}.
1177 * @param publicKeyData the serialised public key.
1178 * @return the public key (as previously passed to {@link #encodePublicKey(CipherParameters)}).
1179 * @throws IOException if parsing the serialised public key fails.
1180 * @see #encodePublicKey(CipherParameters)
1181 * @see #decodePrivateKey(byte[])
1182 */
1183 public CipherParameters decodePublicKey(byte[] publicKeyData) throws IOException
1184 {
1185 AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.createKey(publicKeyData);
1186 return asymmetricKeyParameter;
1187 }
1188
1189 /**
1190 * Encode (serialise) a public key in order to store it or transport it over a network.
1191 * @param publicKey the public key to be encoded; must not be <code>null</code>.
1192 * @return the encoded (serialised) form of the public key. Can be passed to {@link #decodePublicKey(byte[])} to
1193 * reverse this method.
1194 * @see #decodePublicKey(byte[])
1195 * @see #encodePrivateKey(CipherParameters)
1196 */
1197 public byte[] encodePublicKey(CipherParameters publicKey)
1198 {
1199 if (publicKey == null)
1200 throw new IllegalArgumentException("publicKey == null");
1201
1202 // TODO use a class-based map or similar registry!
1203 if (publicKey instanceof RSAKeyParameters) {
1204 RSAKeyParameters rsaPublicKey = (RSAKeyParameters) publicKey;
1205
1206 SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
1207 new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, new DERNull()),
1208 new RSAPublicKeyStructure(rsaPublicKey.getModulus(), rsaPublicKey.getExponent()).getDERObject()
1209 );
1210 return info.getDEREncoded();
1211 }
1212
1213 throw new UnsupportedOperationException("publicKey.class=\"" + publicKey.getClass().getName() + "\" not yet supported!");
1214 }
1215
1216 /**
1217 * Decode (deserialise) a private key, that was previously encoded (serialised) by {@link #encodePrivateKey(CipherParameters)}.
1218 * @param privateKeyData the serialised private key.
1219 * @return the private key (as previously passed to {@link #encodePrivateKey(CipherParameters)}).
1220 * @throws IOException if parsing the serialised private key fails.
1221 * @see #encodePrivateKey(CipherParameters)
1222 * @see #decodePublicKey(byte[])
1223 */
1224 public CipherParameters decodePrivateKey(byte[] privateKeyData) throws IOException
1225 {
1226 AsymmetricKeyParameter asymmetricKeyParameter = PrivateKeyFactory.createKey(privateKeyData);
1227 return asymmetricKeyParameter;
1228 }
1229
1230 /**
1231 * <p>
1232 * Encode (serialise) a private key in order to store it or transport it over a network.
1233 * </p><p>
1234 * <b>Important: You should keep your private key secret!</b> Thus, you might want to encrypt the result before
1235 * storing it to a file or sending it somewhere!
1236 * </p>
1237 * @param privateKey the private key to be encoded; must not be <code>null</code>.
1238 * @return the encoded (serialised) form of the private key. Can be passed to {@link #decodePrivateKey(byte[])} to
1239 * reverse this method.
1240 * @see #decodePrivateKey(byte[])
1241 * @see #encodePublicKey(CipherParameters)
1242 */
1243 public byte[] encodePrivateKey(CipherParameters privateKey)
1244 {
1245 if (privateKey == null)
1246 throw new IllegalArgumentException("privateKey == null");
1247
1248 // TODO use a class-based map or similar registry!
1249 if (privateKey instanceof RSAPrivateCrtKeyParameters) {
1250 RSAPrivateCrtKeyParameters rsaPrivateKey = (RSAPrivateCrtKeyParameters) privateKey;
1251
1252 PrivateKeyInfo info = new PrivateKeyInfo(
1253 new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, new DERNull()),
1254 new RSAPrivateKeyStructure(
1255 rsaPrivateKey.getModulus(), rsaPrivateKey.getPublicExponent(), rsaPrivateKey.getExponent(),
1256 rsaPrivateKey.getP(), rsaPrivateKey.getQ(), rsaPrivateKey.getDP(),
1257 rsaPrivateKey.getDQ(), rsaPrivateKey.getQInv()).getDERObject()
1258 );
1259 return info.getDEREncoded();
1260 }
1261
1262 throw new UnsupportedOperationException("privateKey.class=\"" + privateKey.getClass().getName() + "\" not yet supported!");
1263 }
1264
1265 private Map<String, MACCalculatorFactory> macName2macCalculatorFactory = new HashMap<String, MACCalculatorFactory>();
1266
1267 private void registerMACCalculatorFactory(String macName, MACCalculatorFactory factory)
1268 {
1269 if (macName != null)
1270 factory.setAlgorithmName(macName);
1271
1272 logger.trace("registerMACCalculatorFactory: algorithmName=\"{}\" factoryClass=\"{}\"", factory.getAlgorithmName(), factory.getClass().getName());
1273 macName2macCalculatorFactory.put(factory.getAlgorithmName(), factory);
1274 }
1275
1276 @SuppressWarnings("deprecation")
1277 private void registerDeprecatedMACCalculatorFactories()
1278 {
1279 registerMACCalculatorFactory("OLDHMACSHA384", new MACCalculatorFactoryImpl.OldSHA384());
1280 registerMACCalculatorFactory("OLDHMACSHA512", new MACCalculatorFactoryImpl.OldSHA512());
1281 }
1282
1283 {
1284 registerMACCalculatorFactory("DES", new MACCalculatorFactoryImpl.DES());
1285 registerMACCalculatorFactory("DESMAC", new MACCalculatorFactoryImpl.DES());
1286
1287 registerMACCalculatorFactory("DES64", new MACCalculatorFactoryImpl.DES64());
1288 registerMACCalculatorFactory("DES64MAC", new MACCalculatorFactoryImpl.DES64());
1289
1290 registerMACCalculatorFactory("DES/CFB8", new MACCalculatorFactoryImpl.DESCFB8());
1291 registerMACCalculatorFactory("DESMAC/CFB8", new MACCalculatorFactoryImpl.DESCFB8());
1292
1293 registerMACCalculatorFactory("DESWITHISO9797", new MACCalculatorFactoryImpl.DES9797Alg3());
1294 registerMACCalculatorFactory("DESWITHISO9797MAC", new MACCalculatorFactoryImpl.DES9797Alg3());
1295
1296 registerMACCalculatorFactory("ISO9797ALG3", new MACCalculatorFactoryImpl.DES9797Alg3());
1297 registerMACCalculatorFactory("ISO9797ALG3MAC", new MACCalculatorFactoryImpl.DES9797Alg3());
1298
1299 registerMACCalculatorFactory("ISO9797ALG3WITHISO7816-4PADDING", new MACCalculatorFactoryImpl.DES9797Alg3with7816d4());
1300 registerMACCalculatorFactory("ISO9797ALG3MACWITHISO7816-4PADDING", new MACCalculatorFactoryImpl.DES9797Alg3with7816d4());
1301
1302 registerMACCalculatorFactory("RC2", new MACCalculatorFactoryImpl.RC2());
1303 registerMACCalculatorFactory("RC2MAC", new MACCalculatorFactoryImpl.RC2());
1304
1305 registerMACCalculatorFactory("RC2/CFB8", new MACCalculatorFactoryImpl.RC2CFB8());
1306 registerMACCalculatorFactory("RC2MAC/CFB8", new MACCalculatorFactoryImpl.RC2CFB8());
1307
1308 registerMACCalculatorFactory("GOST28147", new MACCalculatorFactoryImpl.GOST28147());
1309 registerMACCalculatorFactory("GOST28147MAC", new MACCalculatorFactoryImpl.GOST28147());
1310
1311 registerDeprecatedMACCalculatorFactories();
1312
1313 registerMACCalculatorFactory("HMACMD2", new MACCalculatorFactoryImpl.MD2());
1314 registerMACCalculatorFactory("HMAC-MD2", new MACCalculatorFactoryImpl.MD2());
1315 registerMACCalculatorFactory("HMAC/MD2", new MACCalculatorFactoryImpl.MD2());
1316
1317 registerMACCalculatorFactory("HMACMD4", new MACCalculatorFactoryImpl.MD4());
1318 registerMACCalculatorFactory("HMAC-MD4", new MACCalculatorFactoryImpl.MD4());
1319 registerMACCalculatorFactory("HMAC/MD4", new MACCalculatorFactoryImpl.MD4());
1320
1321 registerMACCalculatorFactory("HMACMD5", new MACCalculatorFactoryImpl.MD5());
1322 registerMACCalculatorFactory("HMAC-MD5", new MACCalculatorFactoryImpl.MD5());
1323 registerMACCalculatorFactory("HMAC/MD5", new MACCalculatorFactoryImpl.MD5());
1324
1325 registerMACCalculatorFactory("HMACSHA1", new MACCalculatorFactoryImpl.SHA1());
1326 registerMACCalculatorFactory("HMAC-SHA1", new MACCalculatorFactoryImpl.SHA1());
1327 registerMACCalculatorFactory("HMAC/SHA1", new MACCalculatorFactoryImpl.SHA1());
1328
1329 registerMACCalculatorFactory("HMACSHA224", new MACCalculatorFactoryImpl.SHA224());
1330 registerMACCalculatorFactory("HMAC-SHA224", new MACCalculatorFactoryImpl.SHA224());
1331 registerMACCalculatorFactory("HMAC/SHA224", new MACCalculatorFactoryImpl.SHA224());
1332
1333 registerMACCalculatorFactory("HMACSHA256", new MACCalculatorFactoryImpl.SHA256());
1334 registerMACCalculatorFactory("HMAC-SHA256", new MACCalculatorFactoryImpl.SHA256());
1335 registerMACCalculatorFactory("HMAC/SHA256", new MACCalculatorFactoryImpl.SHA256());
1336
1337 registerMACCalculatorFactory("HMACSHA384", new MACCalculatorFactoryImpl.SHA384());
1338 registerMACCalculatorFactory("HMAC-SHA384", new MACCalculatorFactoryImpl.SHA384());
1339 registerMACCalculatorFactory("HMAC/SHA384", new MACCalculatorFactoryImpl.SHA384());
1340
1341 registerMACCalculatorFactory("HMACSHA512", new MACCalculatorFactoryImpl.SHA512());
1342 registerMACCalculatorFactory("HMAC-SHA512", new MACCalculatorFactoryImpl.SHA512());
1343 registerMACCalculatorFactory("HMAC/SHA512", new MACCalculatorFactoryImpl.SHA512());
1344
1345 registerMACCalculatorFactory("HMACRIPEMD128", new MACCalculatorFactoryImpl.RIPEMD128());
1346 registerMACCalculatorFactory("HMAC-RIPEMD128", new MACCalculatorFactoryImpl.RIPEMD128());
1347 registerMACCalculatorFactory("HMAC/RIPEMD128", new MACCalculatorFactoryImpl.RIPEMD128());
1348
1349 registerMACCalculatorFactory("HMACRIPEMD160", new MACCalculatorFactoryImpl.RIPEMD160());
1350 registerMACCalculatorFactory("HMAC-RIPEMD160", new MACCalculatorFactoryImpl.RIPEMD160());
1351 registerMACCalculatorFactory("HMAC/RIPEMD160", new MACCalculatorFactoryImpl.RIPEMD160());
1352
1353 registerMACCalculatorFactory("HMACTIGER", new MACCalculatorFactoryImpl.Tiger());
1354 registerMACCalculatorFactory("HMAC-TIGER", new MACCalculatorFactoryImpl.Tiger());
1355 registerMACCalculatorFactory("HMAC/TIGER", new MACCalculatorFactoryImpl.Tiger());
1356 }
1357
1358 /**
1359 * <p>
1360 * Create a <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> calculator.
1361 * </p>
1362 *
1363 * @param algorithmName the name of the MAC algorithm. See <a target="_blank" href="http://cumulus4j.org/${project.version}/documentation/supported-algorithms.html">Supported algorithms</a>
1364 * for a list of supported algorithms or use {@link #getSupportedMACAlgorithms()} to query them.
1365 * @param initWithDefaults whether to
1366 * {@link MACCalculator#init(org.bouncycastle.crypto.CipherParameters) initialise} the <code>MACCalculator</code> with default values
1367 * so that it can be used immediately as-is.
1368 * @return a new instance of {@link MACCalculator} (iff <code>initWithDefaults==true</code> ready-to-use;
1369 * otherwise requiring {@link MACCalculator#init(org.bouncycastle.crypto.CipherParameters) initialisation}
1370 * before it can be used).
1371 * @throws NoSuchAlgorithmException if there is no {@link MACCalculatorFactory} registered to suit the given <code>algorithmName</code>.
1372 * @see #getSupportedMACAlgorithms()
1373 */
1374 public MACCalculator createMACCalculator(String algorithmName, boolean initWithDefaults)
1375 throws NoSuchAlgorithmException
1376 {
1377 if (algorithmName == null)
1378 throw new IllegalArgumentException("algorithmName == null");
1379
1380 MACCalculatorFactory factory = macName2macCalculatorFactory.get(algorithmName.toUpperCase(Locale.ENGLISH));
1381 if (factory == null)
1382 throw new NoSuchAlgorithmException("There is no MAC calculator registered for algorithmName=\"" + algorithmName.toUpperCase(Locale.ENGLISH) + "\"!");
1383
1384 return factory.createMACCalculator(initWithDefaults);
1385 }
1386
1387 /**
1388 * Get all supported <a target="_blank" href="http://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> algorithms.
1389 * {@link #createMACCalculator(String, boolean)} should be able to return a {@link MACCalculator} for each of them.
1390 * @return all supported MAC algorithms.
1391 * @see #createMACCalculator(String, boolean)
1392 */
1393 public Set<String> getSupportedMACAlgorithms()
1394 {
1395 return Collections.unmodifiableSet(new TreeSet<String>(macName2macCalculatorFactory.keySet()));
1396 }
1397 }