1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt; 18 19 import org.apache.harmony.security.utils.AlgNameMapper; 20 import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException; 21 import java.io.ByteArrayInputStream; 22 import java.io.ByteArrayOutputStream; 23 import java.io.InputStream; 24 import java.math.BigInteger; 25 import java.security.InvalidKeyException; 26 import java.security.NoSuchAlgorithmException; 27 import java.security.NoSuchProviderException; 28 import java.security.Principal; 29 import java.security.PublicKey; 30 import java.security.Signature; 31 import java.security.SignatureException; 32 import java.security.cert.CRLException; 33 import java.security.cert.Certificate; 34 import java.security.cert.X509CRL; 35 import java.security.cert.X509CRLEntry; 36 import java.security.cert.X509Certificate; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Calendar; 40 import java.util.Date; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Set; 44 import java.util.TimeZone; 45 import javax.security.auth.x500.X500Principal; 46 47 public class OpenSSLX509CRL extends X509CRL { 48 private final long mContext; 49 50 private OpenSSLX509CRL(long ctx) { 51 mContext = ctx; 52 } 53 54 public static OpenSSLX509CRL fromX509DerInputStream(InputStream is) throws ParsingException { 55 final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is); 56 57 try { 58 final long crlCtx = NativeCrypto.d2i_X509_CRL_bio(bis.getBioContext()); 59 if (crlCtx == 0) { 60 return null; 61 } 62 return new OpenSSLX509CRL(crlCtx); 63 } catch (Exception e) { 64 throw new ParsingException(e); 65 } finally { 66 bis.release(); 67 } 68 } 69 70 public static List<OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is) 71 throws ParsingException { 72 OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is); 73 74 final long[] certRefs; 75 try { 76 certRefs = NativeCrypto.d2i_PKCS7_bio(bis.getBioContext(), NativeCrypto.PKCS7_CRLS); 77 } catch (Exception e) { 78 throw new ParsingException(e); 79 } finally { 80 bis.release(); 81 } 82 83 final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length); 84 for (int i = 0; i < certRefs.length; i++) { 85 if (certRefs[i] == 0) { 86 continue; 87 } 88 certs.add(new OpenSSLX509CRL(certRefs[i])); 89 } 90 return certs; 91 } 92 93 public static OpenSSLX509CRL fromX509PemInputStream(InputStream is) throws ParsingException { 94 final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is); 95 96 try { 97 final long crlCtx = NativeCrypto.PEM_read_bio_X509_CRL(bis.getBioContext()); 98 if (crlCtx == 0) { 99 return null; 100 } 101 return new OpenSSLX509CRL(crlCtx); 102 } catch (Exception e) { 103 throw new ParsingException(e); 104 } finally { 105 bis.release(); 106 } 107 } 108 109 public static List<OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is) 110 throws ParsingException { 111 OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is); 112 113 final long[] certRefs; 114 try { 115 certRefs = NativeCrypto.PEM_read_bio_PKCS7(bis.getBioContext(), 116 NativeCrypto.PKCS7_CRLS); 117 } catch (Exception e) { 118 throw new ParsingException(e); 119 } finally { 120 bis.release(); 121 } 122 123 final List<OpenSSLX509CRL> certs = new ArrayList<OpenSSLX509CRL>(certRefs.length); 124 for (int i = 0; i < certRefs.length; i++) { 125 if (certRefs[i] == 0) { 126 continue; 127 } 128 certs.add(new OpenSSLX509CRL(certRefs[i])); 129 } 130 return certs; 131 } 132 133 @Override 134 public Set<String> getCriticalExtensionOIDs() { 135 String[] critOids = 136 NativeCrypto.get_X509_CRL_ext_oids(mContext, NativeCrypto.EXTENSION_TYPE_CRITICAL); 137 138 /* 139 * This API has a special case that if there are no extensions, we 140 * should return null. So if we have no critical extensions, we'll check 141 * non-critical extensions. 142 */ 143 if ((critOids.length == 0) 144 && (NativeCrypto.get_X509_CRL_ext_oids(mContext, 145 NativeCrypto.EXTENSION_TYPE_NON_CRITICAL).length == 0)) { 146 return null; 147 } 148 149 return new HashSet<String>(Arrays.asList(critOids)); 150 } 151 152 @Override 153 public byte[] getExtensionValue(String oid) { 154 return NativeCrypto.X509_CRL_get_ext_oid(mContext, oid); 155 } 156 157 @Override 158 public Set<String> getNonCriticalExtensionOIDs() { 159 String[] nonCritOids = 160 NativeCrypto.get_X509_CRL_ext_oids(mContext, 161 NativeCrypto.EXTENSION_TYPE_NON_CRITICAL); 162 163 /* 164 * This API has a special case that if there are no extensions, we 165 * should return null. So if we have no non-critical extensions, we'll 166 * check critical extensions. 167 */ 168 if ((nonCritOids.length == 0) 169 && (NativeCrypto.get_X509_CRL_ext_oids(mContext, 170 NativeCrypto.EXTENSION_TYPE_CRITICAL).length == 0)) { 171 return null; 172 } 173 174 return new HashSet<String>(Arrays.asList(nonCritOids)); 175 } 176 177 @Override 178 public boolean hasUnsupportedCriticalExtension() { 179 final String[] criticalOids = 180 NativeCrypto.get_X509_CRL_ext_oids(mContext, NativeCrypto.EXTENSION_TYPE_CRITICAL); 181 for (String oid : criticalOids) { 182 final long extensionRef = NativeCrypto.X509_CRL_get_ext(mContext, oid); 183 if (NativeCrypto.X509_supported_extension(extensionRef) != 1) { 184 return true; 185 } 186 } 187 188 return false; 189 } 190 191 @Override 192 public byte[] getEncoded() throws CRLException { 193 return NativeCrypto.i2d_X509_CRL(mContext); 194 } 195 196 private void verifyOpenSSL(OpenSSLKey pkey) throws CRLException, NoSuchAlgorithmException, 197 InvalidKeyException, NoSuchProviderException, SignatureException { 198 NativeCrypto.X509_CRL_verify(mContext, pkey.getPkeyContext()); 199 } 200 201 private void verifyInternal(PublicKey key, String sigProvider) throws CRLException, 202 NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, 203 SignatureException { 204 String sigAlg = getSigAlgName(); 205 if (sigAlg == null) { 206 sigAlg = getSigAlgOID(); 207 } 208 209 final Signature sig; 210 if (sigProvider == null) { 211 sig = Signature.getInstance(sigAlg); 212 } else { 213 sig = Signature.getInstance(sigAlg, sigProvider); 214 } 215 216 sig.initVerify(key); 217 sig.update(getTBSCertList()); 218 if (!sig.verify(getSignature())) { 219 throw new SignatureException("signature did not verify"); 220 } 221 } 222 223 @Override 224 public void verify(PublicKey key) throws CRLException, NoSuchAlgorithmException, 225 InvalidKeyException, NoSuchProviderException, SignatureException { 226 if (key instanceof OpenSSLKeyHolder) { 227 OpenSSLKey pkey = ((OpenSSLKeyHolder) key).getOpenSSLKey(); 228 verifyOpenSSL(pkey); 229 return; 230 } 231 232 verifyInternal(key, null); 233 } 234 235 @Override 236 public void verify(PublicKey key, String sigProvider) throws CRLException, 237 NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, 238 SignatureException { 239 verifyInternal(key, sigProvider); 240 } 241 242 @Override 243 public int getVersion() { 244 return (int) NativeCrypto.X509_CRL_get_version(mContext) + 1; 245 } 246 247 @Override 248 public Principal getIssuerDN() { 249 return getIssuerX500Principal(); 250 } 251 252 @Override 253 public X500Principal getIssuerX500Principal() { 254 final byte[] issuer = NativeCrypto.X509_CRL_get_issuer_name(mContext); 255 return new X500Principal(issuer); 256 } 257 258 @Override 259 public Date getThisUpdate() { 260 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 261 calendar.set(Calendar.MILLISECOND, 0); 262 NativeCrypto.ASN1_TIME_to_Calendar(NativeCrypto.X509_CRL_get_lastUpdate(mContext), 263 calendar); 264 return calendar.getTime(); 265 } 266 267 @Override 268 public Date getNextUpdate() { 269 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 270 calendar.set(Calendar.MILLISECOND, 0); 271 NativeCrypto.ASN1_TIME_to_Calendar(NativeCrypto.X509_CRL_get_nextUpdate(mContext), 272 calendar); 273 return calendar.getTime(); 274 } 275 276 @Override 277 public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { 278 final long revokedRef = NativeCrypto.X509_CRL_get0_by_serial(mContext, 279 serialNumber.toByteArray()); 280 if (revokedRef == 0) { 281 return null; 282 } 283 284 return new OpenSSLX509CRLEntry(NativeCrypto.X509_REVOKED_dup(revokedRef)); 285 } 286 287 @Override 288 public X509CRLEntry getRevokedCertificate(X509Certificate certificate) { 289 if (certificate instanceof OpenSSLX509Certificate) { 290 OpenSSLX509Certificate osslCert = (OpenSSLX509Certificate) certificate; 291 final long x509RevokedRef = NativeCrypto.X509_CRL_get0_by_cert(mContext, 292 osslCert.getContext()); 293 294 if (x509RevokedRef == 0) { 295 return null; 296 } 297 298 return new OpenSSLX509CRLEntry(NativeCrypto.X509_REVOKED_dup(x509RevokedRef)); 299 } 300 301 return getRevokedCertificate(certificate.getSerialNumber()); 302 } 303 304 @Override 305 public Set<? extends X509CRLEntry> getRevokedCertificates() { 306 final long[] entryRefs = NativeCrypto.X509_CRL_get_REVOKED(mContext); 307 if (entryRefs == null || entryRefs.length == 0) { 308 return null; 309 } 310 311 final Set<OpenSSLX509CRLEntry> crlSet = new HashSet<OpenSSLX509CRLEntry>(); 312 for (long entryRef : entryRefs) { 313 crlSet.add(new OpenSSLX509CRLEntry(entryRef)); 314 } 315 316 return crlSet; 317 } 318 319 @Override 320 public byte[] getTBSCertList() throws CRLException { 321 return NativeCrypto.get_X509_CRL_crl_enc(mContext); 322 } 323 324 @Override 325 public byte[] getSignature() { 326 return NativeCrypto.get_X509_CRL_signature(mContext); 327 } 328 329 @Override 330 public String getSigAlgName() { 331 return AlgNameMapper.map2AlgName(getSigAlgOID()); 332 } 333 334 @Override 335 public String getSigAlgOID() { 336 return NativeCrypto.get_X509_CRL_sig_alg_oid(mContext); 337 } 338 339 @Override 340 public byte[] getSigAlgParams() { 341 return NativeCrypto.get_X509_CRL_sig_alg_parameter(mContext); 342 } 343 344 @Override 345 public boolean isRevoked(Certificate cert) { 346 if (!(cert instanceof X509Certificate)) { 347 return false; 348 } 349 350 final OpenSSLX509Certificate osslCert; 351 if (cert instanceof OpenSSLX509Certificate) { 352 osslCert = (OpenSSLX509Certificate) cert; 353 } else { 354 try { 355 osslCert = OpenSSLX509Certificate.fromX509DerInputStream(new ByteArrayInputStream( 356 cert.getEncoded())); 357 } catch (Exception e) { 358 throw new RuntimeException("cannot convert certificate", e); 359 } 360 } 361 362 final long x509RevokedRef = NativeCrypto.X509_CRL_get0_by_cert(mContext, 363 osslCert.getContext()); 364 365 return x509RevokedRef != 0; 366 } 367 368 @Override 369 public String toString() { 370 ByteArrayOutputStream os = new ByteArrayOutputStream(); 371 final long bioCtx = NativeCrypto.create_BIO_OutputStream(os); 372 try { 373 NativeCrypto.X509_CRL_print(bioCtx, mContext); 374 return os.toString(); 375 } finally { 376 NativeCrypto.BIO_free_all(bioCtx); 377 } 378 } 379 380 @Override 381 protected void finalize() throws Throwable { 382 try { 383 if (mContext != 0) { 384 NativeCrypto.X509_CRL_free(mContext); 385 } 386 } finally { 387 super.finalize(); 388 } 389 } 390 391 } 392