ISO9796d2PSSSignerpublic class ISO9796d2PSSSigner extends Object implements org.bouncycastle.crypto.SignerWithRecoveryISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
Note: the usual length for the salt is the length of the hash
function used in bytes. |
Fields Summary |
---|
public static final int | TRAILER_IMPLICIT | public static final int | TRAILER_RIPEMD160 | public static final int | TRAILER_RIPEMD128 | public static final int | TRAILER_SHA1 | private org.bouncycastle.crypto.Digest | digest | private org.bouncycastle.crypto.AsymmetricBlockCipher | cipher | private SecureRandom | random | private byte[] | standardSalt | private int | hLen | private int | trailer | private int | keyBits | private byte[] | block | private byte[] | mBuf | private int | messageLength | private int | saltLength | private boolean | fullMessage | private byte[] | recoveredMessage |
Constructors Summary |
---|
public ISO9796d2PSSSigner(org.bouncycastle.crypto.AsymmetricBlockCipher cipher, org.bouncycastle.crypto.Digest digest, int saltLength, boolean implicit)Generate a signer for the with either implicit or explicit trailers
for ISO9796-2, scheme 2 or 3.
this.cipher = cipher;
this.digest = digest;
this.hLen = digest.getDigestSize();
this.saltLength = saltLength;
if (implicit)
{
trailer = TRAILER_IMPLICIT;
}
else
{
if (digest instanceof SHA1Digest)
{
trailer = TRAILER_SHA1;
}
// BEGIN android-removed
// else if (digest instanceof RIPEMD160Digest)
// {
// trailer = TRAILER_RIPEMD160;
// }
// else if (digest instanceof RIPEMD128Digest)
// {
// trailer = TRAILER_RIPEMD128;
// }
// END android-removed
else
{
throw new IllegalArgumentException("no valid trailer for digest");
}
}
| public ISO9796d2PSSSigner(org.bouncycastle.crypto.AsymmetricBlockCipher cipher, org.bouncycastle.crypto.Digest digest, int saltLength)Constructor for a signer with an explicit digest trailer.
this(cipher, digest, saltLength, false);
|
Methods Summary |
---|
private void | ItoOSP(int i, byte[] sp)int to octet string.
sp[0] = (byte)(i >>> 24);
sp[1] = (byte)(i >>> 16);
sp[2] = (byte)(i >>> 8);
sp[3] = (byte)(i >>> 0);
| private void | LtoOSP(long l, byte[] sp)long to octet string.
sp[0] = (byte)(l >>> 56);
sp[1] = (byte)(l >>> 48);
sp[2] = (byte)(l >>> 40);
sp[3] = (byte)(l >>> 32);
sp[4] = (byte)(l >>> 24);
sp[5] = (byte)(l >>> 16);
sp[6] = (byte)(l >>> 8);
sp[7] = (byte)(l >>> 0);
| private void | clearBlock(byte[] block)clear possible sensitive data
for (int i = 0; i != block.length; i++)
{
block[i] = 0;
}
| public byte[] | generateSignature()generate a signature for the loaded message using the key we were
initialised with.
int digSize = digest.getDigestSize();
byte[] m2Hash = new byte[digSize];
digest.doFinal(m2Hash, 0);
byte[] C = new byte[8];
LtoOSP(messageLength * 8, C);
digest.update(C, 0, C.length);
digest.update(mBuf, 0, messageLength);
digest.update(m2Hash, 0, m2Hash.length);
byte[] salt;
if (standardSalt != null)
{
salt = standardSalt;
}
else
{
salt = new byte[saltLength];
random.nextBytes(salt);
}
digest.update(salt, 0, salt.length);
byte[] hash = new byte[digest.getDigestSize()];
digest.doFinal(hash, 0);
int tLength = 2;
if (trailer == TRAILER_IMPLICIT)
{
tLength = 1;
}
int off = block.length - messageLength - salt.length - hLen - tLength - 1;
block[off] = 0x01;
System.arraycopy(mBuf, 0, block, off + 1, messageLength);
System.arraycopy(salt, 0, block, off + 1 + messageLength, salt.length);
byte[] dbMask = maskGeneratorFunction1(hash, 0, hash.length, block.length - hLen - tLength);
for (int i = 0; i != dbMask.length; i++)
{
block[i] ^= dbMask[i];
}
System.arraycopy(hash, 0, block, block.length - hLen - tLength, hLen);
if (trailer == TRAILER_IMPLICIT)
{
block[block.length - 1] = (byte)TRAILER_IMPLICIT;
}
else
{
block[block.length - 2] = (byte)(trailer >>> 8);
block[block.length - 1] = (byte)trailer;
}
block[0] &= 0x7f;
byte[] b = cipher.processBlock(block, 0, block.length);
clearBlock(mBuf);
clearBlock(block);
messageLength = 0;
return b;
| public byte[] | getRecoveredMessage()Return a reference to the recoveredMessage message.
return recoveredMessage;
| public boolean | hasFullMessage()Return true if the full message was recoveredMessage.
return fullMessage;
| public void | init(boolean forSigning, org.bouncycastle.crypto.CipherParameters param)Initialise the signer.
RSAKeyParameters kParam = null;
int lengthOfSalt = saltLength;
if (param instanceof ParametersWithRandom)
{
ParametersWithRandom p = (ParametersWithRandom)param;
kParam = (RSAKeyParameters)p.getParameters();
random = p.getRandom();
}
else if (param instanceof ParametersWithSalt)
{
ParametersWithSalt p = (ParametersWithSalt)param;
kParam = (RSAKeyParameters)p.getParameters();
standardSalt = p.getSalt();
lengthOfSalt = standardSalt.length;
}
else
{
kParam = (RSAKeyParameters)param;
if (forSigning)
{
random = new SecureRandom();
}
}
cipher.init(forSigning, kParam);
keyBits = kParam.getModulus().bitLength();
block = new byte[(keyBits + 7) / 8];
if (trailer == TRAILER_IMPLICIT)
{
mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 1];
}
else
{
mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 2];
}
reset();
| private boolean | isSameAs(byte[] a, byte[] b)compare two byte arrays.
if (messageLength != b.length)
{
return false;
}
for (int i = 0; i != b.length; i++)
{
if (a[i] != b[i])
{
return false;
}
}
return true;
| private byte[] | maskGeneratorFunction1(byte[] Z, int zOff, int zLen, int length)mask generator function, as described in PKCS1v2.
byte[] mask = new byte[length];
byte[] hashBuf = new byte[hLen];
byte[] C = new byte[4];
int counter = 0;
digest.reset();
while (counter < (length / hLen))
{
ItoOSP(counter, C);
digest.update(Z, zOff, zLen);
digest.update(C, 0, C.length);
digest.doFinal(hashBuf, 0);
System.arraycopy(hashBuf, 0, mask, counter * hLen, hLen);
counter++;
}
if ((counter * hLen) < length)
{
ItoOSP(counter, C);
digest.update(Z, zOff, zLen);
digest.update(C, 0, C.length);
digest.doFinal(hashBuf, 0);
System.arraycopy(hashBuf, 0, mask, counter * hLen, mask.length - (counter * hLen));
}
return mask;
| public void | reset()reset the internal state
digest.reset();
messageLength = 0;
if (recoveredMessage != null)
{
clearBlock(recoveredMessage);
}
recoveredMessage = null;
fullMessage = false;
| public void | update(byte b)update the internal digest with the byte b
if (messageLength < mBuf.length)
{
mBuf[messageLength++] = b;
}
else
{
digest.update(b);
}
| public void | update(byte[] in, int off, int len)update the internal digest with the byte array in
while (len > 0 && messageLength < mBuf.length)
{
this.update(in[off]);
off++;
len--;
}
if (len > 0)
{
digest.update(in, off, len);
}
| public boolean | verifySignature(byte[] signature)return true if the signature represents a ISO9796-2 signature
for the passed in message.
byte[] block = null;
try
{
block = cipher.processBlock(signature, 0, signature.length);
}
catch (Exception e)
{
return false;
}
//
// adjust block size for leading zeroes if necessary
//
if (block.length < (keyBits + 7) / 8)
{
byte[] tmp = new byte[(keyBits + 7) / 8];
System.arraycopy(block, 0, tmp, tmp.length - block.length, block.length);
block = tmp;
}
int tLength = 0;
if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0)
{
tLength = 1;
}
else
{
int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF);
switch (sigTrail)
{
// BEGIN android-removed
// case TRAILER_RIPEMD160:
// if (!(digest instanceof RIPEMD160Digest))
// {
// throw new IllegalStateException("signer should be initialised with RIPEMD160");
// }
// break;
// END android-removed
case TRAILER_SHA1:
if (!(digest instanceof SHA1Digest))
{
throw new IllegalStateException("signer should be initialised with SHA1");
}
break;
// BEGIN android-removed
// case TRAILER_RIPEMD128:
// if (!(digest instanceof RIPEMD128Digest))
// {
// throw new IllegalStateException("signer should be initialised with RIPEMD128");
// }
// break;
// END android-removed
default:
throw new IllegalArgumentException("unrecognised hash in signature");
}
tLength = 2;
}
//
// calculate H(m2)
//
byte[] m2Hash = new byte[hLen];
digest.doFinal(m2Hash, 0);
//
// remove the mask
//
byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - tLength, hLen, block.length - hLen - tLength);
for (int i = 0; i != dbMask.length; i++)
{
block[i] ^= dbMask[i];
}
block[0] &= 0x7f;
//
// find out how much padding we've got
//
int mStart = 0;
for (mStart = 0; mStart != block.length; mStart++)
{
if (block[mStart] == 0x01)
{
break;
}
}
mStart++;
if (mStart >= block.length)
{
clearBlock(block);
return false;
}
if (mStart > 1)
{
fullMessage = true;
}
else
{
fullMessage = false;
}
recoveredMessage = new byte[dbMask.length - mStart - saltLength];
System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length);
//
// check the hashes
//
byte[] C = new byte[8];
LtoOSP(recoveredMessage.length * 8, C);
digest.update(C, 0, C.length);
if (recoveredMessage.length != 0)
{
digest.update(recoveredMessage, 0, recoveredMessage.length);
}
digest.update(m2Hash, 0, m2Hash.length);
byte[] hash = new byte[digest.getDigestSize()];
digest.update(block, mStart + recoveredMessage.length, dbMask.length - mStart - recoveredMessage.length);
digest.doFinal(hash, 0);
int off = block.length - tLength - hash.length;
for (int i = 0; i != hash.length; i++)
{
if (hash[i] != block[off + i])
{
clearBlock(block);
clearBlock(hash);
clearBlock(recoveredMessage);
fullMessage = false;
return false;
}
}
//
// if they've input a message check what we've recovered against
// what was input.
//
if (messageLength != 0)
{
if (!isSameAs(mBuf, recoveredMessage))
{
clearBlock(mBuf);
clearBlock(block);
return false;
}
}
clearBlock(mBuf);
clearBlock(block);
messageLength = 0;
return true;
|
|