001: package org.bouncycastle.jce.provider;
002:
003: import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
004: import org.bouncycastle.crypto.AsymmetricBlockCipher;
005: import org.bouncycastle.crypto.CipherParameters;
006: import org.bouncycastle.crypto.Digest;
007: import org.bouncycastle.crypto.InvalidCipherTextException;
008: import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
009: import org.bouncycastle.crypto.encodings.OAEPEncoding;
010: import org.bouncycastle.crypto.encodings.PKCS1Encoding;
011: import org.bouncycastle.crypto.engines.RSABlindedEngine;
012: import org.bouncycastle.crypto.params.ParametersWithRandom;
013: import org.bouncycastle.util.Strings;
014:
015: import javax.crypto.BadPaddingException;
016: import javax.crypto.Cipher;
017: import javax.crypto.IllegalBlockSizeException;
018: import javax.crypto.NoSuchPaddingException;
019: import javax.crypto.spec.OAEPParameterSpec;
020: import javax.crypto.spec.PSource;
021: import java.io.ByteArrayOutputStream;
022: import java.security.AlgorithmParameters;
023: import java.security.InvalidAlgorithmParameterException;
024: import java.security.InvalidKeyException;
025: import java.security.InvalidParameterException;
026: import java.security.Key;
027: import java.security.NoSuchAlgorithmException;
028: import java.security.SecureRandom;
029: import java.security.interfaces.RSAPrivateKey;
030: import java.security.interfaces.RSAPublicKey;
031: import java.security.spec.AlgorithmParameterSpec;
032: import java.security.spec.InvalidParameterSpecException;
033: import java.security.spec.MGF1ParameterSpec;
034:
035: public class JCERSACipher extends WrapCipherSpi {
036: private AsymmetricBlockCipher cipher;
037: private AlgorithmParameterSpec paramSpec;
038: private AlgorithmParameters engineParams;
039: private boolean publicKeyOnly = false;
040: private boolean privateKeyOnly = false;
041: private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
042:
043: public JCERSACipher(AsymmetricBlockCipher engine) {
044: cipher = engine;
045: }
046:
047: public JCERSACipher(OAEPParameterSpec pSpec) {
048: try {
049: initFromSpec(pSpec);
050: } catch (NoSuchPaddingException e) {
051: throw new IllegalArgumentException(e.getMessage());
052: }
053: }
054:
055: public JCERSACipher(boolean publicKeyOnly, boolean privateKeyOnly,
056: AsymmetricBlockCipher engine) {
057: this .publicKeyOnly = publicKeyOnly;
058: this .privateKeyOnly = privateKeyOnly;
059: cipher = engine;
060: }
061:
062: private void initFromSpec(OAEPParameterSpec pSpec)
063: throws NoSuchPaddingException {
064: MGF1ParameterSpec mgfParams = (MGF1ParameterSpec) pSpec
065: .getMGFParameters();
066: Digest digest = JCEDigestUtil.getDigest(mgfParams
067: .getDigestAlgorithm());
068:
069: if (digest == null) {
070: throw new NoSuchPaddingException(
071: "no match on OAEP constructor for digest algorithm: "
072: + mgfParams.getDigestAlgorithm());
073: }
074:
075: cipher = new OAEPEncoding(new RSABlindedEngine(), digest,
076: ((PSource.PSpecified) pSpec.getPSource()).getValue());
077: paramSpec = pSpec;
078: }
079:
080: protected int engineGetBlockSize() {
081: try {
082: return cipher.getInputBlockSize();
083: } catch (NullPointerException e) {
084: throw new IllegalStateException(
085: "RSA Cipher not initialised");
086: }
087: }
088:
089: protected byte[] engineGetIV() {
090: return null;
091: }
092:
093: protected int engineGetKeySize(Key key) {
094: if (key instanceof RSAPrivateKey) {
095: RSAPrivateKey k = (RSAPrivateKey) key;
096:
097: return k.getModulus().bitLength();
098: } else if (key instanceof RSAPublicKey) {
099: RSAPublicKey k = (RSAPublicKey) key;
100:
101: return k.getModulus().bitLength();
102: }
103:
104: throw new IllegalArgumentException("not an RSA key!");
105: }
106:
107: protected int engineGetOutputSize(int inputLen) {
108: try {
109: return cipher.getOutputBlockSize();
110: } catch (NullPointerException e) {
111: throw new IllegalStateException(
112: "RSA Cipher not initialised");
113: }
114: }
115:
116: protected AlgorithmParameters engineGetParameters() {
117: if (engineParams == null) {
118: if (paramSpec != null) {
119: try {
120: engineParams = AlgorithmParameters.getInstance(
121: "OAEP", "BC");
122: engineParams.init(paramSpec);
123: } catch (Exception e) {
124: throw new RuntimeException(e.toString());
125: }
126: }
127: }
128:
129: return engineParams;
130: }
131:
132: protected void engineSetMode(String mode)
133: throws NoSuchAlgorithmException {
134: String md = Strings.toUpperCase(mode);
135:
136: if (md.equals("NONE") || md.equals("ECB")) {
137: return;
138: }
139:
140: if (md.equals("1")) {
141: privateKeyOnly = true;
142: publicKeyOnly = false;
143: return;
144: } else if (md.equals("2")) {
145: privateKeyOnly = false;
146: publicKeyOnly = true;
147: return;
148: }
149:
150: throw new NoSuchAlgorithmException("can't support mode " + mode);
151: }
152:
153: protected void engineSetPadding(String padding)
154: throws NoSuchPaddingException {
155: String pad = Strings.toUpperCase(padding);
156:
157: if (pad.equals("NOPADDING")) {
158: cipher = new RSABlindedEngine();
159: } else if (pad.equals("PKCS1PADDING")) {
160: cipher = new PKCS1Encoding(new RSABlindedEngine());
161: } else if (pad.equals("ISO9796-1PADDING")) {
162: cipher = new ISO9796d1Encoding(new RSABlindedEngine());
163: } else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING")) {
164: initFromSpec(new OAEPParameterSpec("MD5", "MGF1",
165: new MGF1ParameterSpec("MD5"),
166: PSource.PSpecified.DEFAULT));
167: } else if (pad.equals("OAEPPADDING")) {
168: initFromSpec(OAEPParameterSpec.DEFAULT);
169: } else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING")) {
170: initFromSpec(OAEPParameterSpec.DEFAULT);
171: } else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING")) {
172: initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1",
173: new MGF1ParameterSpec("SHA-224"),
174: PSource.PSpecified.DEFAULT));
175: } else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING")) {
176: initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1",
177: MGF1ParameterSpec.SHA256,
178: PSource.PSpecified.DEFAULT));
179: } else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING")) {
180: initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1",
181: MGF1ParameterSpec.SHA384,
182: PSource.PSpecified.DEFAULT));
183: } else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING")) {
184: initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1",
185: MGF1ParameterSpec.SHA512,
186: PSource.PSpecified.DEFAULT));
187: } else {
188: throw new NoSuchPaddingException(padding
189: + " unavailable with RSA.");
190: }
191: }
192:
193: protected void engineInit(int opmode, Key key,
194: AlgorithmParameterSpec params, SecureRandom random)
195: throws InvalidKeyException,
196: InvalidAlgorithmParameterException {
197: CipherParameters param;
198:
199: if (params == null || params instanceof OAEPParameterSpec) {
200: if (key instanceof RSAPublicKey) {
201: if (privateKeyOnly) {
202: throw new InvalidKeyException(
203: "mode 1 requires RSAPrivateKey");
204: }
205:
206: param = RSAUtil
207: .generatePublicKeyParameter((RSAPublicKey) key);
208: } else if (key instanceof RSAPrivateKey) {
209: if (publicKeyOnly) {
210: throw new InvalidKeyException(
211: "mode 2 requires RSAPublicKey");
212: }
213:
214: param = RSAUtil
215: .generatePrivateKeyParameter((RSAPrivateKey) key);
216: } else {
217: throw new InvalidKeyException(
218: "unknown key type passed to RSA");
219: }
220:
221: if (params != null) {
222: OAEPParameterSpec spec = (OAEPParameterSpec) params;
223:
224: paramSpec = params;
225:
226: if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1")
227: && !spec.getMGFAlgorithm().equals(
228: PKCSObjectIdentifiers.id_mgf1.getId())) {
229: throw new InvalidAlgorithmParameterException(
230: "unknown mask generation function specified");
231: }
232:
233: if (!(spec.getMGFParameters() instanceof MGF1ParameterSpec)) {
234: throw new InvalidAlgorithmParameterException(
235: "unkown MGF parameters");
236: }
237:
238: MGF1ParameterSpec mgfParams = (MGF1ParameterSpec) spec
239: .getMGFParameters();
240:
241: if (!JCEDigestUtil.isSameDigest(mgfParams
242: .getDigestAlgorithm(), spec
243: .getDigestAlgorithm())) {
244: throw new InvalidAlgorithmParameterException(
245: "digest algorithm for MGF should be the same as for OAEP parameters.");
246: }
247:
248: Digest digest = JCEDigestUtil.getDigest(mgfParams
249: .getDigestAlgorithm());
250:
251: if (digest == null) {
252: throw new InvalidAlgorithmParameterException(
253: "no match on MGF digest algorithm: "
254: + mgfParams.getDigestAlgorithm());
255: }
256:
257: cipher = new OAEPEncoding(new RSABlindedEngine(),
258: digest,
259: ((PSource.PSpecified) spec.getPSource())
260: .getValue());
261: }
262: } else {
263: throw new IllegalArgumentException(
264: "unknown parameter type.");
265: }
266:
267: if (!(cipher instanceof RSABlindedEngine)) {
268: if (random != null) {
269: param = new ParametersWithRandom(param, random);
270: } else {
271: param = new ParametersWithRandom(param,
272: new SecureRandom());
273: }
274: }
275:
276: switch (opmode) {
277: case Cipher.ENCRYPT_MODE:
278: case Cipher.WRAP_MODE:
279: cipher.init(true, param);
280: break;
281: case Cipher.DECRYPT_MODE:
282: case Cipher.UNWRAP_MODE:
283: cipher.init(false, param);
284: break;
285: default:
286: throw new InvalidParameterException("unknown opmode "
287: + opmode + " passed to RSA");
288: }
289: }
290:
291: protected void engineInit(int opmode, Key key,
292: AlgorithmParameters params, SecureRandom random)
293: throws InvalidKeyException,
294: InvalidAlgorithmParameterException {
295: AlgorithmParameterSpec paramSpec = null;
296:
297: if (params != null) {
298: try {
299: paramSpec = params
300: .getParameterSpec(OAEPParameterSpec.class);
301: } catch (InvalidParameterSpecException e) {
302: throw new InvalidAlgorithmParameterException(
303: "cannot recognise parameters: " + e.toString(),
304: e);
305: }
306: }
307:
308: engineParams = params;
309: engineInit(opmode, key, paramSpec, random);
310: }
311:
312: protected void engineInit(int opmode, Key key, SecureRandom random)
313: throws InvalidKeyException {
314: try {
315: engineInit(opmode, key, (AlgorithmParameterSpec) null,
316: random);
317: } catch (InvalidAlgorithmParameterException e) {
318: // this shouldn't happen
319: throw new RuntimeException("Eeeek! " + e.toString(), e);
320: }
321: }
322:
323: protected byte[] engineUpdate(byte[] input, int inputOffset,
324: int inputLen) {
325: bOut.write(input, inputOffset, inputLen);
326:
327: if (cipher instanceof RSABlindedEngine) {
328: if (bOut.size() > cipher.getInputBlockSize() + 1) {
329: throw new ArrayIndexOutOfBoundsException(
330: "too much data for RSA block");
331: }
332: } else {
333: if (bOut.size() > cipher.getInputBlockSize()) {
334: throw new ArrayIndexOutOfBoundsException(
335: "too much data for RSA block");
336: }
337: }
338:
339: return null;
340: }
341:
342: protected int engineUpdate(byte[] input, int inputOffset,
343: int inputLen, byte[] output, int outputOffset) {
344: bOut.write(input, inputOffset, inputLen);
345:
346: if (cipher instanceof RSABlindedEngine) {
347: if (bOut.size() > cipher.getInputBlockSize() + 1) {
348: throw new ArrayIndexOutOfBoundsException(
349: "too much data for RSA block");
350: }
351: } else {
352: if (bOut.size() > cipher.getInputBlockSize()) {
353: throw new ArrayIndexOutOfBoundsException(
354: "too much data for RSA block");
355: }
356: }
357:
358: return 0;
359: }
360:
361: protected byte[] engineDoFinal(byte[] input, int inputOffset,
362: int inputLen) throws IllegalBlockSizeException,
363: BadPaddingException {
364: if (input != null) {
365: bOut.write(input, inputOffset, inputLen);
366: }
367:
368: if (cipher instanceof RSABlindedEngine) {
369: if (bOut.size() > cipher.getInputBlockSize() + 1) {
370: throw new ArrayIndexOutOfBoundsException(
371: "too much data for RSA block");
372: }
373: } else {
374: if (bOut.size() > cipher.getInputBlockSize()) {
375: throw new ArrayIndexOutOfBoundsException(
376: "too much data for RSA block");
377: }
378: }
379:
380: try {
381: byte[] bytes = bOut.toByteArray();
382:
383: bOut.reset();
384:
385: return cipher.processBlock(bytes, 0, bytes.length);
386: } catch (InvalidCipherTextException e) {
387: throw new BadPaddingException(e.getMessage());
388: }
389: }
390:
391: protected int engineDoFinal(byte[] input, int inputOffset,
392: int inputLen, byte[] output, int outputOffset)
393: throws IllegalBlockSizeException, BadPaddingException {
394: if (input != null) {
395: bOut.write(input, inputOffset, inputLen);
396: }
397:
398: if (cipher instanceof RSABlindedEngine) {
399: if (bOut.size() > cipher.getInputBlockSize() + 1) {
400: throw new ArrayIndexOutOfBoundsException(
401: "too much data for RSA block");
402: }
403: } else {
404: if (bOut.size() > cipher.getInputBlockSize()) {
405: throw new ArrayIndexOutOfBoundsException(
406: "too much data for RSA block");
407: }
408: }
409:
410: byte[] out;
411:
412: try {
413: byte[] bytes = bOut.toByteArray();
414: bOut.reset();
415:
416: out = cipher.processBlock(bytes, 0, bytes.length);
417: } catch (InvalidCipherTextException e) {
418: throw new BadPaddingException(e.getMessage());
419: }
420:
421: for (int i = 0; i != out.length; i++) {
422: output[outputOffset + i] = out[i];
423: }
424:
425: return out.length;
426: }
427:
428: /**
429: * classes that inherit from us.
430: */
431:
432: static public class NoPadding extends JCERSACipher {
433: public NoPadding() {
434: super (new RSABlindedEngine());
435: }
436: }
437:
438: static public class PKCS1v1_5Padding extends JCERSACipher {
439: public PKCS1v1_5Padding() {
440: super (new PKCS1Encoding(new RSABlindedEngine()));
441: }
442: }
443:
444: static public class PKCS1v1_5Padding_PrivateOnly extends
445: JCERSACipher {
446: public PKCS1v1_5Padding_PrivateOnly() {
447: super (false, true,
448: new PKCS1Encoding(new RSABlindedEngine()));
449: }
450: }
451:
452: static public class PKCS1v1_5Padding_PublicOnly extends
453: JCERSACipher {
454: public PKCS1v1_5Padding_PublicOnly() {
455: super (true, false,
456: new PKCS1Encoding(new RSABlindedEngine()));
457: }
458: }
459:
460: static public class OAEPPadding extends JCERSACipher {
461: public OAEPPadding() {
462: super (OAEPParameterSpec.DEFAULT);
463: }
464: }
465:
466: static public class ISO9796d1Padding extends JCERSACipher {
467: public ISO9796d1Padding() {
468: super (new ISO9796d1Encoding(new RSABlindedEngine()));
469: }
470: }
471: }
|