1 package org.bouncycastle.crypto.encodings; 2 3 import java.security.AccessController; 4 import java.security.PrivilegedAction; 5 import java.security.SecureRandom; 6 7 import org.bouncycastle.crypto.AsymmetricBlockCipher; 8 import org.bouncycastle.crypto.CipherParameters; 9 import org.bouncycastle.crypto.InvalidCipherTextException; 10 import org.bouncycastle.crypto.params.AsymmetricKeyParameter; 11 import org.bouncycastle.crypto.params.ParametersWithRandom; 12 13 /** 14 * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this 15 * depends on your application - see PKCS1 Version 2 for details. 16 */ 17 public class PKCS1Encoding 18 implements AsymmetricBlockCipher 19 { 20 /** 21 * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to 22 * work with one of these set the system property org.bouncycastle.pkcs1.strict to false. 23 * <p> 24 * The system property is checked during construction of the encoding object, it is set to 25 * true by default. 26 * </p> 27 */ 28 public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict"; 29 30 private static final int HEADER_LENGTH = 10; 31 32 private SecureRandom random; 33 private AsymmetricBlockCipher engine; 34 private boolean forEncryption; 35 private boolean forPrivateKey; 36 private boolean useStrictLength; 37 38 /** 39 * Basic constructor. 40 * @param cipher 41 */ 42 public PKCS1Encoding( 43 AsymmetricBlockCipher cipher) 44 { 45 this.engine = cipher; 46 this.useStrictLength = useStrict(); 47 } 48 49 // 50 // for J2ME compatibility 51 // 52 private boolean useStrict() 53 { 54 // required if security manager has been installed. 55 String strict = (String)AccessController.doPrivileged(new PrivilegedAction() 56 { 57 public Object run() 58 { 59 return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY); 60 } 61 }); 62 63 return strict == null || strict.equals("true"); 64 } 65 66 public AsymmetricBlockCipher getUnderlyingCipher() 67 { 68 return engine; 69 } 70 71 public void init( 72 boolean forEncryption, 73 CipherParameters param) 74 { 75 AsymmetricKeyParameter kParam; 76 77 if (param instanceof ParametersWithRandom) 78 { 79 ParametersWithRandom rParam = (ParametersWithRandom)param; 80 81 this.random = rParam.getRandom(); 82 kParam = (AsymmetricKeyParameter)rParam.getParameters(); 83 } 84 else 85 { 86 this.random = new SecureRandom(); 87 kParam = (AsymmetricKeyParameter)param; 88 } 89 90 engine.init(forEncryption, param); 91 92 this.forPrivateKey = kParam.isPrivate(); 93 this.forEncryption = forEncryption; 94 } 95 96 public int getInputBlockSize() 97 { 98 int baseBlockSize = engine.getInputBlockSize(); 99 100 if (forEncryption) 101 { 102 return baseBlockSize - HEADER_LENGTH; 103 } 104 else 105 { 106 return baseBlockSize; 107 } 108 } 109 110 public int getOutputBlockSize() 111 { 112 int baseBlockSize = engine.getOutputBlockSize(); 113 114 if (forEncryption) 115 { 116 return baseBlockSize; 117 } 118 else 119 { 120 return baseBlockSize - HEADER_LENGTH; 121 } 122 } 123 124 public byte[] processBlock( 125 byte[] in, 126 int inOff, 127 int inLen) 128 throws InvalidCipherTextException 129 { 130 if (forEncryption) 131 { 132 return encodeBlock(in, inOff, inLen); 133 } 134 else 135 { 136 return decodeBlock(in, inOff, inLen); 137 } 138 } 139 140 private byte[] encodeBlock( 141 byte[] in, 142 int inOff, 143 int inLen) 144 throws InvalidCipherTextException 145 { 146 if (inLen > getInputBlockSize()) 147 { 148 throw new IllegalArgumentException("input data too large"); 149 } 150 151 byte[] block = new byte[engine.getInputBlockSize()]; 152 153 if (forPrivateKey) 154 { 155 block[0] = 0x01; // type code 1 156 157 for (int i = 1; i != block.length - inLen - 1; i++) 158 { 159 block[i] = (byte)0xFF; 160 } 161 } 162 else 163 { 164 random.nextBytes(block); // random fill 165 166 block[0] = 0x02; // type code 2 167 168 // 169 // a zero byte marks the end of the padding, so all 170 // the pad bytes must be non-zero. 171 // 172 for (int i = 1; i != block.length - inLen - 1; i++) 173 { 174 while (block[i] == 0) 175 { 176 block[i] = (byte)random.nextInt(); 177 } 178 } 179 } 180 181 block[block.length - inLen - 1] = 0x00; // mark the end of the padding 182 System.arraycopy(in, inOff, block, block.length - inLen, inLen); 183 184 return engine.processBlock(block, 0, block.length); 185 } 186 187 /** 188 * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format. 189 */ 190 private byte[] decodeBlock( 191 byte[] in, 192 int inOff, 193 int inLen) 194 throws InvalidCipherTextException 195 { 196 byte[] block = engine.processBlock(in, inOff, inLen); 197 198 if (block.length < getOutputBlockSize()) 199 { 200 throw new InvalidCipherTextException("block truncated"); 201 } 202 203 byte type = block[0]; 204 205 if (type != 1 && type != 2) 206 { 207 throw new InvalidCipherTextException("unknown block type"); 208 } 209 210 if (useStrictLength && block.length != engine.getOutputBlockSize()) 211 { 212 throw new InvalidCipherTextException("block incorrect size"); 213 } 214 215 // 216 // find and extract the message block. 217 // 218 int start; 219 220 for (start = 1; start != block.length; start++) 221 { 222 byte pad = block[start]; 223 224 if (pad == 0) 225 { 226 break; 227 } 228 if (type == 1 && pad != (byte)0xff) 229 { 230 throw new InvalidCipherTextException("block padding incorrect"); 231 } 232 } 233 234 start++; // data should start at the next byte 235 236 if (start > block.length || start < HEADER_LENGTH) 237 { 238 throw new InvalidCipherTextException("no data in block"); 239 } 240 241 byte[] result = new byte[block.length - start]; 242 243 System.arraycopy(block, start, result, 0, result.length); 244 245 return result; 246 } 247 } 248