1 package org.bouncycastle.asn1; 2 3 import java.io.IOException; 4 5 /** 6 * A DER encoded SET object 7 * <p> 8 * For X.690 syntax rules, see {@link ASN1Set}. 9 * </p><p> 10 * For short: Constructing this form does sort the supplied elements, 11 * and the sorting happens also before serialization (if necesssary). 12 * This is different from the way {@link BERSet},{@link DLSet} does things. 13 * </p> 14 */ 15 public class DERSet 16 extends ASN1Set 17 { convert(ASN1Set set)18 public static DERSet convert(ASN1Set set) 19 { 20 return (DERSet)set.toDERObject(); 21 } 22 23 private int bodyLength = -1; 24 25 /** 26 * create an empty set 27 */ DERSet()28 public DERSet() 29 { 30 } 31 32 /** 33 * create a set containing one object 34 * @param element the object to go in the set 35 */ DERSet(ASN1Encodable element)36 public DERSet(ASN1Encodable element) 37 { 38 super(element); 39 } 40 41 /** 42 * create a set containing a vector of objects. 43 * @param elementVector the vector of objects to make up the set. 44 */ DERSet(ASN1EncodableVector elementVector)45 public DERSet(ASN1EncodableVector elementVector) 46 { 47 super(elementVector, true); 48 } 49 50 /** 51 * create a set containing an array of objects. 52 * @param elements the array of objects to make up the set. 53 */ DERSet(ASN1Encodable[] elements)54 public DERSet(ASN1Encodable[] elements) 55 { 56 super(elements, true); 57 } 58 DERSet(boolean isSorted, ASN1Encodable[] elements)59 DERSet(boolean isSorted, ASN1Encodable[] elements) 60 { 61 super(checkSorted(isSorted), elements); 62 } 63 getBodyLength()64 private int getBodyLength() throws IOException 65 { 66 if (bodyLength < 0) 67 { 68 int count = elements.length; 69 int totalLength = 0; 70 71 for (int i = 0; i < count; ++i) 72 { 73 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject(); 74 totalLength += derObject.encodedLength(); 75 } 76 77 this.bodyLength = totalLength; 78 } 79 80 return bodyLength; 81 } 82 encodedLength()83 int encodedLength() throws IOException 84 { 85 int length = getBodyLength(); 86 87 return 1 + StreamUtil.calculateBodyLength(length) + length; 88 } 89 90 /* 91 * A note on the implementation: 92 * <p> 93 * As DER requires the constructed, definite-length model to 94 * be used for structured types, this varies slightly from the 95 * ASN.1 descriptions given. Rather than just outputting SET, 96 * we also have to specify CONSTRUCTED, and the objects length. 97 */ encode(ASN1OutputStream out, boolean withTag)98 void encode(ASN1OutputStream out, boolean withTag) throws IOException 99 { 100 if (withTag) 101 { 102 out.write(BERTags.SET | BERTags.CONSTRUCTED); 103 } 104 105 DEROutputStream derOut = out.getDERSubStream(); 106 107 int count = elements.length; 108 if (bodyLength >= 0 || count > 16) 109 { 110 out.writeLength(getBodyLength()); 111 112 for (int i = 0; i < count; ++i) 113 { 114 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject(); 115 derObject.encode(derOut, true); 116 } 117 } 118 else 119 { 120 int totalLength = 0; 121 122 ASN1Primitive[] derObjects = new ASN1Primitive[count]; 123 for (int i = 0; i < count; ++i) 124 { 125 ASN1Primitive derObject = elements[i].toASN1Primitive().toDERObject(); 126 derObjects[i] = derObject; 127 totalLength += derObject.encodedLength(); 128 } 129 130 this.bodyLength = totalLength; 131 out.writeLength(totalLength); 132 133 for (int i = 0; i < count; ++i) 134 { 135 derObjects[i].encode(derOut, true); 136 } 137 } 138 } 139 toDERObject()140 ASN1Primitive toDERObject() 141 { 142 return isSorted ? this : super.toDERObject(); 143 } 144 toDLObject()145 ASN1Primitive toDLObject() 146 { 147 return this; 148 } 149 checkSorted(boolean isSorted)150 private static boolean checkSorted(boolean isSorted) 151 { 152 if (!isSorted) 153 { 154 throw new IllegalStateException("DERSet elements should always be in sorted order"); 155 } 156 return isSorted; 157 } 158 } 159