Jabit/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java

156 lines
5.9 KiB
Java

/*
* Copyright 2015 Christian Basler
*
* Licensed 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 ch.dissem.bitmessage.cryptography.bc;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.ApplicationException;
import ch.dissem.bitmessage.ports.AbstractCryptography;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
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.ECPoint;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.KeySpec;
import java.util.Arrays;
/**
* As Spongycastle can't be used on the Oracle JVM, and Bouncycastle doesn't work properly on Android (thanks, Google),
* this is the Bouncycastle implementation.
*/
public class BouncyCryptography extends AbstractCryptography {
private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1");
private static final String ALGORITHM_ECDSA = "ECDSA";
public BouncyCryptography() {
super(new BouncyCastleProvider());
}
@Override
public byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector) {
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()),
new PKCS7Padding()
);
CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector);
cipher.init(encrypt, params);
byte[] buffer = new byte[cipher.getOutputSize(data.length)];
int length = cipher.processBytes(data, 0, data.length, buffer, 0);
try {
length += cipher.doFinal(buffer, length);
} catch (InvalidCipherTextException e) {
throw new IllegalArgumentException(e);
}
if (length < buffer.length) {
return Arrays.copyOfRange(buffer, 0, length);
}
return buffer;
}
@Override
public byte[] createPublicKey(byte[] privateKey) {
return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize().getEncoded(false);
}
private ECPoint keyToPoint(byte[] publicKey) {
BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33));
BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65));
return EC_CURVE_PARAMETERS.getCurve().createPoint(x, y);
}
@Override
public boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey) {
try {
ECParameterSpec spec = new ECParameterSpec(
EC_CURVE_PARAMETERS.getCurve(),
EC_CURVE_PARAMETERS.getG(),
EC_CURVE_PARAMETERS.getN(),
EC_CURVE_PARAMETERS.getH(),
EC_CURVE_PARAMETERS.getSeed()
);
ECPoint Q = keyToPoint(pubkey.getSigningKey());
KeySpec keySpec = new ECPublicKeySpec(Q, spec);
PublicKey publicKey = KeyFactory.getInstance(ALGORITHM_ECDSA, provider).generatePublic(keySpec);
Signature sig = Signature.getInstance(ALGORITHM_ECDSA, provider);
sig.initVerify(publicKey);
sig.update(data);
return sig.verify(signature);
} catch (GeneralSecurityException e) {
throw new ApplicationException(e);
}
}
@Override
public byte[] getSignature(byte[] data, PrivateKey privateKey) {
try {
ECParameterSpec spec = new ECParameterSpec(
EC_CURVE_PARAMETERS.getCurve(),
EC_CURVE_PARAMETERS.getG(),
EC_CURVE_PARAMETERS.getN(),
EC_CURVE_PARAMETERS.getH(),
EC_CURVE_PARAMETERS.getSeed()
);
BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey());
KeySpec keySpec = new ECPrivateKeySpec(d, spec);
java.security.PrivateKey privKey = KeyFactory.getInstance(ALGORITHM_ECDSA, provider)
.generatePrivate(keySpec);
Signature sig = Signature.getInstance(ALGORITHM_ECDSA, provider);
sig.initSign(privKey);
sig.update(data);
return sig.sign();
} catch (GeneralSecurityException e) {
throw new ApplicationException(e);
}
}
@Override
public byte[] multiply(byte[] K, byte[] r) {
return keyToPoint(K).multiply(keyToBigInt(r)).normalize().getEncoded(false);
}
@Override
public byte[] createPoint(byte[] x, byte[] y) {
return EC_CURVE_PARAMETERS.getCurve().createPoint(
new BigInteger(1, x),
new BigInteger(1, y)
).getEncoded(false);
}
}