001: package org.bouncycastle.crypto.encodings;
002:
003: import org.bouncycastle.crypto.AsymmetricBlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.InvalidCipherTextException;
006: import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
007: import org.bouncycastle.crypto.params.ParametersWithRandom;
008:
009: import java.security.SecureRandom;
010:
011: /**
012: * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
013: * depends on your application - see PKCS1 Version 2 for details.
014: */
015: public class PKCS1Encoding implements AsymmetricBlockCipher {
016: /**
017: * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
018: * work with one of these set the system property org.bouncycastle.pkcs1.strict to false.
019: * <p>
020: * The system property is checked during construction of the encoding object, it is set to
021: * true by default.
022: * </p>
023: */
024: public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
025:
026: private static final int HEADER_LENGTH = 10;
027:
028: private SecureRandom random;
029: private AsymmetricBlockCipher engine;
030: private boolean forEncryption;
031: private boolean forPrivateKey;
032: private boolean useStrictLength;
033:
034: /**
035: * Basic constructor.
036: * @param cipher
037: */
038: public PKCS1Encoding(AsymmetricBlockCipher cipher) {
039: this .engine = cipher;
040: this .useStrictLength = useStrict();
041: }
042:
043: //
044: // for J2ME compatibility
045: //
046: private boolean useStrict() {
047: String strict = System
048: .getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
049:
050: return strict == null || strict.equals("true");
051: }
052:
053: public AsymmetricBlockCipher getUnderlyingCipher() {
054: return engine;
055: }
056:
057: public void init(boolean forEncryption, CipherParameters param) {
058: AsymmetricKeyParameter kParam;
059:
060: if (param instanceof ParametersWithRandom) {
061: ParametersWithRandom rParam = (ParametersWithRandom) param;
062:
063: this .random = rParam.getRandom();
064: kParam = (AsymmetricKeyParameter) rParam.getParameters();
065: } else {
066: this .random = new SecureRandom();
067: kParam = (AsymmetricKeyParameter) param;
068: }
069:
070: engine.init(forEncryption, param);
071:
072: this .forPrivateKey = kParam.isPrivate();
073: this .forEncryption = forEncryption;
074: }
075:
076: public int getInputBlockSize() {
077: int baseBlockSize = engine.getInputBlockSize();
078:
079: if (forEncryption) {
080: return baseBlockSize - HEADER_LENGTH;
081: } else {
082: return baseBlockSize;
083: }
084: }
085:
086: public int getOutputBlockSize() {
087: int baseBlockSize = engine.getOutputBlockSize();
088:
089: if (forEncryption) {
090: return baseBlockSize;
091: } else {
092: return baseBlockSize - HEADER_LENGTH;
093: }
094: }
095:
096: public byte[] processBlock(byte[] in, int inOff, int inLen)
097: throws InvalidCipherTextException {
098: if (forEncryption) {
099: return encodeBlock(in, inOff, inLen);
100: } else {
101: return decodeBlock(in, inOff, inLen);
102: }
103: }
104:
105: private byte[] encodeBlock(byte[] in, int inOff, int inLen)
106: throws InvalidCipherTextException {
107: byte[] block = new byte[engine.getInputBlockSize()];
108:
109: if (forPrivateKey) {
110: block[0] = 0x01; // type code 1
111:
112: for (int i = 1; i != block.length - inLen - 1; i++) {
113: block[i] = (byte) 0xFF;
114: }
115: } else {
116: random.nextBytes(block); // random fill
117:
118: block[0] = 0x02; // type code 2
119:
120: //
121: // a zero byte marks the end of the padding, so all
122: // the pad bytes must be non-zero.
123: //
124: for (int i = 1; i != block.length - inLen - 1; i++) {
125: while (block[i] == 0) {
126: block[i] = (byte) random.nextInt();
127: }
128: }
129: }
130:
131: block[block.length - inLen - 1] = 0x00; // mark the end of the padding
132: System.arraycopy(in, inOff, block, block.length - inLen, inLen);
133:
134: return engine.processBlock(block, 0, block.length);
135: }
136:
137: /**
138: * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
139: */
140: private byte[] decodeBlock(byte[] in, int inOff, int inLen)
141: throws InvalidCipherTextException {
142: byte[] block = engine.processBlock(in, inOff, inLen);
143:
144: if (block.length < getOutputBlockSize()) {
145: throw new InvalidCipherTextException("block truncated");
146: }
147:
148: byte type = block[0];
149:
150: if (type != 1 && type != 2) {
151: throw new InvalidCipherTextException("unknown block type");
152: }
153:
154: if (useStrictLength
155: && block.length != engine.getOutputBlockSize()) {
156: throw new InvalidCipherTextException("block incorrect size");
157: }
158:
159: //
160: // find and extract the message block.
161: //
162: int start;
163:
164: for (start = 1; start != block.length; start++) {
165: byte pad = block[start];
166:
167: if (pad == 0) {
168: break;
169: }
170: if (type == 1 && pad != (byte) 0xff) {
171: throw new InvalidCipherTextException(
172: "block padding incorrect");
173: }
174: }
175:
176: start++; // data should start at the next byte
177:
178: if (start >= block.length || start < HEADER_LENGTH) {
179: throw new InvalidCipherTextException("no data in block");
180: }
181:
182: byte[] result = new byte[block.length - start];
183:
184: System.arraycopy(block, start, result, 0, result.length);
185:
186: return result;
187: }
188: }
|