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 }