ECDSA (Elliptic Curve Digital Signature Algorithm)
Digital Signature Algorithm은 Nist 표준이며 국내 표준 전자서명 알고리즘이다.
앞에 붙은 "EC"는 Elliptic Curve 연산을 사용하여 DSA가 구현되었다는 의미로 해석할 수 있다.
단, 분명히 ECDSA가 DSA에서 파생된 알고리즘이지만, 서로 다른 알고리즘이다.
참고로 Etheruem과 그 영향을 받은 대부분의 블록체인이 사용자 계정 확인을 위해 ECDSA 전자서명을 사용한다.
ECDSA with Java Lib
- 서명키(비밀키)
- BigInteger 난수 값.
- PrivateKey 객체로 관리됨
- 검증키(공개키)
- ECPoint 값; 서명키 값으로 유도되는 ECC Point.
- PublicKey 객체로 관리됨.
- ECC Parameter Set
- ECDSA 서명/검증 알고리즘에 ECC 연산이 활용됨
- ECC 연산은 EC 파라미터에 따라 연산이 상이함
- 따라서 특정 합의된 EC 파라미터로 ECPoint를 정의하는 것이 중요함.
- 전자서명(공개값)
- 2 BigInteger; (r, s)
- Signature 객체로 관리됨.
PrivateKey, PublicKey, Signature 객체는 모두 byte[] 형으로 Export하는 getEncoded() 함수를 제공한다.
그리고 Encoding은 DER 인코딩을 사용한다.
KeyPair 생성 - random key pair
본 포스팅의 예제는 모두 Secp256k1 parameter set을 사용함.
아직 키쌍을 보유하지 않은 User는 아래 예제를 통해 랜덤한 키쌍을 생성한다.
KeyPair.getPrivate()을 통해 PrivateKey 객체를 얻고, KeyPair.getPublic()을 통해 PublicKey 객체를 얻을 수 있다.
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.ECGenParameterSpec;
import java.security.GeneralSecurityException;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
static{
Security.insertProviderAt(new BouncyCastleProvider(), 0);
}
public static KeyPair generateRandomKeyPair() {
try {
ECGenParameterSpec pairParams = new ECGenParameterSpec("secp256k1");
KeyPairGenerator pairGen = KeyPairGenerator.getInstance("EC", "BC");
pairGen.initialize(pairParams);
return pairGen.generateKeyPair();
} catch (GeneralSecurityException e){
throw new RuntimeException(e);
}
}
KeyPair 생성 - with specific private key
이미 키를 보유한 User는 자신의 키로 PrivateKey와 PublicKey 객체를 생성한다. 개인키 값을 입력으로 genPrivateKeyFromBigInt 함수를 호출하여 PrivateKey 객체를 얻고, 그 PrivateKey 객체를 입력으로 genPublicKeyFromPrivKey 함수를 호출하여 PublicKey 객체를 얻을 수 있다.
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.GeneralSecurityException;
import java.security.Security;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
static{
Security.insertProviderAt(new BouncyCastleProvider(), 0);
}
public static PrivateKey genPrivateKeyFromBigInt(BigInteger D) {
ECParameterSpec paramSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(D, paramSpec);
try {
KeyFactory kf = KeyFactory.getInstance("EC", "BC");
return kf.generatePrivate(privateKeySpec);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
import java.security.PublicKey;
import java.security.KeyFactory;
import java.security.GeneralSecurityException;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
public static PublicKey genPublicKeyFromPrivKey(PrivateKey privKey) {
try {
ECParameterSpec ecps = ECNamedCurveTable.getParameterSpec("secp256k1");
BigInteger D = ((ECPrivateKey)privKey).getD();
ECPoint PK = ecps.getG().multiply(D);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(PK, ecps);
return KeyFactory.getInstance("EC", "BC").generatePublic(publicKeySpec);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
Signing
임의길이 byte-Array와 PrivateKey로 전자서명을 생성한다. 생성된 Signature는 내부적으로 2개의 BigInteger 값으로 관리되며, DER 인코딩 포멧으로 표현된다.
import java.security.Signature;
import java.security.GeneralSecurityException;
public static byte[] sign(byte[] msg, PrivateKey privKey) {
try {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(privKey);
ecdsaSign.update(msg);
return ecdsaSign.sign();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
hashing
Signing 알고리즘은 내부적으로 byte[] msg를 Hash한 값으로 연산된다. 따라서 개발자의 팔자에 따라 msg의 Hash를 구해야 하는 경우가 생길 수 있다. 표준 ECDSA의 경우 SHA-256 Hash 알고리즘을 사용해야하는데, 예제는 다음과 같다.
import java.security.MessageDigest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
static {
Security.insertProviderAt(new BouncyCastleProvider(), 0);
}
public static byte[] digest(byte[] msg) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256", "BC");
md.update(msg);
return md.digest();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
Verification
서명시 사용했던 msg와 PublicKey로 Signature를 검증한다.
import java.security.PublicKey;
import java.security.Signature;
import java.security.GeneralSecurityException;
public static boolean verify(byte[] signed, byte[] sig, PublicKey pubKey) {
try {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initVerify(pubKey);
ecdsaSign.update(signed);
return ecdsaSign.verify(sig);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
Convert between ECPoint and PublicKey
검증키가 ECPoint로 주어진 경우, 다음 예제 프로그램을 통해 PublicKey 객체를 생성할 수 있다.
import java.security.KeyFactory;
import java.security.Security;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
static{
Security.insertProviderAt(new BouncyCastleProvider(), 0);
}
public static PublicKey genPublicKeyFromPoint(ECPoint point) {
try {
ECParameterSpec ecps = ECNamedCurveTable.getParameterSpec("secp256k1");
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(point, ecps);
return KeyFactory.getInstance("EC", "BC").generatePublic(publicKeySpec);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
역으로, PublicKey 객체에서 ECPoint 값을 추출하는 방법은 다음과 같다.
import java.security.PublicKey;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.ECNamedCurveTable;
public static ECPoint publicKeyToECPoint(PublicKey pubKey) {
ECParameterSpec ecps = ECNamedCurveTable.getParameterSpec("secp256k1");
return ecps.getCurve().decodePoint(pubKey.getEncoded());
}
'Applied Cryptography' 카테고리의 다른 글
Blind Signature based RSA (0) | 2020.07.07 |
---|---|
X.509 인증서 (+ Yessign 공인인증서 Sample) (0) | 2020.06.30 |
투표와 전자투표 (0) | 2020.06.19 |
Shamir's Secret Sharing과 Lagrange Interpolation (0) | 2020.06.19 |
전자 서명 알고리즘 (0) | 2020.06.19 |