001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2015 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jws.handlers;
018
019import org.forgerock.json.jose.exceptions.JwsSigningException;
020import org.forgerock.json.jose.exceptions.JwsVerifyingException;
021import org.forgerock.json.jose.jws.JwsAlgorithm;
022import org.forgerock.json.jose.jws.JwsAlgorithmType;
023import org.forgerock.json.jose.utils.Utils;
024import org.forgerock.util.Reject;
025import org.forgerock.util.SignatureUtil;
026
027import java.security.InvalidKeyException;
028import java.security.Key;
029import java.security.NoSuchAlgorithmException;
030import java.security.PrivateKey;
031import java.security.PublicKey;
032import java.security.Signature;
033import java.security.SignatureException;
034
035/**
036 * An implementation of the SigningHandler which can sign and verify using algorithms from the RSA family.
037 *
038 * @since 2.0.0
039 */
040public class RSASigningHandler implements SigningHandler {
041
042    private final SignatureUtil signatureUtil;
043    private final Key key;
044
045    /**
046     * Constructs a new RSASigningHandler, with a SignatureUtil instance to delegate the signing and verifying calls to.
047     *
048     * @param key The key used to sign and verify the signature.
049     * @param signatureUtil An instance of the SignatureUtil.
050     */
051    public RSASigningHandler(Key key, SignatureUtil signatureUtil) {
052        this.key = key;
053        this.signatureUtil = signatureUtil;
054    }
055
056    /**
057     * {@inheritDoc}
058     */
059    @Override
060    public byte[] sign(JwsAlgorithm algorithm, String data) {
061        validateAlgorithm(algorithm);
062        try {
063            Reject.ifFalse(key instanceof PrivateKey, "RSA requires private key for signing.");
064            return signatureUtil.sign((PrivateKey) key, algorithm.getAlgorithm(), data);
065        } catch (SignatureException e) {
066            if (e.getCause() != null && e.getCause().getClass().isAssignableFrom(NoSuchAlgorithmException.class)) {
067                throw new JwsSigningException("Unsupported Signing Algorithm, " + algorithm.getAlgorithm(), e);
068            }
069            throw new JwsSigningException(e);
070        }
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    @Override
077    public byte[] sign(final JwsAlgorithm algorithm, final byte[] data) {
078        validateAlgorithm(algorithm);
079        try {
080            Reject.ifFalse(key instanceof PrivateKey, "RSA requires private key for signing.");
081            Signature signature = Signature.getInstance(algorithm.getAlgorithm());
082            signature.initSign((PrivateKey) key);
083            signature.update(data);
084            return signature.sign();
085        } catch (SignatureException | InvalidKeyException e) {
086            throw new JwsSigningException(e);
087        } catch (NoSuchAlgorithmException e) {
088            throw new JwsSigningException("Unsupported Signing Algorithm, " + algorithm.getAlgorithm(), e);
089        }
090    }
091
092    /**
093     * {@inheritDoc}
094     */
095    @Override
096    public boolean verify(JwsAlgorithm algorithm, byte[] data, byte[] signature) {
097        validateAlgorithm(algorithm);
098        try {
099            Reject.ifFalse(key instanceof PublicKey, "RSA requires public key for signature verification.");
100            return signatureUtil.verify((PublicKey) key, algorithm.getAlgorithm(),
101                    new String(data, Utils.CHARSET), signature);
102        } catch (SignatureException e) {
103            if (e.getCause() != null && e.getCause().getClass().isAssignableFrom(NoSuchAlgorithmException.class)) {
104                throw new JwsVerifyingException("Unsupported Signing Algorithm, " + algorithm.getAlgorithm(), e);
105            }
106            throw new JwsVerifyingException(e);
107        }
108    }
109
110    private void validateAlgorithm(JwsAlgorithm algorithm) {
111        Reject.ifNull(algorithm, "Algorithm must not be null.");
112        Reject.ifTrue(algorithm.getAlgorithmType() != JwsAlgorithmType.RSA, "Not an RSA algorithm.");
113    }
114}