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

org.xipki.security.util.SignerUtil Maven / Gradle / Ivy

The newest version!
/*
 *
 * Copyright (c) 2013 - 2017 Lijun Liao
 *
 * 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 org.xipki.security.util;

import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.engines.RSABlindedEngine;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.crypto.signers.PSSSigner;
import org.bouncycastle.util.encoders.Hex;
import org.xipki.common.util.ParamUtil;
import org.xipki.security.HashAlgoType;
import org.xipki.security.exception.XiSecurityException;

/**
 * utility class for converting java.security RSA objects into their
 * org.bouncycastle.crypto counterparts.
 *
 * @author Lijun Liao
 * @since 2.0.0
 */

public class SignerUtil {

    private static final Map digestPkcsPrefix = new HashMap<>();

    static {
        digestPkcsPrefix.put(HashAlgoType.SHA1,
                Hex.decode("3021300906052b0e03021a05000414"));
        digestPkcsPrefix.put(HashAlgoType.SHA224,
                Hex.decode("302d300d06096086480165030402040500041c"));
        digestPkcsPrefix.put(HashAlgoType.SHA256,
                Hex.decode("3031300d060960864801650304020105000420"));
        digestPkcsPrefix.put(HashAlgoType.SHA384,
                Hex.decode("3041300d060960864801650304020205000430"));
        digestPkcsPrefix.put(HashAlgoType.SHA512,
                Hex.decode("3051300d060960864801650304020305000440"));
        digestPkcsPrefix.put(HashAlgoType.SHA3_224,
                Hex.decode("302d300d06096086480165030402070500041c"));
        digestPkcsPrefix.put(HashAlgoType.SHA3_256,
                Hex.decode("3031300d060960864801650304020805000420"));
        digestPkcsPrefix.put(HashAlgoType.SHA3_384,
                Hex.decode("3041300d060960864801650304020905000430"));
        digestPkcsPrefix.put(HashAlgoType.SHA3_512,
                Hex.decode("3051300d060960864801650304020a05000440"));
    }

    private SignerUtil() {
    }

    // CHECKSTYLE:SKIP
    public static RSAKeyParameters generateRSAPublicKeyParameter(final RSAPublicKey key) {
        ParamUtil.requireNonNull("key", key);
        return new RSAKeyParameters(false, key.getModulus(), key.getPublicExponent());

    }

    // CHECKSTYLE:SKIP
    public static RSAKeyParameters generateRSAPrivateKeyParameter(final RSAPrivateKey key) {
        ParamUtil.requireNonNull("key", key);
        if (key instanceof RSAPrivateCrtKey) {
            RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;

            return new RSAPrivateCrtKeyParameters(rsaKey.getModulus(), rsaKey.getPublicExponent(),
                    rsaKey.getPrivateExponent(), rsaKey.getPrimeP(), rsaKey.getPrimeQ(),
                    rsaKey.getPrimeExponentP(), rsaKey.getPrimeExponentQ(),
                    rsaKey.getCrtCoefficient());
        } else {
            return new RSAKeyParameters(true, key.getModulus(), key.getPrivateExponent());
        }
    }

    // CHECKSTYLE:SKIP
    public static PSSSigner createPSSRSASigner(final AlgorithmIdentifier sigAlgId)
            throws XiSecurityException {
        return createPSSRSASigner(sigAlgId, null);
    }

    // CHECKSTYLE:SKIP
    public static PSSSigner createPSSRSASigner(final AlgorithmIdentifier sigAlgId,
            final AsymmetricBlockCipher cipher)
            throws XiSecurityException {
        ParamUtil.requireNonNull("sigAlgId", sigAlgId);
        if (!PKCSObjectIdentifiers.id_RSASSA_PSS.equals(sigAlgId.getAlgorithm())) {
            throw new XiSecurityException("signature algorithm " + sigAlgId.getAlgorithm()
                + " is not allowed");
        }

        AlgorithmIdentifier digAlgId;
        try {
            digAlgId = AlgorithmUtil.extractDigesetAlgFromSigAlg(sigAlgId);
        } catch (NoSuchAlgorithmException ex) {
            throw new XiSecurityException(ex.getMessage(), ex);
        }

        RSASSAPSSparams param = RSASSAPSSparams.getInstance(sigAlgId.getParameters());

        AlgorithmIdentifier mfgDigAlgId = AlgorithmIdentifier.getInstance(
                param.getMaskGenAlgorithm().getParameters());

        Digest dig = getDigest(digAlgId);
        Digest mfgDig = getDigest(mfgDigAlgId);

        int saltSize = param.getSaltLength().intValue();
        int trailerField = param.getTrailerField().intValue();
        AsymmetricBlockCipher tmpCipher = (cipher == null) ? new RSABlindedEngine() : cipher;

        return new PSSSigner(tmpCipher, dig, mfgDig, saltSize, getTrailer(trailerField));
    }

