This is an automated email from the ASF dual-hosted git repository. toulmean pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
The following commit(s) were added to refs/heads/main by this push: new 3257e45 Add methods to encrypt and decrypt with secp256k1 new 2a30e2a Merge pull request #387 from atoulme/encrypt_decrypt 3257e45 is described below commit 3257e45cfa9d5daad2dd9e05308c7cebacdec6c5 Author: Antoine Toulme <anto...@lunar-ocean.com> AuthorDate: Mon Mar 21 22:35:40 2022 -0700 Add methods to encrypt and decrypt with secp256k1 --- .../apache/tuweni/crypto/DecryptionException.java | 23 ++++++++++ .../apache/tuweni/crypto/EncryptionException.java | 23 ++++++++++ .../java/org/apache/tuweni/crypto/SECP256K1.java | 51 ++++++++++++++++++++++ .../org/apache/tuweni/crypto/SECP256K1Test.java | 8 ++++ 4 files changed, 105 insertions(+) diff --git a/crypto/src/main/java/org/apache/tuweni/crypto/DecryptionException.java b/crypto/src/main/java/org/apache/tuweni/crypto/DecryptionException.java new file mode 100644 index 0000000..2c3a685 --- /dev/null +++ b/crypto/src/main/java/org/apache/tuweni/crypto/DecryptionException.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.crypto; + +/** + * Exception thrown when decryption fails. + */ +public class DecryptionException extends RuntimeException { + + public DecryptionException(Throwable t) { + super(t); + } +} diff --git a/crypto/src/main/java/org/apache/tuweni/crypto/EncryptionException.java b/crypto/src/main/java/org/apache/tuweni/crypto/EncryptionException.java new file mode 100644 index 0000000..4f0d398 --- /dev/null +++ b/crypto/src/main/java/org/apache/tuweni/crypto/EncryptionException.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE + * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.apache.tuweni.crypto; + +/** + * Exception thrown when encryption fails. + */ +public class EncryptionException extends RuntimeException { + + public EncryptionException(Throwable t) { + super(t); + } +} diff --git a/crypto/src/main/java/org/apache/tuweni/crypto/SECP256K1.java b/crypto/src/main/java/org/apache/tuweni/crypto/SECP256K1.java index 7e323cc..167bfb9 100644 --- a/crypto/src/main/java/org/apache/tuweni/crypto/SECP256K1.java +++ b/crypto/src/main/java/org/apache/tuweni/crypto/SECP256K1.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkState; import static java.nio.file.StandardOpenOption.READ; import static org.apache.tuweni.crypto.Hash.keccak256; import static org.apache.tuweni.crypto.SECP256K1.Parameters.CURVE; +import static org.apache.tuweni.crypto.SECP256K1.Parameters.PARAMETER_SPEC; import static org.apache.tuweni.io.file.Files.atomicReplace; import org.apache.tuweni.bytes.Bytes; @@ -37,6 +38,7 @@ import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -44,6 +46,7 @@ import java.security.SecureRandom; import java.security.spec.ECGenParameterSpec; import java.util.Arrays; import javax.annotation.Nullable; +import javax.crypto.Cipher; import javax.security.auth.Destroyable; import com.google.common.base.Objects; @@ -59,6 +62,12 @@ import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; import org.bouncycastle.math.ec.ECAlgorithms; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.FixedPointCombMultiplier; @@ -91,6 +100,7 @@ public final class SECP256K1 { // Lazily initialize parameters by using java initialization on demand public static final class Parameters { public static final ECDomainParameters CURVE; + static final ECParameterSpec PARAMETER_SPEC; static final BigInteger CURVE_ORDER; static final BigInteger HALF_CURVE_ORDER; static final KeyPairGenerator KEY_PAIR_GENERATOR; @@ -105,6 +115,11 @@ public final class SECP256K1 { } X9ECParameters params = SECNamedCurves.getByName(CURVE_NAME); CURVE = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + PARAMETER_SPEC = new ECParameterSpec( + params.getCurve(), + CURVE.getG(), + CURVE.getN(), + CURVE.getH()); CURVE_ORDER = CURVE.getN(); HALF_CURVE_ORDER = CURVE_ORDER.shiftRight(1); if (CURVE_ORDER.compareTo(SecP256K1Curve.q) >= 0) { @@ -212,6 +227,42 @@ public final class SECP256K1 { } /** + * Encrypts bytes using a public key. + * @param publicKey the public key for encryption + * @param payload the payload to encrypt + * @return the encrypted data + */ + public static Bytes encrypt(SECP256K1.PublicKey publicKey, Bytes payload) { + try { + ECPoint ecPoint = publicKey.asEcPoint(); + ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, PARAMETER_SPEC); + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + java.security.PublicKey bcKey = keyFactory.generatePublic(keySpec); + + Cipher iesCipher = Cipher.getInstance("ECIES", "BC"); + iesCipher.init(Cipher.ENCRYPT_MODE, bcKey); + byte[] output = iesCipher.doFinal(payload.toArrayUnsafe()); + return Bytes.wrap(output); + } catch(Exception e) { + throw new EncryptionException(e); + } + } + + public static Bytes decrypt(SECP256K1.SecretKey secretKey, Bytes encrypted) { + try { + ECPrivateKeySpec keySpec = new ECPrivateKeySpec(secretKey.bytes().toBigInteger(), PARAMETER_SPEC); + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + java.security.PrivateKey bcKey = keyFactory.generatePrivate(keySpec); + Cipher iesCipher = Cipher.getInstance("ECIES", "BC"); + iesCipher.init(Cipher.DECRYPT_MODE, bcKey); + byte[] output = iesCipher.doFinal(encrypted.toArrayUnsafe()); + return Bytes.wrap(output); + } catch (Exception e) { + throw new DecryptionException(e); + } + } + + /** * Generates an ECDSA signature. * * @param data The data to sign. diff --git a/crypto/src/test/java/org/apache/tuweni/crypto/SECP256K1Test.java b/crypto/src/test/java/org/apache/tuweni/crypto/SECP256K1Test.java index 75f7352..9223910 100644 --- a/crypto/src/test/java/org/apache/tuweni/crypto/SECP256K1Test.java +++ b/crypto/src/test/java/org/apache/tuweni/crypto/SECP256K1Test.java @@ -354,4 +354,12 @@ class SECP256K1Test { Bytes32 otherSharedSecret = SECP256K1.calculateKeyAgreement(otherKP.secretKey(), kp.publicKey()); assertEquals(sharedSecret, otherSharedSecret); } + + @Test + void encryptDecrypt() { + KeyPair kp = SECP256K1.KeyPair.random(); + Bytes encrypted = SECP256K1.encrypt(kp.publicKey(), Bytes.fromHexString("0xdeadbeef")); + Bytes decrypted = SECP256K1.decrypt(kp.secretKey(), encrypted); + assertEquals(Bytes.fromHexString("0xdeadbeef"), decrypted); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@tuweni.apache.org For additional commands, e-mail: commits-h...@tuweni.apache.org