1package org.bouncycastle.cert; 2 3import java.io.OutputStream; 4import java.math.BigInteger; 5import java.util.ArrayList; 6import java.util.List; 7 8import org.bouncycastle.asn1.ASN1Integer; 9import org.bouncycastle.asn1.ASN1ObjectIdentifier; 10import org.bouncycastle.asn1.ASN1Sequence; 11import org.bouncycastle.asn1.x500.X500Name; 12import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 13import org.bouncycastle.asn1.x509.GeneralName; 14import org.bouncycastle.asn1.x509.GeneralNames; 15import org.bouncycastle.asn1.x509.Holder; 16import org.bouncycastle.asn1.x509.IssuerSerial; 17import org.bouncycastle.asn1.x509.ObjectDigestInfo; 18import org.bouncycastle.operator.DigestCalculator; 19import org.bouncycastle.operator.DigestCalculatorProvider; 20import org.bouncycastle.util.Arrays; 21import org.bouncycastle.util.Selector; 22 23/** 24 * The Holder object. 25 * 26 * <pre> 27 * Holder ::= SEQUENCE { 28 * baseCertificateID [0] IssuerSerial OPTIONAL, 29 * -- the issuer and serial number of 30 * -- the holder's Public Key Certificate 31 * entityName [1] GeneralNames OPTIONAL, 32 * -- the name of the claimant or role 33 * objectDigestInfo [2] ObjectDigestInfo OPTIONAL 34 * -- used to directly authenticate the holder, 35 * -- for example, an executable 36 * } 37 * </pre> 38 * <p> 39 * <b>Note:</b> If objectDigestInfo comparisons are to be carried out the static 40 * method setDigestCalculatorProvider <b>must</b> be called once to configure the class 41 * to do the necessary calculations. 42 * </p> 43 */ 44public class AttributeCertificateHolder 45 implements Selector 46{ 47 private static DigestCalculatorProvider digestCalculatorProvider; 48 49 final Holder holder; 50 51 AttributeCertificateHolder(ASN1Sequence seq) 52 { 53 holder = Holder.getInstance(seq); 54 } 55 56 public AttributeCertificateHolder(X500Name issuerName, 57 BigInteger serialNumber) 58 { 59 holder = new Holder(new IssuerSerial( 60 new GeneralNames(new GeneralName(issuerName)), 61 new ASN1Integer(serialNumber))); 62 } 63 64 public AttributeCertificateHolder(X509CertificateHolder cert) 65 { 66 holder = new Holder(new IssuerSerial(generateGeneralNames(cert.getIssuer()), 67 new ASN1Integer(cert.getSerialNumber()))); 68 } 69 70 public AttributeCertificateHolder(X500Name principal) 71 { 72 holder = new Holder(generateGeneralNames(principal)); 73 } 74 75 /** 76 * Constructs a holder for v2 attribute certificates with a hash value for 77 * some type of object. 78 * <p> 79 * <code>digestedObjectType</code> can be one of the following: 80 * <ul> 81 * <li>0 - publicKey - A hash of the public key of the holder must be 82 * passed. 83 * <li>1 - publicKeyCert - A hash of the public key certificate of the 84 * holder must be passed. 85 * <li>2 - otherObjectDigest - A hash of some other object type must be 86 * passed. <code>otherObjectTypeID</code> must not be empty. 87 * </ul> 88 * <p> 89 * This cannot be used if a v1 attribute certificate is used. 90 * 91 * @param digestedObjectType The digest object type. 92 * @param digestAlgorithm The algorithm identifier for the hash. 93 * @param otherObjectTypeID The object type ID if 94 * <code>digestedObjectType</code> is 95 * <code>otherObjectDigest</code>. 96 * @param objectDigest The hash value. 97 */ 98 public AttributeCertificateHolder(int digestedObjectType, 99 ASN1ObjectIdentifier digestAlgorithm, ASN1ObjectIdentifier otherObjectTypeID, byte[] objectDigest) 100 { 101 holder = new Holder(new ObjectDigestInfo(digestedObjectType, 102 otherObjectTypeID, new AlgorithmIdentifier(digestAlgorithm), Arrays 103 .clone(objectDigest))); 104 } 105 106 /** 107 * Returns the digest object type if an object digest info is used. 108 * <p> 109 * <ul> 110 * <li>0 - publicKey - A hash of the public key of the holder must be 111 * passed. 112 * <li>1 - publicKeyCert - A hash of the public key certificate of the 113 * holder must be passed. 114 * <li>2 - otherObjectDigest - A hash of some other object type must be 115 * passed. <code>otherObjectTypeID</code> must not be empty. 116 * </ul> 117 * 118 * @return The digest object type or -1 if no object digest info is set. 119 */ 120 public int getDigestedObjectType() 121 { 122 if (holder.getObjectDigestInfo() != null) 123 { 124 return holder.getObjectDigestInfo().getDigestedObjectType() 125 .getValue().intValue(); 126 } 127 return -1; 128 } 129 130 /** 131 * Returns algorithm identifier for the digest used if ObjectDigestInfo is present. 132 * 133 * @return digest AlgorithmIdentifier or <code>null</code> if ObjectDigestInfo is absent. 134 */ 135 public AlgorithmIdentifier getDigestAlgorithm() 136 { 137 if (holder.getObjectDigestInfo() != null) 138 { 139 return holder.getObjectDigestInfo().getDigestAlgorithm(); 140 } 141 return null; 142 } 143 144 /** 145 * Returns the hash if an object digest info is used. 146 * 147 * @return The hash or <code>null</code> if ObjectDigestInfo is absent. 148 */ 149 public byte[] getObjectDigest() 150 { 151 if (holder.getObjectDigestInfo() != null) 152 { 153 return holder.getObjectDigestInfo().getObjectDigest().getBytes(); 154 } 155 return null; 156 } 157 158 /** 159 * Returns the digest algorithm ID if an object digest info is used. 160 * 161 * @return The digest algorithm ID or <code>null</code> if no object 162 * digest info is set. 163 */ 164 public ASN1ObjectIdentifier getOtherObjectTypeID() 165 { 166 if (holder.getObjectDigestInfo() != null) 167 { 168 new ASN1ObjectIdentifier(holder.getObjectDigestInfo().getOtherObjectTypeID().getId()); 169 } 170 return null; 171 } 172 173 private GeneralNames generateGeneralNames(X500Name principal) 174 { 175 return new GeneralNames(new GeneralName(principal)); 176 } 177 178 private boolean matchesDN(X500Name subject, GeneralNames targets) 179 { 180 GeneralName[] names = targets.getNames(); 181 182 for (int i = 0; i != names.length; i++) 183 { 184 GeneralName gn = names[i]; 185 186 if (gn.getTagNo() == GeneralName.directoryName) 187 { 188 if (X500Name.getInstance(gn.getName()).equals(subject)) 189 { 190 return true; 191 } 192 } 193 } 194 195 return false; 196 } 197 198 private X500Name[] getPrincipals(GeneralName[] names) 199 { 200 List l = new ArrayList(names.length); 201 202 for (int i = 0; i != names.length; i++) 203 { 204 if (names[i].getTagNo() == GeneralName.directoryName) 205 { 206 l.add(X500Name.getInstance(names[i].getName())); 207 } 208 } 209 210 return (X500Name[])l.toArray(new X500Name[l.size()]); 211 } 212 213 /** 214 * Return any principal objects inside the attribute certificate holder 215 * entity names field. 216 * 217 * @return an array of Principal objects (usually X500Principal), null if no 218 * entity names field is set. 219 */ 220 public X500Name[] getEntityNames() 221 { 222 if (holder.getEntityName() != null) 223 { 224 return getPrincipals(holder.getEntityName().getNames()); 225 } 226 227 return null; 228 } 229 230 /** 231 * Return the principals associated with the issuer attached to this holder 232 * 233 * @return an array of principals, null if no BaseCertificateID is set. 234 */ 235 public X500Name[] getIssuer() 236 { 237 if (holder.getBaseCertificateID() != null) 238 { 239 return getPrincipals(holder.getBaseCertificateID().getIssuer().getNames()); 240 } 241 242 return null; 243 } 244 245 /** 246 * Return the serial number associated with the issuer attached to this 247 * holder. 248 * 249 * @return the certificate serial number, null if no BaseCertificateID is 250 * set. 251 */ 252 public BigInteger getSerialNumber() 253 { 254 if (holder.getBaseCertificateID() != null) 255 { 256 return holder.getBaseCertificateID().getSerial().getValue(); 257 } 258 259 return null; 260 } 261 262 public Object clone() 263 { 264 return new AttributeCertificateHolder((ASN1Sequence)holder.toASN1Object()); 265 } 266 267 public boolean match(Object obj) 268 { 269 if (!(obj instanceof X509CertificateHolder)) 270 { 271 return false; 272 } 273 274 X509CertificateHolder x509Cert = (X509CertificateHolder)obj; 275 276 if (holder.getBaseCertificateID() != null) 277 { 278 return holder.getBaseCertificateID().getSerial().getValue().equals(x509Cert.getSerialNumber()) 279 && matchesDN(x509Cert.getIssuer(), holder.getBaseCertificateID().getIssuer()); 280 } 281 282 if (holder.getEntityName() != null) 283 { 284 if (matchesDN(x509Cert.getSubject(), 285 holder.getEntityName())) 286 { 287 return true; 288 } 289 } 290 291 if (holder.getObjectDigestInfo() != null) 292 { 293 try 294 { 295 DigestCalculator digCalc = digestCalculatorProvider.get(holder.getObjectDigestInfo().getDigestAlgorithm()); 296 OutputStream digOut = digCalc.getOutputStream(); 297 298 switch (getDigestedObjectType()) 299 { 300 case ObjectDigestInfo.publicKey: 301 // TODO: DSA Dss-parms 302 digOut.write(x509Cert.getSubjectPublicKeyInfo().getEncoded()); 303 break; 304 case ObjectDigestInfo.publicKeyCert: 305 digOut.write(x509Cert.getEncoded()); 306 break; 307 } 308 309 digOut.close(); 310 311 if (!Arrays.areEqual(digCalc.getDigest(), getObjectDigest())) 312 { 313 return false; 314 } 315 } 316 catch (Exception e) 317 { 318 return false; 319 } 320 } 321 322 return false; 323 } 324 325 public boolean equals(Object obj) 326 { 327 if (obj == this) 328 { 329 return true; 330 } 331 332 if (!(obj instanceof AttributeCertificateHolder)) 333 { 334 return false; 335 } 336 337 AttributeCertificateHolder other = (AttributeCertificateHolder)obj; 338 339 return this.holder.equals(other.holder); 340 } 341 342 public int hashCode() 343 { 344 return this.holder.hashCode(); 345 } 346 347 /** 348 * Set a digest calculator provider to be used if matches are attempted using 349 * ObjectDigestInfo, 350 * 351 * @param digCalcProvider a provider of digest calculators. 352 */ 353 public static void setDigestCalculatorProvider(DigestCalculatorProvider digCalcProvider) 354 { 355 digestCalculatorProvider = digCalcProvider; 356 } 357} 358