mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-25 17:46:44 +00:00
add support for x25519 and secp256r1 keys
This commit is contained in:
parent
35bebe13bc
commit
dba1a9a2be
3 changed files with 186 additions and 0 deletions
|
@ -4,6 +4,8 @@ import com.sparrowwallet.drongo.crypto.*;
|
|||
import com.sparrowwallet.drongo.protocol.ProtocolException;
|
||||
import com.sparrowwallet.drongo.protocol.Ripemd160;
|
||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.crypto.digests.SHA512Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
|
@ -15,6 +17,9 @@ import java.math.BigInteger;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.*;
|
||||
|
||||
public class Utils {
|
||||
|
@ -338,6 +343,21 @@ public class Utils {
|
|||
return Sha256Hash.hash(buffer.array());
|
||||
}
|
||||
|
||||
public static byte[] getRawKeyBytesFromPKCS8(PrivateKey pkcs8Key) {
|
||||
try {
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Key.getEncoded());
|
||||
PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(keySpec.getEncoded());
|
||||
return privateKeyInfo.parsePrivateKey().toASN1Primitive().getEncoded();
|
||||
} catch(IOException e) {
|
||||
throw new IllegalArgumentException("Error parsing private key", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getRawKeyBytesFromX509(PublicKey x509Key) {
|
||||
SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(x509Key.getEncoded());
|
||||
return spki.getPublicKeyData().getBytes();
|
||||
}
|
||||
|
||||
public static class LexicographicByteArrayComparator implements Comparator<byte[]> {
|
||||
@Override
|
||||
public int compare(byte[] left, byte[] right) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package com.sparrowwallet.drongo.crypto;
|
||||
|
||||
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.signers.ECDSASigner;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Secp256r1Key {
|
||||
private static final X9ECParameters CURVE_PARAMS = ECNamedCurveTable.getByName("P-256");
|
||||
|
||||
public static final ECDomainParameters CURVE;
|
||||
|
||||
private final ECPoint point;
|
||||
|
||||
static {
|
||||
CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), CURVE_PARAMS.getH());
|
||||
}
|
||||
|
||||
public Secp256r1Key(byte[] publicKeyBytes) {
|
||||
this.point = CURVE.getCurve().decodePoint(publicKeyBytes);
|
||||
}
|
||||
|
||||
public boolean verify(byte[] challenge, byte[] challengeSignature) {
|
||||
ECDSASigner signer = new ECDSASigner();
|
||||
signer.init(false, new ECPublicKeyParameters(point, CURVE));
|
||||
|
||||
int halfLength = challengeSignature.length / 2;
|
||||
byte[] r = new byte[halfLength];
|
||||
byte[] s = new byte[halfLength];
|
||||
System.arraycopy(challengeSignature, 0, r, 0, halfLength);
|
||||
System.arraycopy(challengeSignature, halfLength, s, 0, halfLength);
|
||||
|
||||
return signer.verifySignature(challenge, new BigInteger(1, r), new BigInteger(1, s));
|
||||
}
|
||||
}
|
127
src/main/java/com/sparrowwallet/drongo/crypto/X25519Key.java
Normal file
127
src/main/java/com/sparrowwallet/drongo/crypto/X25519Key.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
package com.sparrowwallet.drongo.crypto;
|
||||
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.security.interfaces.XECPrivateKey;
|
||||
import java.security.interfaces.XECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.Optional;
|
||||
|
||||
public class X25519Key {
|
||||
private KeyPair keyPair;
|
||||
private final AlgorithmParameterSpec ecSpec;
|
||||
|
||||
public X25519Key() {
|
||||
try {
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("X25519");
|
||||
this.keyPair = keyPairGenerator.generateKeyPair();
|
||||
this.ecSpec = keyPairGenerator.generateKeyPair().getPrivate().getParams();
|
||||
} catch(NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public X25519Key(byte[] priv) {
|
||||
this();
|
||||
|
||||
X25519PrivateKeyParameters privateKeyParams = new X25519PrivateKeyParameters(priv, 0);
|
||||
X25519PublicKeyParameters publicKeyParams = privateKeyParams.generatePublicKey();
|
||||
|
||||
PrivateKey privateKey = new BouncyCastlePrivateKey(privateKeyParams);
|
||||
PublicKey publicKey = new BouncyCastlePublicKey(publicKeyParams);
|
||||
this.keyPair = new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair() {
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public byte[] getRawPrivateKeyBytes() {
|
||||
return Utils.getRawKeyBytesFromPKCS8(keyPair.getPrivate());
|
||||
}
|
||||
|
||||
public byte[] getRawPublicKeyBytes() {
|
||||
return Utils.getRawKeyBytesFromX509(keyPair.getPublic());
|
||||
}
|
||||
|
||||
public class BouncyCastlePrivateKey implements XECPrivateKey {
|
||||
private final X25519PrivateKeyParameters privateKeyParams;
|
||||
|
||||
BouncyCastlePrivateKey(X25519PrivateKeyParameters privateKeyParams) {
|
||||
this.privateKeyParams = privateKeyParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "X25519";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "RAW";
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return privateKeyParams.getEncoded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<byte[]> getScalar() {
|
||||
return Optional.of(getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlgorithmParameterSpec getParams() {
|
||||
return ecSpec;
|
||||
}
|
||||
}
|
||||
|
||||
public class BouncyCastlePublicKey implements XECPublicKey {
|
||||
private final X25519PublicKeyParameters publicKeyParams;
|
||||
|
||||
BouncyCastlePublicKey(X25519PublicKeyParameters publicKeyParams) {
|
||||
this.publicKeyParams = publicKeyParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "X25519";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "X.509";
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
try {
|
||||
ASN1ObjectIdentifier algOid = new ASN1ObjectIdentifier("1.3.101.110");
|
||||
AlgorithmIdentifier algId = new AlgorithmIdentifier(algOid);
|
||||
SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo(algId, publicKeyParams.getEncoded());
|
||||
return spki.getEncoded();
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getU() {
|
||||
return new BigInteger(1, publicKeyParams.getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlgorithmParameterSpec getParams() {
|
||||
return ecSpec;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue