package de.persosim.simulator.crypto.certificates;
import static org.globaltester.logging.BasicLogger.DEBUG;
import static org.globaltester.logging.BasicLogger.log;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import org.globaltester.cryptoprovider.Crypto;
import de.persosim.simulator.crypto.CryptoUtil;
import de.persosim.simulator.crypto.DomainParameterSetEcdh;
import de.persosim.simulator.protocols.ta.TaOid;
import de.persosim.simulator.tlv.ConstructedTlvDataObject;
import de.persosim.simulator.tlv.PrimitiveTlvDataObject;
import de.persosim.simulator.tlv.TlvConstants;
import de.persosim.simulator.utils.Utils;
/**
* This class represents an EC public key to be used in the context of CV certificates.
*
* @author slutters
*
*/
public class CvEcPublicKey extends CvPublicKey implements ECPublicKey {
private static final long serialVersionUID = 1L;
protected byte[] publicPointEncoding;
public CvEcPublicKey(CvOid cvOid, ECPublicKey ecPublicKey) {
super(cvOid, ecPublicKey);
}
public CvEcPublicKey(ConstructedTlvDataObject publicKeyEncoding) throws GeneralSecurityException {
super(parseOid(publicKeyEncoding), null);
if (cvOid.getKeyType().equals("EC")) {
ECParameterSpec paramSpec = null;
if (publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_86)) {
if (publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_81)
&& publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_82)
&& publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_83)
&& publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_84)
&& publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_85)
&& publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_87)) {
paramSpec = CryptoUtil.parseParameterSpecEc(publicKeyEncoding);
if (publicKeyEncoding.containsTlvDataObject(TlvConstants.TAG_86)) {
key = CryptoUtil.parsePublicKeyEc(publicKeyEncoding, paramSpec);
} else{
throw new IllegalArgumentException("no public key component found");
}
} else {
publicPointEncoding = publicKeyEncoding.getTlvDataObject(TAG_86).getValueField();
}
} else{
throw new IllegalArgumentException("no public key component found");
}
} else{
throw new IllegalArgumentException("no EC key indicated by OID");
}
}
private static CvOid parseOid(ConstructedTlvDataObject publicKeyData) {
// TODO replace fixed TaOid with OID factory, checking for CvOid compliance
return new TaOid(publicKeyData.getTlvDataObject(TlvConstants.TAG_06).getValueField());
}
@Override
public ConstructedTlvDataObject toTlvDataObject(boolean includeConditionalObjects) {
ConstructedTlvDataObject publicKeyBody = new ConstructedTlvDataObject(TAG_7F49);
PrimitiveTlvDataObject objectIdentifier = new PrimitiveTlvDataObject(TAG_06, this.cvOid.toByteArray());
publicKeyBody.addTlvDataObject(objectIdentifier);
if(isComplete()) {
ECParameterSpec ecParams = ((ECPublicKey) key).getParams();
EllipticCurve curve = ecParams.getCurve();
int referenceLength = DomainParameterSetEcdh.getPublicPointReferenceLengthL(((ECFieldFp) curve.getField()).getP());
PrimitiveTlvDataObject publicPoint = new PrimitiveTlvDataObject(TAG_86, CryptoUtil.encode(((ECPublicKey) key).getW(), referenceLength, CryptoUtil.ENCODING_UNCOMPRESSED));
if(includeConditionalObjects) {
PrimitiveTlvDataObject primeModulus = new PrimitiveTlvDataObject(TAG_81, Utils.toUnsignedByteArray(((ECFieldFp) curve.getField()).getP()));
PrimitiveTlvDataObject firstCoefficient = new PrimitiveTlvDataObject(TAG_82, Utils.toUnsignedByteArray(curve.getA()));
PrimitiveTlvDataObject secondCoefficient = new PrimitiveTlvDataObject(TAG_83, Utils.toUnsignedByteArray(curve.getB()));
PrimitiveTlvDataObject basePoint = new PrimitiveTlvDataObject(TAG_84, CryptoUtil.encode(ecParams.getGenerator(), referenceLength, CryptoUtil.ENCODING_UNCOMPRESSED));
PrimitiveTlvDataObject orderOfTheBasePoint = new PrimitiveTlvDataObject(TAG_85, Utils.toUnsignedByteArray(ecParams.getOrder()));
publicKeyBody.addTlvDataObject(primeModulus);
publicKeyBody.addTlvDataObject(firstCoefficient);
publicKeyBody.addTlvDataObject(secondCoefficient);
publicKeyBody.addTlvDataObject(basePoint);
publicKeyBody.addTlvDataObject(orderOfTheBasePoint);
}
publicKeyBody.addTlvDataObject(publicPoint);
if(includeConditionalObjects) {
PrimitiveTlvDataObject coFactor = new PrimitiveTlvDataObject(TAG_87, Utils.toUnsignedByteArray(new BigInteger((new Integer(ecParams.getCofactor())).toString())));
publicKeyBody.addTlvDataObject(coFactor);
}
} else{
PrimitiveTlvDataObject publicPoint = new PrimitiveTlvDataObject(TAG_86, publicPointEncoding);
publicKeyBody.addTlvDataObject(publicPoint);
}
return publicKeyBody;
}
@Override
public ECParameterSpec getParams() {
if(key != null) {
return ((ECPublicKey) key).getParams();
} else{
return null;
}
}
@Override
public ECPoint getW() {
if(key != null) {
return ((ECPublicKey) key).getW();
} else{
return null;
}
}
@Override
public boolean isComplete() {
return key != null;
}
@Override
public KeyPairGenerator getKeyPairGenerator(SecureRandom secRandom) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator;
keyPairGenerator = KeyPairGenerator.getInstance(getAlgorithm(), Crypto.getCryptoProvider());
keyPairGenerator.initialize(getParams(), secRandom);
return keyPairGenerator;
}
@Override
public boolean updateKey(PublicKey publicKey) {
if(key == null) {
if(publicKey instanceof ECPublicKey) {
ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
ECParameterSpec ecParams = ecPublicKey.getParams();
if(ecParams == null) {
log(CvEcPublicKey.class, "updating key must provide domain parameters", DEBUG);
return false;
}
DomainParameterSetEcdh domParamsEcdh = new DomainParameterSetEcdh(ecParams);
ECPoint publicPoint = DomainParameterSetEcdh.reconstructPoint(publicPointEncoding);
key = domParamsEcdh.reconstructPublicKey(publicPoint, Crypto.getCryptoProvider());
if(key == null) {
log(CvEcPublicKey.class, "key update failed", DEBUG);
return false;
} else{
publicPointEncoding = null;
log(CvEcPublicKey.class, "key update successfull", DEBUG);
return true;
}
} else{
throw new IllegalArgumentException("updating key must be of type ECPublicKey");
}
} else{
log(CvEcPublicKey.class, "key update unnecessary", DEBUG);
return false; // key already complete and fully usable
}
}
@Override
public boolean matchesCoreMaterial(CvPublicKey publicKey) {
if (publicKey instanceof CvEcPublicKey){
CvEcPublicKey cvKey = (CvEcPublicKey) publicKey;
return cvKey.getW().equals(this.getW());
}
return false;
}
}