package com.ripple.crypto.ecdsa; 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 java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; public class ECDSASignature { /** The two components of the signature. */ public BigInteger r, s; /** Constructs a signature with the given components. */ public ECDSASignature(BigInteger r, BigInteger s) { this.r = r; this.s = s; } /** * DER is an international standard for serializing data structures which is widely used in cryptography. * It's somewhat like protocol buffers but less convenient. This method returns a standard DER encoding * of the signature, as recognized by OpenSSL and other libraries. */ public byte[] encodeToDER() { try { return derByteStream().toByteArray(); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } public static ECDSASignature decodeFromDER(byte[] bytes) { try { ASN1InputStream decoder = new ASN1InputStream(bytes); DLSequence seq = (DLSequence) decoder.readObject(); DERInteger r, s; try { r = (DERInteger) seq.getObjectAt(0); s = (DERInteger) seq.getObjectAt(1); } catch (ClassCastException e) { return null; } decoder.close(); // OpenSSL deviates from the DER spec by interpreting these values as unsigned, though they should not be // Thus, we always use the positive versions. See: http://r6.ca/blog/20111119T211504Z.html return new ECDSASignature(r.getPositiveValue(), s.getPositiveValue()); } catch (IOException e) { throw new RuntimeException(e); } } protected ByteArrayOutputStream derByteStream() throws IOException { // Usually 70-72 bytes. ByteArrayOutputStream bos = new ByteArrayOutputStream(72); DERSequenceGenerator seq = new DERSequenceGenerator(bos); seq.addObject(new DERInteger(r)); seq.addObject(new DERInteger(s)); seq.close(); return bos; } }