    private static byte getTrailer(final int trailerField) {
        if (trailerField == 1) {
            return org.bouncycastle.crypto.signers.PSSSigner.TRAILER_IMPLICIT;
        }

        throw new IllegalArgumentException("unknown trailer field");
    }

    // CHECKSTYLE:SKIP
    public static byte[] EMSA_PKCS1_v1_5_encoding(final byte[] hashValue,
            final int modulusBigLength, final HashAlgoType hashAlgo)
            throws XiSecurityException {
        ParamUtil.requireNonNull("hashValue", hashValue);
        ParamUtil.requireNonNull("hashAlgo", hashAlgo);

        final int hashLen = hashAlgo.length();
        ParamUtil.requireRange("hashValue.length", hashValue.length, hashLen, hashLen);

        int blockSize = (modulusBigLength + 7) / 8;
        byte[] prefix = digestPkcsPrefix.get(hashAlgo);

        if (prefix.length + hashLen + 3 > blockSize) {
            throw new XiSecurityException("data too long (maximal " + (blockSize - 3)
                    + " allowed): " + (prefix.length + hashLen));
        }

        byte[] block = new byte[blockSize];

        block[0] = 0x00;
        // type code 1
        block[1] = 0x01;

        int offset = 2;
        while (offset < block.length - prefix.length - hashLen - 1) {
            block[offset++] = (byte) 0xFF;
        }
        // mark the end of the padding
        block[offset++] = 0x00;

        System.arraycopy(prefix, 0, block, offset, prefix.length);
        offset += prefix.length;
        System.arraycopy(hashValue, 0, block, offset, hashValue.length);
        return block;
    }

    // CHECKSTYLE:SKIP
    public static byte[] EMSA_PKCS1_v1_5_encoding(final byte[] encodedDigestInfo,
            final int modulusBigLength)
            throws XiSecurityException {
        ParamUtil.requireNonNull("encodedDigestInfo", encodedDigestInfo);

        int msgLen = encodedDigestInfo.length;
        int blockSize = (modulusBigLength + 7) / 8;

        if (msgLen + 3 > blockSize) {
            throw new XiSecurityException("data too long (maximal " + (blockSize - 3)
                    + " allowed): " + msgLen);
        }

        byte[] block = new byte[blockSize];

        block[0] = 0x00;
        // type code 1
        block[1] = 0x01;

        int offset = 2;
        while (offset < block.length - msgLen - 1) {
            block[offset++] = (byte) 0xFF;
        }
        // mark the end of the padding
        block[offset++] = 0x00;

        System.arraycopy(encodedDigestInfo, 0, block, offset, encodedDigestInfo.length);
        return block;
    }

