package org.ripple.power; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import org.ripple.power.RippleSchemas.BinaryFormatField; import org.ripple.bouncycastle.asn1.ASN1InputStream; import org.ripple.bouncycastle.asn1.DERInteger; import org.ripple.bouncycastle.asn1.DERSequenceGenerator; import org.ripple.bouncycastle.asn1.DLSequence; import org.ripple.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.ripple.bouncycastle.crypto.params.ECPublicKeyParameters; import org.ripple.bouncycastle.crypto.signers.ECDSASigner; import org.ripple.bouncycastle.math.ec.ECPoint; public class RippleSigner { RipplePrivateKey privateKey; public RippleSigner(RipplePrivateKey privateKey) { this.privateKey = privateKey; } public RippleObject sign(RippleObject serObjToSign) throws Exception { if (serObjToSign.getField(BinaryFormatField.TxnSignature) != null) { throw new Exception("Object already signed"); } RippleObject signedRBO = new RippleObject(serObjToSign); signedRBO.putField(BinaryFormatField.SigningPubKey, privateKey .getPublicKey().getPublicPoint().getEncoded()); byte[] hashOfRBOBytes = signedRBO.generateHashFromBinaryObject(); ECDSASignature signature = signHash(hashOfRBOBytes); signedRBO.putField(BinaryFormatField.TxnSignature, signature.encodeToDER()); return signedRBO; } private ECDSASignature signHash(byte[] hashOfBytes) throws Exception { if (hashOfBytes.length != 32) { throw new RuntimeException("can sign only a hash of 32 bytes"); } ECDSASigner signer = new ECDSASigner(); ECPrivateKeyParameters privKey = privateKey.getECPrivateKey(); signer.init(true, privKey); BigInteger[] RandS = signer.generateSignature(hashOfBytes); return new ECDSASignature(RandS[0], RandS[1], privateKey.getPublicKey() .getPublicPoint()); } public boolean isSignatureVerified(RippleObject serObj) { try { byte[] signatureBytes = (byte[]) serObj .getField(BinaryFormatField.TxnSignature); if (signatureBytes == null) { throw new RuntimeException("The specified has no signature"); } byte[] signingPubKeyBytes = (byte[]) serObj .getField(BinaryFormatField.SigningPubKey); if (signingPubKeyBytes == null) { throw new RuntimeException( "The specified has no public key associated to the signature"); } RippleObject unsignedRBO = serObj.getUnsignedCopy(); byte[] hashToVerify = unsignedRBO.generateHashFromBinaryObject(); ECDSASigner signer = new ECDSASigner(); ECDSASignature signature = new ECDSASignature(signatureBytes, signingPubKeyBytes); signer.init(false, new ECPublicKeyParameters( signature.publicSigningKey, RippleGenerator.SECP256K1_PARAMS)); return signer.verifySignature(hashToVerify, signature.r, signature.s); } catch (IOException e) { throw new RuntimeException(e); } } public static class ECDSASignature { public BigInteger r, s; private ECPoint publicSigningKey; public ECDSASignature(BigInteger r, BigInteger s, ECPoint publicSigningKey) { this.r = r; this.s = s; this.publicSigningKey = publicSigningKey; } public ECDSASignature(byte[] signatureDEREncodedBytes, byte[] signingPubKey) throws IOException { publicSigningKey = RippleGenerator.SECP256K1_PARAMS.getCurve() .decodePoint(signingPubKey); ASN1InputStream decoder = new ASN1InputStream( signatureDEREncodedBytes); DLSequence seq = (DLSequence) decoder.readObject(); DERInteger r = (DERInteger) seq.getObjectAt(0); DERInteger s = (DERInteger) seq.getObjectAt(1); this.r = r.getPositiveValue(); this.s = s.getPositiveValue(); decoder.close(); } public byte[] encodeToDER() { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(72); DERSequenceGenerator seq = new DERSequenceGenerator(bos); seq.addObject(new DERInteger(r)); seq.addObject(new DERInteger(s)); seq.close(); return bos.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } } }