All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.crypto.signers.ISO9796d2PSSSigner Maven / Gradle / Ivy

Go to download

The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for Java 1.8 and later with debug enabled.

The newest version!
package org.bouncycastle.crypto.signers;

import java.security.SecureRandom;

import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.SignerWithRecovery;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.params.ParametersWithSalt;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.util.Arrays;

/**
 * ISO9796-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. */ public class ISO9796d2PSSSigner implements SignerWithRecovery { /** @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; private Digest digest; private 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; private byte[] preSig; private byte[] preBlock; private int preMStart; private int preTLength; /** * Generate a signer with either implicit or explicit trailers for ISO9796-2, scheme 2 or 3. * * @param cipher base cipher to use for signature creation/verification * @param digest digest to use. * @param saltLength length of salt in bytes. * @param implicit whether or not the trailer is implicit or gives the hash. */ public ISO9796d2PSSSigner( AsymmetricBlockCipher cipher, Digest digest, int saltLength, boolean implicit) { this.cipher = cipher; this.digest = digest; this.hLen = digest.getDigestSize(); this.saltLength = saltLength; 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. * @param saltLength length of salt in bytes. */ public ISO9796d2PSSSigner( AsymmetricBlockCipher cipher, Digest digest, int saltLength) { this(cipher, digest, saltLength, false); } /** * Initialise the signer. * * @param forSigning true if for signing, false if for verification. * @param param parameters for signature generation/verification. If the * parameters are for generation they should be a ParametersWithRandom, * a ParametersWithSalt, or just an RSAKeyParameters object. If RSAKeyParameters * are passed in a SecureRandom will be created. * @throws IllegalArgumentException if wrong parameter type or a fixed * salt is passed in which is the wrong length. */ public void init( boolean forSigning, CipherParameters param) { RSAKeyParameters kParam; int lengthOfSalt = saltLength; if (param instanceof ParametersWithRandom) { ParametersWithRandom p = (ParametersWithRandom)param; kParam = (RSAKeyParameters)p.getParameters(); if (forSigning) { random = p.getRandom(); } } else if (param instanceof ParametersWithSalt) { ParametersWithSalt p = (ParametersWithSalt)param; kParam = (RSAKeyParameters)p.getParameters(); standardSalt = p.getSalt(); lengthOfSalt = standardSalt.length; if (standardSalt.length != saltLength) { throw new IllegalArgumentException("Fixed salt is of wrong length"); } } else { kParam = (RSAKeyParameters)param; if (forSigning) { random = CryptoServicesRegistrar.getSecureRandom(); } } cipher.init(forSigning, kParam); keyBits = kParam.getModulus().bitLength(); block = new byte[(keyBits + 7) / 8]; if (trailer == ISOTrailers.TRAILER_IMPLICIT) { mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 1]; } else { mBuf = new byte[block.length - digest.getDigestSize() - lengthOfSalt - 1 - 2]; } reset(); } /** * compare two byte arrays - constant time */ private boolean isSameAs( byte[] a, byte[] b) { boolean isOkay = true; if (messageLength != b.length) { isOkay = false; } for (int i = 0; i != b.length; i++) { if (a[i] != b[i]) { isOkay = false; } } return isOkay; } /** * clear possible sensitive data */ private void clearBlock( byte[] block) { for (int i = 0; i != block.length; i++) { block[i] = 0; } } public void updateWithRecoveredMessage(byte[] signature) throws InvalidCipherTextException { byte[] block = cipher.processBlock(signature, 0, signature.length); // // 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); clearBlock(block); block = tmp; } int tLength; if (((block[block.length - 1] & 0xFF) ^ 0xBC) == 0) { tLength = 1; } else { int sigTrail = ((block[block.length - 2] & 0xFF) << 8) | (block[block.length - 1] & 0xFF); Integer trailerObj = ISOTrailers.getTrailer(digest); if (trailerObj != null) { int trailer = trailerObj.intValue(); if (sigTrail != trailer) { if (!(trailer == ISOTrailers.TRAILER_SHA512_256 && sigTrail == 0x40CC)) { throw new IllegalStateException("signer initialised with wrong digest for trailer " + sigTrail); } } } else { 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 != block.length; mStart++) { if (block[mStart] == 0x01) { break; } } mStart++; if (mStart >= block.length) { clearBlock(block); } fullMessage = (mStart > 1); recoveredMessage = new byte[dbMask.length - mStart - saltLength]; System.arraycopy(block, mStart, recoveredMessage, 0, recoveredMessage.length); System.arraycopy(recoveredMessage, 0, mBuf, 0, recoveredMessage.length); preSig = signature; preBlock = block; preMStart = mStart; preTLength = tLength; } /** * update the internal digest with the byte b */ public void update( byte b) { if (preSig == null && messageLength < mBuf.length) { mBuf[messageLength++] = b; } else { digest.update(b); } } /** * update the internal digest with the byte array in */ public void update( byte[] in, int off, int len) { if (preSig == null) { while (len > 0 && messageLength < mBuf.length) { this.update(in[off]); off++; len--; } } if (len > 0) { digest.update(in, off, len); } } /** * reset the internal state */ public void reset() { digest.reset(); messageLength = 0; if (mBuf != null) { clearBlock(mBuf); } if (recoveredMessage != null) { clearBlock(recoveredMessage); recoveredMessage = null; } fullMessage = false; if (preSig != null) { preSig = null; clearBlock(preBlock); preBlock = null; } } /** * generate a signature for the loaded message using the key we were * initialised with. */ public byte[] generateSignature() throws CryptoException { 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 == ISOTrailers.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 == ISOTrailers.TRAILER_IMPLICIT) { block[block.length - 1] = (byte)ISOTrailers.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); recoveredMessage = new byte[messageLength]; fullMessage = (messageLength <= mBuf.length); System.arraycopy(mBuf, 0, recoveredMessage, 0, recoveredMessage.length); clearBlock(mBuf); clearBlock(block); messageLength = 0; return b; } /** * return true if the signature represents a ISO9796-2 signature * for the passed in message. */ public boolean verifySignature( byte[] signature) { // // calculate H(m2) // byte[] m2Hash = new byte[hLen]; digest.doFinal(m2Hash, 0); byte[] block; int tLength; int mStart = 0; if (preSig == null) { try { updateWithRecoveredMessage(signature); } catch (Exception e) { return false; } } else { if (!Arrays.areEqual(preSig, signature)) { throw new IllegalStateException("updateWithRecoveredMessage called on different signature"); } } block = preBlock; mStart = preMStart; tLength = preTLength; preSig = null; preBlock = null; // // 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); // Update for the salt if (standardSalt != null) { digest.update(standardSalt, 0, standardSalt.length); } else { digest.update(block, mStart + recoveredMessage.length, saltLength); } byte[] hash = new byte[digest.getDigestSize()]; digest.doFinal(hash, 0); int off = block.length - tLength - hash.length; boolean isOkay = true; for (int i = 0; i != hash.length; i++) { if (hash[i] != block[off + i]) { isOkay = false; } } clearBlock(block); clearBlock(hash); if (!isOkay) { fullMessage = false; messageLength = 0; clearBlock(recoveredMessage); return false; } // // if they've input a message check what we've recovered against // what was input. // if (messageLength != 0) { if (!isSameAs(mBuf, recoveredMessage)) { messageLength = 0; clearBlock(mBuf); return false; } } messageLength = 0; clearBlock(mBuf); return true; } /** * Return true if the full message was recoveredMessage. * * @return true on full message recovery, false otherwise, or if not sure. * @see org.bouncycastle.crypto.SignerWithRecovery#hasFullMessage() */ public boolean hasFullMessage() { return fullMessage; } /** * Return a reference to the recoveredMessage message, either as it was added * to a just generated signature, or extracted from a verified one. * * @return the full/partial recoveredMessage message. * @see org.bouncycastle.crypto.SignerWithRecovery#getRecoveredMessage() */ public byte[] getRecoveredMessage() { return recoveredMessage; } /** * int to octet string. */ private void ItoOSP( int i, byte[] sp) { sp[0] = (byte)(i >>> 24); sp[1] = (byte)(i >>> 16); sp[2] = (byte)(i >>> 8); sp[3] = (byte)(i >>> 0); } /** * long to octet string. */ private void LtoOSP( long l, byte[] sp) { 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); } /** * mask generator function, as described in PKCS1v2. */ private byte[] maskGeneratorFunction1( byte[] Z, int zOff, int zLen, int length) { 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; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy