CipherSpi.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.jcajce.provider.asymmetric.rsa; 2 3import java.io.ByteArrayOutputStream; 4import java.security.AlgorithmParameters; 5import java.security.InvalidAlgorithmParameterException; 6import java.security.InvalidKeyException; 7import java.security.InvalidParameterException; 8import java.security.Key; 9import java.security.NoSuchAlgorithmException; 10import java.security.SecureRandom; 11import java.security.interfaces.RSAPrivateKey; 12import java.security.interfaces.RSAPublicKey; 13import java.security.spec.AlgorithmParameterSpec; 14import java.security.spec.InvalidParameterSpecException; 15import java.security.spec.MGF1ParameterSpec; 16 17import javax.crypto.BadPaddingException; 18import javax.crypto.Cipher; 19import javax.crypto.IllegalBlockSizeException; 20import javax.crypto.NoSuchPaddingException; 21import javax.crypto.spec.OAEPParameterSpec; 22import javax.crypto.spec.PSource; 23 24import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 25import org.bouncycastle.crypto.AsymmetricBlockCipher; 26import org.bouncycastle.crypto.CipherParameters; 27import org.bouncycastle.crypto.Digest; 28import org.bouncycastle.crypto.InvalidCipherTextException; 29// BEGIN android-removed 30// import org.bouncycastle.crypto.encodings.ISO9796d1Encoding; 31// END android-removed 32import org.bouncycastle.crypto.encodings.OAEPEncoding; 33import org.bouncycastle.crypto.encodings.PKCS1Encoding; 34import org.bouncycastle.crypto.engines.RSABlindedEngine; 35import org.bouncycastle.crypto.params.ParametersWithRandom; 36import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi; 37import org.bouncycastle.jcajce.provider.util.DigestFactory; 38import org.bouncycastle.jce.provider.BouncyCastleProvider; 39import org.bouncycastle.util.Strings; 40 41public class CipherSpi 42 extends BaseCipherSpi 43{ 44 private AsymmetricBlockCipher cipher; 45 private AlgorithmParameterSpec paramSpec; 46 private AlgorithmParameters engineParams; 47 private boolean publicKeyOnly = false; 48 private boolean privateKeyOnly = false; 49 private ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 50 51 public CipherSpi( 52 AsymmetricBlockCipher engine) 53 { 54 cipher = engine; 55 } 56 57 public CipherSpi( 58 OAEPParameterSpec pSpec) 59 { 60 try 61 { 62 initFromSpec(pSpec); 63 } 64 catch (NoSuchPaddingException e) 65 { 66 throw new IllegalArgumentException(e.getMessage()); 67 } 68 } 69 70 public CipherSpi( 71 boolean publicKeyOnly, 72 boolean privateKeyOnly, 73 AsymmetricBlockCipher engine) 74 { 75 this.publicKeyOnly = publicKeyOnly; 76 this.privateKeyOnly = privateKeyOnly; 77 cipher = engine; 78 } 79 80 private void initFromSpec( 81 OAEPParameterSpec pSpec) 82 throws NoSuchPaddingException 83 { 84 MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)pSpec.getMGFParameters(); 85 Digest digest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm()); 86 87 if (digest == null) 88 { 89 throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm()); 90 } 91 92 cipher = new OAEPEncoding(new RSABlindedEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue()); 93 paramSpec = pSpec; 94 } 95 96 protected int engineGetBlockSize() 97 { 98 try 99 { 100 return cipher.getInputBlockSize(); 101 } 102 catch (NullPointerException e) 103 { 104 throw new IllegalStateException("RSA Cipher not initialised"); 105 } 106 } 107 108 protected int engineGetKeySize( 109 Key key) 110 { 111 if (key instanceof RSAPrivateKey) 112 { 113 RSAPrivateKey k = (RSAPrivateKey)key; 114 115 return k.getModulus().bitLength(); 116 } 117 else if (key instanceof RSAPublicKey) 118 { 119 RSAPublicKey k = (RSAPublicKey)key; 120 121 return k.getModulus().bitLength(); 122 } 123 124 throw new IllegalArgumentException("not an RSA key!"); 125 } 126 127 protected int engineGetOutputSize( 128 int inputLen) 129 { 130 try 131 { 132 return cipher.getOutputBlockSize(); 133 } 134 catch (NullPointerException e) 135 { 136 throw new IllegalStateException("RSA Cipher not initialised"); 137 } 138 } 139 140 protected AlgorithmParameters engineGetParameters() 141 { 142 if (engineParams == null) 143 { 144 if (paramSpec != null) 145 { 146 try 147 { 148 engineParams = AlgorithmParameters.getInstance("OAEP", BouncyCastleProvider.PROVIDER_NAME); 149 engineParams.init(paramSpec); 150 } 151 catch (Exception e) 152 { 153 throw new RuntimeException(e.toString()); 154 } 155 } 156 } 157 158 return engineParams; 159 } 160 161 protected void engineSetMode( 162 String mode) 163 throws NoSuchAlgorithmException 164 { 165 String md = Strings.toUpperCase(mode); 166 167 if (md.equals("NONE") || md.equals("ECB")) 168 { 169 return; 170 } 171 172 if (md.equals("1")) 173 { 174 privateKeyOnly = true; 175 publicKeyOnly = false; 176 return; 177 } 178 else if (md.equals("2")) 179 { 180 privateKeyOnly = false; 181 publicKeyOnly = true; 182 return; 183 } 184 185 throw new NoSuchAlgorithmException("can't support mode " + mode); 186 } 187 188 protected void engineSetPadding( 189 String padding) 190 throws NoSuchPaddingException 191 { 192 String pad = Strings.toUpperCase(padding); 193 194 if (pad.equals("NOPADDING")) 195 { 196 cipher = new RSABlindedEngine(); 197 } 198 else if (pad.equals("PKCS1PADDING")) 199 { 200 cipher = new PKCS1Encoding(new RSABlindedEngine()); 201 } 202 // BEGIN android-removed 203 // else if (pad.equals("ISO9796-1PADDING")) 204 // { 205 // cipher = new ISO9796d1Encoding(new RSABlindedEngine()); 206 // } 207 // END android-removed 208 else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING")) 209 { 210 initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT)); 211 } 212 else if (pad.equals("OAEPPADDING")) 213 { 214 initFromSpec(OAEPParameterSpec.DEFAULT); 215 } 216 else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-1ANDMGF1PADDING")) 217 { 218 initFromSpec(OAEPParameterSpec.DEFAULT); 219 } 220 // BEGIN android-removed 221 // else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-224ANDMGF1PADDING")) 222 // { 223 // initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT)); 224 // } 225 // END android-removed 226 else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-256ANDMGF1PADDING")) 227 { 228 initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)); 229 } 230 else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-384ANDMGF1PADDING")) 231 { 232 initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT)); 233 } 234 else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-512ANDMGF1PADDING")) 235 { 236 initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT)); 237 } 238 else 239 { 240 throw new NoSuchPaddingException(padding + " unavailable with RSA."); 241 } 242 } 243 244 protected void engineInit( 245 int opmode, 246 Key key, 247 AlgorithmParameterSpec params, 248 SecureRandom random) 249 throws InvalidKeyException, InvalidAlgorithmParameterException 250 { 251 CipherParameters param; 252 253 if (params == null || params instanceof OAEPParameterSpec) 254 { 255 if (key instanceof RSAPublicKey) 256 { 257 if (privateKeyOnly && opmode == Cipher.ENCRYPT_MODE) 258 { 259 throw new InvalidKeyException( 260 "mode 1 requires RSAPrivateKey"); 261 } 262 263 param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)key); 264 } 265 else if (key instanceof RSAPrivateKey) 266 { 267 if (publicKeyOnly && opmode == Cipher.ENCRYPT_MODE) 268 { 269 throw new InvalidKeyException( 270 "mode 2 requires RSAPublicKey"); 271 } 272 273 param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)key); 274 } 275 else 276 { 277 throw new InvalidKeyException("unknown key type passed to RSA"); 278 } 279 280 if (params != null) 281 { 282 OAEPParameterSpec spec = (OAEPParameterSpec)params; 283 284 paramSpec = params; 285 286 if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !spec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId())) 287 { 288 throw new InvalidAlgorithmParameterException("unknown mask generation function specified"); 289 } 290 291 if (!(spec.getMGFParameters() instanceof MGF1ParameterSpec)) 292 { 293 throw new InvalidAlgorithmParameterException("unkown MGF parameters"); 294 } 295 296 Digest digest = DigestFactory.getDigest(spec.getDigestAlgorithm()); 297 298 if (digest == null) 299 { 300 throw new InvalidAlgorithmParameterException("no match on digest algorithm: "+ spec.getDigestAlgorithm()); 301 } 302 303 MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)spec.getMGFParameters(); 304 Digest mgfDigest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm()); 305 306 if (mgfDigest == null) 307 { 308 throw new InvalidAlgorithmParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm()); 309 } 310 311 cipher = new OAEPEncoding(new RSABlindedEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue()); 312 } 313 } 314 else 315 { 316 throw new IllegalArgumentException("unknown parameter type."); 317 } 318 319 if (!(cipher instanceof RSABlindedEngine)) 320 { 321 if (random != null) 322 { 323 param = new ParametersWithRandom(param, random); 324 } 325 else 326 { 327 param = new ParametersWithRandom(param, new SecureRandom()); 328 } 329 } 330 331 bOut.reset(); 332 333 switch (opmode) 334 { 335 case Cipher.ENCRYPT_MODE: 336 case Cipher.WRAP_MODE: 337 cipher.init(true, param); 338 break; 339 case Cipher.DECRYPT_MODE: 340 case Cipher.UNWRAP_MODE: 341 cipher.init(false, param); 342 break; 343 default: 344 throw new InvalidParameterException("unknown opmode " + opmode + " passed to RSA"); 345 } 346 } 347 348 protected void engineInit( 349 int opmode, 350 Key key, 351 AlgorithmParameters params, 352 SecureRandom random) 353 throws InvalidKeyException, InvalidAlgorithmParameterException 354 { 355 AlgorithmParameterSpec paramSpec = null; 356 357 if (params != null) 358 { 359 try 360 { 361 paramSpec = params.getParameterSpec(OAEPParameterSpec.class); 362 } 363 catch (InvalidParameterSpecException e) 364 { 365 throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + e.toString(), e); 366 } 367 } 368 369 engineParams = params; 370 engineInit(opmode, key, paramSpec, random); 371 } 372 373 protected void engineInit( 374 int opmode, 375 Key key, 376 SecureRandom random) 377 throws InvalidKeyException 378 { 379 try 380 { 381 engineInit(opmode, key, (AlgorithmParameterSpec)null, random); 382 } 383 catch (InvalidAlgorithmParameterException e) 384 { 385 // this shouldn't happen 386 throw new InvalidKeyException("Eeeek! " + e.toString(), e); 387 } 388 } 389 390 protected byte[] engineUpdate( 391 byte[] input, 392 int inputOffset, 393 int inputLen) 394 { 395 bOut.write(input, inputOffset, inputLen); 396 397 if (cipher instanceof RSABlindedEngine) 398 { 399 if (bOut.size() > cipher.getInputBlockSize() + 1) 400 { 401 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 402 } 403 } 404 else 405 { 406 if (bOut.size() > cipher.getInputBlockSize()) 407 { 408 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 409 } 410 } 411 412 return null; 413 } 414 415 protected int engineUpdate( 416 byte[] input, 417 int inputOffset, 418 int inputLen, 419 byte[] output, 420 int outputOffset) 421 { 422 bOut.write(input, inputOffset, inputLen); 423 424 if (cipher instanceof RSABlindedEngine) 425 { 426 if (bOut.size() > cipher.getInputBlockSize() + 1) 427 { 428 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 429 } 430 } 431 else 432 { 433 if (bOut.size() > cipher.getInputBlockSize()) 434 { 435 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 436 } 437 } 438 439 return 0; 440 } 441 442 protected byte[] engineDoFinal( 443 byte[] input, 444 int inputOffset, 445 int inputLen) 446 throws IllegalBlockSizeException, BadPaddingException 447 { 448 if (input != null) 449 { 450 bOut.write(input, inputOffset, inputLen); 451 } 452 453 if (cipher instanceof RSABlindedEngine) 454 { 455 if (bOut.size() > cipher.getInputBlockSize() + 1) 456 { 457 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 458 } 459 } 460 else 461 { 462 if (bOut.size() > cipher.getInputBlockSize()) 463 { 464 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 465 } 466 } 467 468 try 469 { 470 byte[] bytes = bOut.toByteArray(); 471 472 bOut.reset(); 473 474 return cipher.processBlock(bytes, 0, bytes.length); 475 } 476 catch (InvalidCipherTextException e) 477 { 478 throw new BadPaddingException(e.getMessage()); 479 } 480 } 481 482 protected int engineDoFinal( 483 byte[] input, 484 int inputOffset, 485 int inputLen, 486 byte[] output, 487 int outputOffset) 488 throws IllegalBlockSizeException, BadPaddingException 489 { 490 if (input != null) 491 { 492 bOut.write(input, inputOffset, inputLen); 493 } 494 495 if (cipher instanceof RSABlindedEngine) 496 { 497 if (bOut.size() > cipher.getInputBlockSize() + 1) 498 { 499 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 500 } 501 } 502 else 503 { 504 if (bOut.size() > cipher.getInputBlockSize()) 505 { 506 throw new ArrayIndexOutOfBoundsException("too much data for RSA block"); 507 } 508 } 509 510 byte[] out; 511 512 try 513 { 514 byte[] bytes = bOut.toByteArray(); 515 516 out = cipher.processBlock(bytes, 0, bytes.length); 517 } 518 catch (InvalidCipherTextException e) 519 { 520 throw new BadPaddingException(e.getMessage()); 521 } 522 finally 523 { 524 bOut.reset(); 525 } 526 527 for (int i = 0; i != out.length; i++) 528 { 529 output[outputOffset + i] = out[i]; 530 } 531 532 return out.length; 533 } 534 535 /** 536 * classes that inherit from us. 537 */ 538 539 static public class NoPadding 540 extends CipherSpi 541 { 542 public NoPadding() 543 { 544 super(new RSABlindedEngine()); 545 } 546 } 547 548 // BEGIN android-removed 549 // static public class PKCS1v1_5Padding 550 // extends CipherSpi 551 // { 552 // public PKCS1v1_5Padding() 553 // { 554 // super(new PKCS1Encoding(new RSABlindedEngine())); 555 // } 556 // } 557 // 558 // static public class PKCS1v1_5Padding_PrivateOnly 559 // extends CipherSpi 560 // { 561 // public PKCS1v1_5Padding_PrivateOnly() 562 // { 563 // super(false, true, new PKCS1Encoding(new RSABlindedEngine())); 564 // } 565 // } 566 // 567 // static public class PKCS1v1_5Padding_PublicOnly 568 // extends CipherSpi 569 // { 570 // public PKCS1v1_5Padding_PublicOnly() 571 // { 572 // super(true, false, new PKCS1Encoding(new RSABlindedEngine())); 573 // } 574 // } 575 // 576 // static public class OAEPPadding 577 // extends CipherSpi 578 // { 579 // public OAEPPadding() 580 // { 581 // super(OAEPParameterSpec.DEFAULT); 582 // } 583 // } 584 // 585 // static public class ISO9796d1Padding 586 // extends CipherSpi 587 // { 588 // public ISO9796d1Padding() 589 // { 590 // super(new ISO9796d1Encoding(new RSABlindedEngine())); 591 // } 592 // } 593 // END android-removed 594} 595