ECDSAKey.java
/*
* @copyright defined in LICENSE.txt
*/
package hera.util.pki;
import static hera.util.ValidationUtils.assertEquals;
import static org.slf4j.LoggerFactory.getLogger;
import hera.util.HexUtils;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
import org.slf4j.Logger;
@EqualsAndHashCode(exclude = "logger")
public class ECDSAKey {
protected final transient Logger logger = getLogger(getClass());
/**
* Create ECDSAKey with keypair.
*
* @param privateKey a private key
* @param publicKey a public key
* @param ecParams an ec parameters
* @return {@link ECDSAKey}
*/
public static ECDSAKey of(final PrivateKey privateKey, final PublicKey publicKey,
final ECDomainParameters ecParams) {
return new ECDSAKey(privateKey, publicKey, ecParams);
}
@Getter
protected final PrivateKey privateKey;
@Getter
protected final PublicKey publicKey;
@Getter
protected final ECDomainParameters params;
@Getter
protected final ECDSAVerifier verifier;
/**
* ECDSAKey constructor.
*
* @param privateKey a private key
* @param publicKey a public key
* @param ecParams an ec parameters
*/
protected ECDSAKey(final PrivateKey privateKey, final PublicKey publicKey,
final ECDomainParameters ecParams) {
this.privateKey = privateKey;
this.publicKey = publicKey;
this.params = ecParams;
this.verifier = new ECDSAVerifier(params);
}
/**
* Sign to message.
*
* @param hashedMessage a sha256-hashed message
*
* @return signature ECDSA signature
*/
public ECDSASignature sign(final byte[] hashedMessage) {
try {
assertEquals(hashedMessage.length, 32, "Sha-256 hashed message should have 32 bytes length");
final ECDSASignature signature = sign(this.privateKey, hashedMessage);
if (logger.isTraceEnabled()) {
logger.trace("Message in hexa: {}", HexUtils.encode(hashedMessage));
logger.trace("ECDSASignature signature: {}", signature);
}
return signature;
} catch (final Throwable e) {
throw new IllegalStateException(e);
}
}
protected ECDSASignature sign(final PrivateKey privateKey, final byte[] message)
throws Exception {
final ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
final BigInteger d = ((org.bouncycastle.jce.interfaces.ECPrivateKey) privateKey).getD();
final ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(d, params);
signer.init(true, privKey);
final BigInteger[] components = signer.generateSignature(message);
final BigInteger r = components[0];
final BigInteger s = params.getN().subtract(components[1]);
return new ECDSASignature(r, s);
}
/**
* Check if {@code signature} is valid for {@code plainText}.
*
* @param hashedMessage a sha256-hashed message
* @param signature ECDSA signature
*
* @return if valid
*/
public boolean verify(final byte[] hashedMessage, final ECDSASignature signature) {
try {
return verifier.verify(getPublicKey(), hashedMessage, signature);
} catch (final Throwable e) {
throw new IllegalStateException(e);
}
}
@Override
public String toString() {
final org.bouncycastle.jce.interfaces.ECPrivateKey ecPrivateKey =
(org.bouncycastle.jce.interfaces.ECPrivateKey) privateKey;
final org.bouncycastle.jce.interfaces.ECPublicKey ecPublicKey =
(org.bouncycastle.jce.interfaces.ECPublicKey) publicKey;
return String.format("%s\n%s", ecPrivateKey.toString(), ecPublicKey.toString());
}
}