/*
* 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.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.xdi.oxauth.model.crypto.Certificate;
import org.xdi.oxauth.model.crypto.signature.ECDSAPrivateKey;
import org.xdi.oxauth.model.crypto.signature.ECDSAPublicKey;
import org.xdi.oxauth.model.crypto.signature.SignatureAlgorithm;
import org.xdi.oxauth.model.util.Base64Util;
import org.xdi.oxauth.model.util.Util;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
/**
* @author Javier Rojas Blum
* @version July 31, 2016
*/
public class ECDSASigner extends AbstractJwsSigner {
private ECDSAPrivateKey ecdsaPrivateKey;
private ECDSAPublicKey ecdsaPublicKey;
public ECDSASigner(SignatureAlgorithm signatureAlgorithm, ECDSAPrivateKey ecdsaPrivateKey) {
super(signatureAlgorithm);
this.ecdsaPrivateKey = ecdsaPrivateKey;
}
public ECDSASigner(SignatureAlgorithm signatureAlgorithm, ECDSAPublicKey ecdsaPublicKey) {
super(signatureAlgorithm);
this.ecdsaPublicKey = ecdsaPublicKey;
}
public ECDSASigner(SignatureAlgorithm signatureAlgorithm, Certificate certificate) {
super(signatureAlgorithm);
this.ecdsaPublicKey = certificate.getEcdsaPublicKey();
}
@Override
public String generateSignature(String signingInput) throws SignatureException {
if (getSignatureAlgorithm() == null) {
throw new SignatureException("The signature algorithm is null");
}
if (ecdsaPrivateKey == null) {
throw new SignatureException("The ECDSA private key is null");
}
if (signingInput == null) {
throw new SignatureException("The signing input is null");
}
try {
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(getSignatureAlgorithm().getCurve().getName());
ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(ecdsaPrivateKey.getD(), ecSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
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 (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 (ecdsaPublicKey == null) {
throw new SignatureException("The ECDSA public key is null");
}
if (signingInput == null) {
throw new SignatureException("The signing input is null");
}
String algorithm;
String curve;
switch (getSignatureAlgorithm()) {
case ES256:
algorithm = "SHA256WITHECDSA";
curve = "P-256";
break;
case ES384:
algorithm = "SHA384WITHECDSA";
curve = "P-384";
break;
case ES512:
algorithm = "SHA512WITHECDSA";
curve = "P-521";
break;
default:
throw new SignatureException("Unsupported signature algorithm");
}
try {
byte[] sigBytes = Base64Util.base64urldecode(signature);
byte[] sigInBytes = signingInput.getBytes(Util.UTF8_STRING_ENCODING);
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(curve);
BigInteger q = ((ECCurve.AbstractFp) ecSpec.getCurve()).getField().getCharacteristic();
ECFieldElement xFieldElement = new ECFieldElement.Fp(q, ecdsaPublicKey.getX());
ECFieldElement yFieldElement = new ECFieldElement.Fp(q, ecdsaPublicKey.getY());
ECPoint pointQ = new ECPoint.Fp(ecSpec.getCurve(), xFieldElement, yFieldElement);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(pointQ, ecSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA", "BC");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Signature sig = Signature.getInstance(algorithm, "BC");
sig.initVerify(publicKey);
sig.update(sigInBytes);
return sig.verify(sigBytes);
} 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 (UnsupportedEncodingException e) {
throw new SignatureException(e);
} catch (Exception e) {
throw new SignatureException(e);
}
}
}