| package org.bouncycastle.crypto.signers; |
| |
| import java.math.BigInteger; |
| |
| import org.bouncycastle.crypto.AsymmetricBlockCipher; |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.CryptoException; |
| import org.bouncycastle.crypto.Digest; |
| import org.bouncycastle.crypto.Signer; |
| import org.bouncycastle.crypto.params.RSAKeyParameters; |
| import org.bouncycastle.util.Arrays; |
| import org.bouncycastle.util.BigIntegers; |
| |
| /** |
| * X9.31-1998 - signing using a hash. |
| * <p> |
| * The message digest hash, H, is encapsulated to form a byte string as follows |
| * <pre> |
| * EB = 06 || PS || 0xBA || H || TRAILER |
| * </pre> |
| * where PS is a string of bytes all of value 0xBB of length such that |EB|=|n|, and TRAILER is the ISO/IEC 10118 part number†for the digest. The byte string, EB, is converted to an integer value, the message representative, f. |
| */ |
| public class X931Signer |
| implements Signer |
| { |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_IMPLICIT = 0xBC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_RIPEMD160 = 0x31CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_RIPEMD128 = 0x32CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_SHA1 = 0x33CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_SHA256 = 0x34CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_SHA512 = 0x35CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_SHA384 = 0x36CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_WHIRLPOOL = 0x37CC; |
| /** @deprecated use ISOTrailers */ |
| static final public int TRAILER_SHA224 = 0x38CC; |
| |
| private Digest digest; |
| private AsymmetricBlockCipher cipher; |
| private RSAKeyParameters kParam; |
| |
| private int trailer; |
| private int keyBits; |
| private byte[] block; |
| |
| /** |
| * Generate a signer with either implicit or explicit trailers for X9.31 |
| * |
| * @param cipher base cipher to use for signature creation/verification |
| * @param digest digest to use. |
| * @param implicit whether or not the trailer is implicit or gives the hash. |
| */ |
| public X931Signer( |
| AsymmetricBlockCipher cipher, |
| Digest digest, |
| boolean implicit) |
| { |
| this.cipher = cipher; |
| this.digest = digest; |
| |
| if (implicit) |
| { |
| trailer = ISOTrailers.TRAILER_IMPLICIT; |
| } |
| else |
| { |
| Integer trailerObj = ISOTrailers.getTrailer(digest); |
| |
| if (trailerObj != null) |
| { |
| trailer = trailerObj.intValue(); |
| } |
| else |
| { |
| throw new IllegalArgumentException("no valid trailer for digest: " + digest.getAlgorithmName()); |
| } |
| } |
| } |
| |
| /** |
| * Constructor for a signer with an explicit digest trailer. |
| * |
| * @param cipher cipher to use. |
| * @param digest digest to sign with. |
| */ |
| public X931Signer( |
| AsymmetricBlockCipher cipher, |
| Digest digest) |
| { |
| this(cipher, digest, false); |
| } |
| |
| public void init( |
| boolean forSigning, |
| CipherParameters param) |
| { |
| kParam = (RSAKeyParameters)param; |
| |
| cipher.init(forSigning, kParam); |
| |
| keyBits = kParam.getModulus().bitLength(); |
| |
| block = new byte[(keyBits + 7) / 8]; |
| |
| reset(); |
| } |
| |
| /** |
| * clear possible sensitive data |
| */ |
| private void clearBlock( |
| byte[] block) |
| { |
| for (int i = 0; i != block.length; i++) |
| { |
| block[i] = 0; |
| } |
| } |
| |
| /** |
| * update the internal digest with the byte b |
| */ |
| public void update( |
| byte b) |
| { |
| digest.update(b); |
| } |
| |
| /** |
| * update the internal digest with the byte array in |
| */ |
| public void update( |
| byte[] in, |
| int off, |
| int len) |
| { |
| digest.update(in, off, len); |
| } |
| |
| /** |
| * reset the internal state |
| */ |
| public void reset() |
| { |
| digest.reset(); |
| } |
| |
| /** |
| * generate a signature for the loaded message using the key we were |
| * initialised with. |
| */ |
| public byte[] generateSignature() |
| throws CryptoException |
| { |
| createSignatureBlock(); |
| |
| BigInteger t = new BigInteger(1, cipher.processBlock(block, 0, block.length)); |
| clearBlock(block); |
| |
| t = t.min(kParam.getModulus().subtract(t)); |
| |
| return BigIntegers.asUnsignedByteArray((kParam.getModulus().bitLength() + 7) / 8, t); |
| } |
| |
| private void createSignatureBlock() |
| { |
| int digSize = digest.getDigestSize(); |
| |
| int delta; |
| |
| if (trailer == ISOTrailers.TRAILER_IMPLICIT) |
| { |
| delta = block.length - digSize - 1; |
| digest.doFinal(block, delta); |
| block[block.length - 1] = (byte)ISOTrailers.TRAILER_IMPLICIT; |
| } |
| else |
| { |
| delta = block.length - digSize - 2; |
| digest.doFinal(block, delta); |
| block[block.length - 2] = (byte)(trailer >>> 8); |
| block[block.length - 1] = (byte)trailer; |
| } |
| |
| block[0] = 0x6b; |
| for (int i = delta - 2; i != 0; i--) |
| { |
| block[i] = (byte)0xbb; |
| } |
| block[delta - 1] = (byte)0xba; |
| } |
| |
| /** |
| * return true if the signature represents a ISO9796-2 signature |
| * for the passed in message. |
| */ |
| public boolean verifySignature( |
| byte[] signature) |
| { |
| try |
| { |
| block = cipher.processBlock(signature, 0, signature.length); |
| } |
| catch (Exception e) |
| { |
| return false; |
| } |
| |
| BigInteger t = new BigInteger(1, block); |
| BigInteger f; |
| |
| if ((t.intValue() & 15) == 12) |
| { |
| f = t; |
| } |
| else |
| { |
| t = kParam.getModulus().subtract(t); |
| if ((t.intValue() & 15) == 12) |
| { |
| f = t; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| createSignatureBlock(); |
| |
| byte[] fBlock = BigIntegers.asUnsignedByteArray(block.length, f); |
| |
| boolean rv = Arrays.constantTimeAreEqual(block, fBlock); |
| |
| clearBlock(block); |
| clearBlock(fBlock); |
| |
| return rv; |
| } |
| } |