package org.bouncycastle.crypto.encodings;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.SecureRandom;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
public class PKCS1Encoding
implements AsymmetricBlockCipher
{
public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
private static final int = 10;
private SecureRandom random;
private AsymmetricBlockCipher engine;
private boolean forEncryption;
private boolean forPrivateKey;
private boolean useStrictLength;
public PKCS1Encoding(
AsymmetricBlockCipher cipher)
{
this.engine = cipher;
this.useStrictLength = useStrict();
}
private boolean useStrict()
{
String strict = (String)AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
}
});
return strict == null || strict.equals("true");
}
public AsymmetricBlockCipher getUnderlyingCipher()
{
return engine;
}
public void init(
boolean forEncryption,
CipherParameters param)
{
AsymmetricKeyParameter kParam;
if (param instanceof ParametersWithRandom)
{
ParametersWithRandom rParam = (ParametersWithRandom)param;
this.random = rParam.getRandom();
kParam = (AsymmetricKeyParameter)rParam.getParameters();
}
else
{
this.random = new SecureRandom();
kParam = (AsymmetricKeyParameter)param;
}
engine.init(forEncryption, param);
this.forPrivateKey = kParam.isPrivate();
this.forEncryption = forEncryption;
}
public int getInputBlockSize()
{
int baseBlockSize = engine.getInputBlockSize();
if (forEncryption)
{
return baseBlockSize - HEADER_LENGTH;
}
else
{
return baseBlockSize;
}
}
public int getOutputBlockSize()
{
int baseBlockSize = engine.getOutputBlockSize();
if (forEncryption)
{
return baseBlockSize;
}
else
{
return baseBlockSize - HEADER_LENGTH;
}
}
public byte[] processBlock(
byte[] in,
int inOff,
int inLen)
throws InvalidCipherTextException
{
if (forEncryption)
{
return encodeBlock(in, inOff, inLen);
}
else
{
return decodeBlock(in, inOff, inLen);
}
}
private byte[] encodeBlock(
byte[] in,
int inOff,
int inLen)
throws InvalidCipherTextException
{
if (inLen > getInputBlockSize())
{
throw new IllegalArgumentException("input data too large");
}
byte[] block = new byte[engine.getInputBlockSize()];
if (forPrivateKey)
{
block[0] = 0x01;
for (int i = 1; i != block.length - inLen - 1; i++)
{
block[i] = (byte)0xFF;
}
}
else
{
random.nextBytes(block);
block[0] = 0x02;
for (int i = 1; i != block.length - inLen - 1; i++)
{
while (block[i] == 0)
{
block[i] = (byte)random.nextInt();
}
}
}
block[block.length - inLen - 1] = 0x00;
System.arraycopy(in, inOff, block, block.length - inLen, inLen);
return engine.processBlock(block, 0, block.length);
}
private byte[] decodeBlock(
byte[] in,
int inOff,
int inLen)
throws InvalidCipherTextException
{
byte[] block = engine.processBlock(in, inOff, inLen);
if (block.length < getOutputBlockSize())
{
throw new InvalidCipherTextException("block truncated");
}
byte type = block[0];
if (type != 1 && type != 2)
{
throw new InvalidCipherTextException("unknown block type");
}
if (useStrictLength && block.length != engine.getOutputBlockSize())
{
throw new InvalidCipherTextException("block incorrect size");
}
int start;
for (start = 1; start != block.length; start++)
{
byte pad = block[start];
if (pad == 0)
{
break;
}
if (type == 1 && pad != (byte)0xff)
{
throw new InvalidCipherTextException("block padding incorrect");
}
}
start++;
if (start > block.length || start < HEADER_LENGTH)
{
throw new InvalidCipherTextException("no data in block");
}
byte[] result = new byte[block.length - start];
System.arraycopy(block, start, result, 0, result.length);
return result;
}
}