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.2.0-SNAPSHOT/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.2.0-SNAPSHOT/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.2.0-SNAPSHOT/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.2.0-SNAPSHOT/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.2.0-SNAPSHOT/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    }