/*
* oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
*
* Copyright (c) 2014, Gluu
*/
package org.xdi.oxauth.model.jws;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.xdi.oxauth.model.crypto.Certificate;
import org.xdi.oxauth.model.crypto.signature.RSAPrivateKey;
import org.xdi.oxauth.model.crypto.signature.RSAPublicKey;
import org.xdi.oxauth.model.crypto.signature.SignatureAlgorithm;
import org.xdi.oxauth.model.util.Base64Util;
import org.xdi.oxauth.model.util.Util;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
/**
* @author Javier Rojas Blum
* @version July 31, 2016
*/
public class RSASigner extends AbstractJwsSigner {
private RSAPrivateKey rsaPrivateKey;
private RSAPublicKey rsaPublicKey;
public RSASigner(SignatureAlgorithm signatureAlgorithm, RSAPrivateKey rsaPrivateKey) {
super(signatureAlgorithm);
this.rsaPrivateKey = rsaPrivateKey;
}
public RSASigner(SignatureAlgorithm signatureAlgorithm, RSAPublicKey rsaPublicKey) {
super(signatureAlgorithm);
this.rsaPublicKey = rsaPublicKey;
}
public RSASigner(SignatureAlgorithm signatureAlgorithm, Certificate certificate) {
super(signatureAlgorithm);
this.rsaPublicKey = certificate.getRsaPublicKey();
}
@Override
public String generateSignature(String signingInput) throws SignatureException {
if (getSignatureAlgorithm() == null) {
throw new SignatureException("The signature algorithm is null");
}
if (rsaPrivateKey == null) {
throw new SignatureException("The RSA private key is null");
}
if (signingInput == null) {
throw new SignatureException("The signing input is null");
}
try {
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(
rsaPrivateKey.getModulus(),
rsaPrivateKey.getPrivateExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
PrivateKey privateKey = keyFactory.generatePrivate(rsaPrivateKeySpec);
Signature signature = Signature.getInstance(getSignatureAlgorithm().getAlgorithm(), "BC");
signature.initSign(privateKey);
signature.update(signingInput.getBytes(Util.UTF8_STRING_ENCODING));
return Base64Util.base64urlencode(signature.sign());
} catch (InvalidKeySpecException e) {
throw new SignatureException(e);
} catch (InvalidKeyException e) {
throw new SignatureException(e);
} catch (NoSuchAlgorithmException e) {
throw new SignatureException(e);
} catch (NoSuchProviderException e) {
throw new SignatureException(e);
} catch (SignatureException e) {
throw new SignatureException(e);
} catch (UnsupportedEncodingException e) {
throw new SignatureException(e);
} catch (Exception e) {
throw new SignatureException(e);
}
}
@Override
public boolean validateSignature(String signingInput, String signature) throws SignatureException {
if (getSignatureAlgorithm() == null) {
throw new SignatureException("The signature algorithm is null");
}
if (rsaPublicKey == null) {
throw new SignatureException("The RSA public key is null");
}
if (signingInput == null) {
throw new SignatureException("The signing input is null");
}
String algorithm = null;
switch (getSignatureAlgorithm()) {
case RS256:
algorithm = "SHA-256";
break;
case RS384:
algorithm = "SHA-384";
break;
case RS512:
algorithm = "SHA-512";
break;
default:
throw new SignatureException("Unsupported signature algorithm");
}
ASN1InputStream aIn = null;
try {
byte[] sigBytes = Base64Util.base64urldecode(signature);
byte[] sigInBytes = signingInput.getBytes(Util.UTF8_STRING_ENCODING);
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(
rsaPublicKey.getModulus(),
rsaPublicKey.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
PublicKey publicKey = keyFactory.generatePublic(rsaPublicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decSig = cipher.doFinal(sigBytes);
aIn = new ASN1InputStream(decSig);
ASN1Sequence seq = (ASN1Sequence) aIn.readObject();
MessageDigest hash = MessageDigest.getInstance(algorithm, "BC");
hash.update(sigInBytes);
ASN1OctetString sigHash = (ASN1OctetString) seq.getObjectAt(1);
return MessageDigest.isEqual(hash.digest(), sigHash.getOctets());
} catch (IOException e) {
throw new SignatureException(e);
} catch (NoSuchAlgorithmException e) {
throw new SignatureException(e);
} catch (InvalidKeyException e) {
throw new SignatureException(e);
} catch (InvalidKeySpecException e) {
throw new SignatureException(e);
} catch (NoSuchPaddingException e) {
throw new SignatureException(e);
} catch (BadPaddingException e) {
throw new SignatureException(e);
} catch (NoSuchProviderException e) {
throw new SignatureException(e);
} catch (IllegalBlockSizeException e) {
throw new SignatureException(e);
} catch (Exception e) {
throw new SignatureException(e);
} finally {
IOUtils.closeQuietly(aIn);
}
}
}