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

se.swedenconnect.signservice.signature.signer.crypto.PSSPadding Maven / Gradle / Ivy

There is a newer version: 1.1.2
Show newest version
/*
 * Copyright 2022 Sweden Connect
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.swedenconnect.signservice.signature.signer.crypto;

import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Digest;

import java.security.SecureRandom;
import java.util.Arrays;

/**
 * RSA-PSS as described in PKCS# 1 v 2.1.
 * 

* Note: the usual value for the salt length is the number of * bytes in the hash function. *

*/ public class PSSPadding { /** The default trailer octet to use */ static final public byte TRAILER_IMPLICIT = (byte) 0xBC; private final Digest contentDigest; private final Digest mgfDigest; private SecureRandom random; private final int modulusBits; private final int hLen; private final int mgfhLen; private final boolean sSet; private final int sLen; private int emBits; private final byte[] salt; private final byte[] mDash; private byte[] block; private final byte trailer; /** * Basic constructor. * * @param modulusBits number of bits in RSA key modulus * @param digest the digest to use. */ public PSSPadding( final int modulusBits, final Digest digest) { this(modulusBits, digest, digest.getDigestSize(), TRAILER_IMPLICIT); } /** * Basic constructor. * * @param modulusBits number of bits in RSA key modulus * @param digest the digest to use. * @param sLen the length of the salt to use (in bytes) */ public PSSPadding( final int modulusBits, final Digest digest, final int sLen) { this(modulusBits, digest, sLen, TRAILER_IMPLICIT); } /** * Constructor. * * @param modulusBits number of bits in RSA key modulus * @param digest the digest to use. * @param sLen the length of the salt to use (in bytes) * @param trailer the trailer byte to use */ public PSSPadding( final int modulusBits, final Digest digest, final int sLen, final byte trailer) { this(modulusBits, digest, digest, sLen, trailer); } /** * Constructor. * * @param modulusBits number of bits in RSA key modulus * @param contentDigest the digest to use for content * @param mgfDigest the digest to use for MGF * @param sLen the length of the salt to use (in bytes) */ public PSSPadding( final int modulusBits, final Digest contentDigest, final Digest mgfDigest, final int sLen) { this(modulusBits, contentDigest, mgfDigest, sLen, TRAILER_IMPLICIT); } /** * Constructor * * @param modulusBits number of bits in RSA key modulus * @param contentDigest the digest to use for content * @param mgfDigest the digest to use for MGF * @param sLen the length of the salt to use (in bytes) * @param trailer the trailer byte to use */ public PSSPadding( final int modulusBits, final Digest contentDigest, final Digest mgfDigest, final int sLen, final byte trailer) { this.modulusBits = modulusBits; this.contentDigest = contentDigest; this.mgfDigest = mgfDigest; this.hLen = contentDigest.getDigestSize(); this.mgfhLen = mgfDigest.getDigestSize(); this.sSet = false; this.sLen = sLen; this.salt = new byte[sLen]; this.mDash = new byte[8 + sLen + hLen]; this.trailer = trailer; init(); } /** * Constructor with explicit salt value. * * @param modulusBits number of bits in RSA key modulus * @param digest the digest to use * @param salt salt value */ public PSSPadding( final int modulusBits, final Digest digest, final byte[] salt) { this(modulusBits, digest, digest, salt, TRAILER_IMPLICIT); } /** * Constructor with explicit salt and MGF hash. * * @param modulusBits number of bits in RSA key modulus * @param contentDigest the digest to use for content * @param mgfDigest the digest to use for MGF * @param salt salt value */ public PSSPadding( final int modulusBits, final Digest contentDigest, final Digest mgfDigest, final byte[] salt) { this(modulusBits, contentDigest, mgfDigest, salt, TRAILER_IMPLICIT); } /** * Constructor with explicit salt, MGF hash algorithm and trailer. * * @param modulusBits number of bits in RSA key modulus * @param contentDigest the digest to use for content * @param mgfDigest the digest to use for MGF * @param salt salt value * @param trailer the trailer byte to use */ public PSSPadding( final int modulusBits, final Digest contentDigest, final Digest mgfDigest, final byte[] salt, final byte trailer) { this.modulusBits = modulusBits; this.contentDigest = contentDigest; this.mgfDigest = mgfDigest; this.hLen = contentDigest.getDigestSize(); this.mgfhLen = mgfDigest.getDigestSize(); this.sSet = true; this.sLen = salt.length; this.salt = salt; this.mDash = new byte[8 + sLen + hLen]; this.trailer = trailer; init(); } /** * Init pss padding. */ private void init() { random = CryptoServicesRegistrar.getSecureRandom(); emBits = modulusBits - 1; if (emBits < (8 * hLen + 8 * sLen + 9)) { throw new IllegalArgumentException("key too small for specified hash and salt lengths"); } block = new byte[(emBits + 7) / 8]; reset(); } /** * clear possible sensitive data. */ private void clearBlock(final byte[] block) { Arrays.fill(block, (byte) 0); } /** * update the internal digest with the byte b * * @param b byte to append to internal digest */ public void update(final byte b) { contentDigest.update(b); } /** * update the internal digest with a byte array.* * * @param in the byte array to add to the internal digest */ public void update(final byte[] in) { contentDigest.update(in, 0, in.length); } /** * update the internal digest. * * @param in input * @param off offset from start * @param len length of data to copy from input */ public void update(final byte[] in, final int off, final int len) { contentDigest.update(in, off, len); } /** * reset the internal state */ public void reset() { contentDigest.reset(); } /** * Generate a padded message for the data that has been loaded using the update() function. * * @return padded message for the data that has been loaded using the update() function */ public byte[] generateSignatureEncodedMessage() throws DataLengthException { contentDigest.doFinal(mDash, mDash.length - hLen - sLen); if (sLen != 0) { if (!sSet) { random.nextBytes(salt); } System.arraycopy(salt, 0, mDash, mDash.length - sLen, sLen); } final byte[] h = new byte[hLen]; contentDigest.update(mDash, 0, mDash.length); contentDigest.doFinal(h, 0); block[block.length - sLen - 1 - hLen - 1] = 0x01; System.arraycopy(salt, 0, block, block.length - sLen - hLen - 1, sLen); byte[] dbMask = maskGeneratorFunction1(h, 0, h.length, block.length - hLen - 1); for (int i = 0; i != dbMask.length; i++) { block[i] ^= dbMask[i]; } block[0] &= (0xff >> ((block.length * 8) - emBits)); System.arraycopy(h, 0, block, block.length - hLen - 1, hLen); block[block.length - 1] = trailer; final byte[] b = new byte[block.length]; System.arraycopy(block, 0, b, 0, block.length); clearBlock(block); return b; } /** * return true if the internal state matches the encodedMessage. The encodedMessage is a pss padded hash value and is typically the * value obtained when performing raw decryption of an RSA-PSS signature value. * * @param encodedMessage encodedMessage to test * @return true if the encodedMessage is consistent with and verified by the PSS padding */ public boolean verifySignatureEncodedMessage(final byte[] encodedMessage) { contentDigest.doFinal(mDash, mDash.length - hLen - sLen); try { System.arraycopy(encodedMessage, 0, block, block.length - encodedMessage.length, encodedMessage.length); } catch (Exception e) { return false; } if (block[block.length - 1] != trailer) { clearBlock(block); return false; } byte[] dbMask = maskGeneratorFunction1(block, block.length - hLen - 1, hLen, block.length - hLen - 1); for (int i = 0; i != dbMask.length; i++) { block[i] ^= dbMask[i]; } block[0] &= (0xff >> ((block.length * 8) - emBits)); for (int i = 0; i != block.length - hLen - sLen - 2; i++) { if (block[i] != 0) { clearBlock(block); return false; } } if (block[block.length - hLen - sLen - 2] != 0x01) { clearBlock(block); return false; } if (sSet) { System.arraycopy(salt, 0, mDash, mDash.length - sLen, sLen); } else { System.arraycopy(block, block.length - sLen - hLen - 1, mDash, mDash.length - sLen, sLen); } contentDigest.update(mDash, 0, mDash.length); contentDigest.doFinal(mDash, mDash.length - hLen); for (int i = block.length - hLen - 1, j = mDash.length - hLen; j != mDash.length; i++, j++) { if ((block[i] ^ mDash[j]) != 0) { clearBlock(mDash); clearBlock(block); return false; } } clearBlock(mDash); clearBlock(block); return true; } /** * int to octet string. */ private void ItoOSP(final int i, final byte[] sp) { sp[0] = (byte) (i >>> 24); sp[1] = (byte) (i >>> 16); sp[2] = (byte) (i >>> 8); sp[3] = (byte) (i); } /** * mask generator function, as described in PKCS1v2. */ private byte[] maskGeneratorFunction1(final byte[] Z, final int zOff, final int zLen, final int length) { final byte[] mask = new byte[length]; final byte[] hashBuf = new byte[mgfhLen]; final byte[] C = new byte[4]; int counter = 0; mgfDigest.reset(); while (counter < (length / mgfhLen)) { ItoOSP(counter, C); mgfDigest.update(Z, zOff, zLen); mgfDigest.update(C, 0, C.length); mgfDigest.doFinal(hashBuf, 0); System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mgfhLen); counter++; } if ((counter * mgfhLen) < length) { ItoOSP(counter, C); mgfDigest.update(Z, zOff, zLen); mgfDigest.update(C, 0, C.length); mgfDigest.doFinal(hashBuf, 0); System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mask.length - (counter * mgfhLen)); } return mask; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy