Skip to content

Commit f633664

Browse files
committed
Adding Chris Black's patch from CODEC-40 adding BigInteger support to Base64. Still needs edge case testing
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/codec/trunk@634904 13f79535-47bb-0310-9956-ffa450edef68
1 parent 0752e9a commit f633664

2 files changed

Lines changed: 115 additions & 1 deletion

File tree

src/java/org/apache/commons/codec/binary/Base64.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.apache.commons.codec.DecoderException;
2323
import org.apache.commons.codec.EncoderException;
2424

25+
import java.math.BigInteger;
26+
2527
/**
2628
* Provides Base64 encoding and decoding as defined by RFC 2045.
2729
*
@@ -532,4 +534,67 @@ public byte[] encode(byte[] pArray) {
532534
return encodeBase64(pArray, false);
533535
}
534536

537+
// Implementation of integer encoding used for crypto
538+
/**
539+
* Decode a byte64-encoded integer according to crypto
540+
* standards such as W3C's XML-Signature
541+
*
542+
* @param pArray a byte array containing base64 character data
543+
* @return A BigInteger
544+
*/
545+
public static BigInteger decodeInteger(byte[] pArray) {
546+
return new BigInteger(1, decodeBase64(pArray));
547+
}
548+
549+
/**
550+
* Encode to a byte64-encoded integer according to crypto
551+
* standards such as W3C's XML-Signature
552+
*
553+
* @param bigInt a BigInteger
554+
* @return A byte array containing base64 character data
555+
* @throws NullPointerException if null is passed in
556+
*/
557+
public static byte[] encodeInteger(BigInteger bigInt) {
558+
if(bigInt == null) {
559+
throw new NullPointerException("encodeInteger called with null parameter");
560+
}
561+
562+
return encodeBase64(toIntegerBytes(bigInt), false);
563+
}
564+
565+
/**
566+
* Returns a byte-array representation of a <code>BigInteger</code>
567+
* without sign bit.
568+
*
569+
* @param bigInt <code>BigInteger</code> to be converted
570+
* @return a byte array representation of the BigInteger parameter
571+
*/
572+
static byte[] toIntegerBytes(BigInteger bigInt) {
573+
int bitlen = bigInt.bitLength();
574+
// round bitlen
575+
bitlen = ((bitlen + 7) >> 3) << 3;
576+
byte[] bigBytes = bigInt.toByteArray();
577+
578+
if(((bigInt.bitLength() % 8) != 0)
579+
&& (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
580+
return bigBytes;
581+
}
582+
583+
// set up params for copying everything but sign bit
584+
int startSrc = 0;
585+
int len = bigBytes.length;
586+
587+
// if bigInt is exactly byte-aligned, just skip signbit in copy
588+
if((bigInt.bitLength() % 8) == 0) {
589+
startSrc = 1;
590+
len--;
591+
}
592+
593+
int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
594+
byte[] resizedBytes = new byte[bitlen / 8];
595+
596+
System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
597+
598+
return resizedBytes;
599+
}
535600
}

src/test/org/apache/commons/codec/binary/Base64Test.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import java.util.Arrays;
2222
import java.util.Random;
23-
23+
import java.math.BigInteger;
2424
import junit.framework.TestCase;
2525

2626
/**
@@ -638,6 +638,55 @@ public void testDiscardWhitespace() throws Exception {
638638
destFromNoWS.equals( orig ) );
639639
}
640640

641+
public void testCodeInteger1() {
642+
String encodedInt1 = "li7dzDacuo67Jg7mtqEm2TRuOMU=";
643+
BigInteger bigInt1 = new BigInteger("85739377120809420210425962799" +
644+
"0318636601332086981");
645+
646+
assertEquals(encodedInt1, new String(Base64.encodeInteger(bigInt1)));
647+
assertEquals(bigInt1, Base64.decodeInteger(encodedInt1.getBytes()));
648+
}
649+
650+
public void testCodeInteger2() {
651+
String encodedInt2 = "9B5ypLY9pMOmtxCeTDHgwdNFeGs=";
652+
BigInteger bigInt2 = new BigInteger("13936727572861167254666467268" +
653+
"91466679477132949611");
654+
655+
assertEquals(encodedInt2, new String(Base64.encodeInteger(bigInt2)));
656+
assertEquals(bigInt2, Base64.decodeInteger(encodedInt2.getBytes()));
657+
}
658+
659+
public void testCodeInteger3() {
660+
String encodedInt3 = "FKIhdgaG5LGKiEtF1vHy4f3y700zaD6QwDS3IrNVGzNp2" +
661+
"rY+1LFWTK6D44AyiC1n8uWz1itkYMZF0/aKDK0Yjg==";
662+
BigInteger bigInt3 = new BigInteger("10806548154093873461951748545" +
663+
"1196989136416448805819079363524309897749044958112417136240557" +
664+
"4495062430572478766856090958495998158114332651671116876320938126");
665+
666+
assertEquals(encodedInt3, new String(Base64.encodeInteger(bigInt3)));
667+
assertEquals(bigInt3, Base64.decodeInteger(encodedInt3.getBytes()));
668+
}
669+
670+
public void testCodeInteger4() {
671+
String encodedInt4 = "ctA8YGxrtngg/zKVvqEOefnwmViFztcnPBYPlJsvh6yKI" +
672+
"4iDm68fnp4Mi3RrJ6bZAygFrUIQLxLjV+OJtgJAEto0xAs+Mehuq1DkSFEpP3o" +
673+
"DzCTOsrOiS1DwQe4oIb7zVk/9l7aPtJMHW0LVlMdwZNFNNJoqMcT2ZfCPrfvYv" +
674+
"Q0=";
675+
BigInteger bigInt4 = new BigInteger("80624726256040348115552042320" +
676+
"6968135001872753709424419772586693950232350200555646471175944" +
677+
"519297087885987040810778908507262272892702303774422853675597" +
678+
"748008534040890923814202286633163248086055216976551456088015" +
679+
"338880713818192088877057717530169381044092839402438015097654" +
680+
"53542091716518238707344493641683483917");
681+
682+
assertEquals(encodedInt4, new String(Base64.encodeInteger(bigInt4)));
683+
assertEquals(bigInt4, Base64.decodeInteger(encodedInt4.getBytes()));
684+
}
685+
686+
public void testCodeIntegerEdgeCases() {
687+
// TODO
688+
}
689+
641690
// -------------------------------------------------------- Private Methods
642691

643692
private String toString(byte[] data) {

0 commit comments

Comments
 (0)