    // CHECKSTYLE:SKIP
    public static byte[] EMSA_PSS_ENCODE(final HashAlgoType contentDigest, final byte[] hashValue,
            final HashAlgoType mgfDigest, final int saltLen, final int modulusBitLength,
            final SecureRandom random)
            throws XiSecurityException {
        final int hLen = contentDigest.length();
        final byte[] salt = new byte[saltLen];
        final byte[] mDash = new byte[8 + saltLen + hLen];
        final byte trailer = (byte)0xBC;

        if (hashValue.length != hLen) {
            throw new XiSecurityException("hashValue.length is incorrect: "
                    + hashValue.length + " != " + hLen);
        }

        int emBits = modulusBitLength - 1;
        if (emBits < (8 * hLen + 8 * saltLen + 9)) {
            throw new IllegalArgumentException("key too small for specified hash and salt lengths");
        }

        System.arraycopy(hashValue, 0, mDash, mDash.length - hLen - saltLen, hLen);

        random.nextBytes(salt);
        System.arraycopy(salt, 0, mDash, mDash.length - saltLen, saltLen);

        byte[] hv = contentDigest.hash(mDash);
        byte[] block = new byte[(emBits + 7) / 8];
        block[block.length - saltLen - 1 - hLen - 1] = 0x01;
        System.arraycopy(salt, 0, block, block.length - saltLen - hLen - 1, saltLen);

        byte[] dbMask = maskGeneratorFunction1(mgfDigest, hv, 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(hv, 0, block, block.length - hLen - 1, hLen);

        block[block.length - 1] = trailer;
        return block;
    }

    /**
     * int to octet string.
     */
    private static void ItoOSP( // CHECKSTYLE:SKIP
        final int i, // CHECKSTYLE:SKIP
        final byte[] sp,
        final int spOffset) {
        sp[spOffset + 0] = (byte)(i >>> 24);
        sp[spOffset + 1] = (byte)(i >>> 16);
        sp[spOffset + 2] = (byte)(i >>> 8);
        sp[spOffset + 3] = (byte)(i >>> 0);
    }

    /**
     * mask generator function, as described in PKCS1v2.
     */
    private static byte[] maskGeneratorFunction1(final HashAlgoType mgfDigest,
            final byte[] Z, // CHECKSTYLE:SKIP
            final int length) {
        int mgfhLen = mgfDigest.length();
        byte[] mask = new byte[length];
        int counter = 0;

        byte[] all = new byte[Z.length + 4];
        System.arraycopy(Z, 0, all, 0, Z.length);

        while (counter < (length / mgfhLen)) {
            ItoOSP(counter, all, Z.length);
            byte[] hashBuf = mgfDigest.hash(all);
            System.arraycopy(hashBuf, 0, mask, counter * mgfhLen, mgfhLen);
            counter++;
        }

        if ((counter * mgfhLen) < length) {
            ItoOSP(counter, all, Z.length);
            byte[] hashBuf = mgfDigest.hash(all);
            int offset = counter * mgfhLen;
            System.arraycopy(hashBuf, 0, mask, offset, mask.length - offset);
        }

        return mask;
    }

    // CHECKSTYLE:SKIP
    public static byte[] convertPlainDSASigToX962(final byte[] signature)
            throws XiSecurityException {
        ParamUtil.requireNonNull("signature", signature);
        if (signature.length % 2 != 0) {
            throw new XiSecurityException("signature.lenth must be even, but is odd");
        }
        byte[] ba = new byte[signature.length / 2];
        ASN1EncodableVector sigder = new ASN1EncodableVector();

        System.arraycopy(signature, 0, ba, 0, ba.length);
        sigder.add(new ASN1Integer(new BigInteger(1, ba)));

        System.arraycopy(signature, ba.length, ba, 0, ba.length);
        sigder.add(new ASN1Integer(new BigInteger(1, ba)));

        DERSequence seq = new DERSequence(sigder);
        try {
            return seq.getEncoded();
        } catch (IOException ex) {
            throw new XiSecurityException("IOException, message: " + ex.getMessage(), ex);
        }
    }

    // CHECKSTYLE:SKIP
    public static byte[] convertX962DSASigToPlain(final byte[] x962Signature, final int keyBitLen)
            throws XiSecurityException {
        ParamUtil.requireNonNull("x962Signature", x962Signature);
        ASN1Sequence seq = ASN1Sequence.getInstance(x962Signature);
        if (seq.size() != 2) {
            throw new IllegalArgumentException("invalid X962Signature");
        }
        BigInteger sigR = ASN1Integer.getInstance(seq.getObjectAt(0)).getPositiveValue();
        BigInteger sigS = ASN1Integer.getInstance(seq.getObjectAt(1)).getPositiveValue();
        return convertDSASigToPlain(sigR, sigS, keyBitLen);
    }

    // CHECKSTYLE:SKIP
    public static byte[] convertDSASigToPlain(final BigInteger sigR, final BigInteger sigS,
            final int keyBitLen)
            throws XiSecurityException {
        ParamUtil.requireNonNull("sigR", sigR);
        ParamUtil.requireNonNull("sigS", sigS);

        final int blockSize = (keyBitLen + 7) / 8;
        int bitLenOfR = sigR.bitLength();
        int bitLenOfS = sigS.bitLength();
        int bitLen = Math.max(bitLenOfR, bitLenOfS);
        if ((bitLen + 7) / 8 > blockSize) {
            throw new XiSecurityException("signature is too large");
        }

        byte[] plainSignature = new byte[2 * blockSize];
        bigIntToBytes(sigR, plainSignature, 0, blockSize);
        bigIntToBytes(sigS, plainSignature, blockSize, blockSize);
        return plainSignature;
    }

    private static void bigIntToBytes(final BigInteger num, final byte[] dest, final int destPos,
            final int length) {
        byte[] bytes = num.toByteArray();
        if (bytes.length == length) {
            System.arraycopy(bytes, 0, dest, destPos, length);
        } else if (bytes.length < length) {
            System.arraycopy(bytes, 0, dest, destPos + length - bytes.length, bytes.length);
        } else {
            System.arraycopy(bytes, bytes.length - length, dest, destPos, length);
        }
    }

    public static Digest getDigest(final HashAlgoType hashAlgo) throws XiSecurityException {
        return hashAlgo.createDigest();
    }

    public static Digest getDigest(final AlgorithmIdentifier hashAlgo) throws XiSecurityException {
        HashAlgoType hat = HashAlgoType.getHashAlgoType(hashAlgo.getAlgorithm());
        if (hat != null) {
            return hat.createDigest();
        } else {
            throw new XiSecurityException(
                    "could not get digest for " + hashAlgo.getAlgorithm().getId());
        }
    }

    public static byte[] getDigestPkcsPrefix(HashAlgoType hashAlgo) {
        byte[] bytes = digestPkcsPrefix.get(hashAlgo);
        return (bytes == null) ? null : Arrays.copyOf(bytes, bytes.length);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy