|
40 | 40 | */
|
41 | 41 | package com.oracle.graal.python.builtins.objects.ssl;
|
42 | 42 |
|
43 |
| -import static com.oracle.graal.python.builtins.modules.SSLModuleBuiltins.LOGGER; |
44 | 43 | import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.JAVA_X509_CA_ISSUERS;
|
45 | 44 | import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.JAVA_X509_CRL_DISTRIBUTION_POINTS;
|
46 | 45 | import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.JAVA_X509_ISSUER;
|
|
54 | 53 | import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.OID_AUTHORITY_INFO_ACCESS;
|
55 | 54 | import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.OID_CA_ISSUERS;
|
56 | 55 | import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.OID_CRL_DISTRIBUTION_POINTS;
|
| 56 | +import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.OID_OCSP; |
57 | 57 |
|
58 | 58 | import java.io.BufferedReader;
|
59 | 59 | import java.io.ByteArrayInputStream;
|
|
89 | 89 | import java.time.ZonedDateTime;
|
90 | 90 | import java.time.format.DateTimeFormatter;
|
91 | 91 | import java.util.ArrayList;
|
| 92 | +import java.util.Arrays; |
92 | 93 | import java.util.Base64;
|
93 | 94 | import java.util.Collection;
|
94 | 95 | import java.util.Collections;
|
95 | 96 | import java.util.Date;
|
96 | 97 | import java.util.List;
|
97 |
| -import java.util.logging.Level; |
98 | 98 |
|
99 | 99 | import javax.crypto.interfaces.DHPrivateKey;
|
100 | 100 | import javax.crypto.interfaces.DHPublicKey;
|
|
106 | 106 | import com.oracle.graal.python.builtins.objects.tuple.PTuple;
|
107 | 107 | import com.oracle.graal.python.nodes.ErrorMessages;
|
108 | 108 | import com.oracle.graal.python.runtime.object.PythonObjectFactory;
|
| 109 | +import com.oracle.graal.python.util.BiConsumer; |
109 | 110 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
110 | 111 | import com.oracle.truffle.api.TruffleFile;
|
111 | 112 | import com.oracle.truffle.api.nodes.Node;
|
112 | 113 |
|
113 |
| -import sun.security.util.DerValue; |
114 |
| -import sun.security.x509.AccessDescription; |
115 |
| -import sun.security.x509.AuthorityInfoAccessExtension; |
116 |
| -import sun.security.x509.CRLDistributionPointsExtension; |
117 |
| -import sun.security.x509.DistributionPoint; |
118 |
| -import sun.security.x509.GeneralName; |
119 |
| -import sun.security.x509.GeneralNameInterface; |
120 |
| -import sun.security.x509.GeneralNames; |
121 |
| -import sun.security.x509.URIName; |
122 |
| -import sun.security.x509.X509CertImpl; |
123 |
| - |
124 | 114 | public final class CertUtils {
|
125 | 115 |
|
126 | 116 | private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
|
@@ -292,89 +282,237 @@ private static PTuple parseSubjectAltName(X509Certificate certificate, PythonObj
|
292 | 282 | return null;
|
293 | 283 | }
|
294 | 284 |
|
| 285 | + // private static |
| 286 | + |
| 287 | + private static final class DerValue { |
| 288 | + private static final byte OCTET_STRING = 0x04; |
| 289 | + private static final byte OBJECT_IDENTIFIER = 0x06; |
| 290 | + private static final byte SEQUENCE = 0x10; |
| 291 | + |
| 292 | + final byte[] data; |
| 293 | + final int end; |
| 294 | + final boolean isContextTag; |
| 295 | + final int contentLen; |
| 296 | + final int contentStart; |
| 297 | + int contentTag; |
| 298 | + |
| 299 | + DerValue(byte[] data) { |
| 300 | + this(data, 0, data.length); |
| 301 | + } |
| 302 | + |
| 303 | + DerValue(byte[] data, int offset, int end) { |
| 304 | + this.data = data; |
| 305 | + this.end = end; |
| 306 | + |
| 307 | + this.contentTag = data[offset] & 0b11111; |
| 308 | + this.isContextTag = (data[offset] & 0b11000000) == 0b10000000; |
| 309 | + int[] lenAndOffset = readLength(data, offset); |
| 310 | + this.contentStart = lenAndOffset[0]; |
| 311 | + this.contentLen = lenAndOffset[1]; |
| 312 | + |
| 313 | + assert this.contentTag != 0b11111 : "extended tag range not supported"; |
| 314 | + assert contentStart + contentLen <= end; |
| 315 | + } |
| 316 | + |
| 317 | + private static int[] readLength(byte[] data, int offset) { |
| 318 | + int lenBase = data[offset + 1] & 0xff; |
| 319 | + if (lenBase < 128) { |
| 320 | + return new int[]{offset + 2, lenBase}; |
| 321 | + } else { |
| 322 | + int lengthOfLength = lenBase - 128; |
| 323 | + if (lengthOfLength > 4) { |
| 324 | + throw new IllegalArgumentException("longer than int-range DER values not supported"); |
| 325 | + } |
| 326 | + int fullLength = 0; |
| 327 | + for (int i = 0; i < lengthOfLength; i++) { |
| 328 | + fullLength = (fullLength << 8) | data[offset + 2 + i]; |
| 329 | + } |
| 330 | + return new int[]{offset + 2 + lengthOfLength, fullLength}; |
| 331 | + } |
| 332 | + } |
| 333 | + |
| 334 | + byte[] getRawData() { |
| 335 | + return Arrays.copyOfRange(data, contentStart, contentStart + contentLen); |
| 336 | + } |
| 337 | + |
| 338 | + DerValue getObjectIdentifier() { |
| 339 | + if (contentTag != OBJECT_IDENTIFIER) { |
| 340 | + return null; |
| 341 | + } else { |
| 342 | + return new DerValue(data, contentStart, contentStart + contentLen); |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + DerValue getContextTag(int tag) { |
| 347 | + if (contentTag != tag || !isContextTag) { |
| 348 | + return null; |
| 349 | + } else { |
| 350 | + return new DerValue(data, contentStart, contentStart + contentLen); |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + DerValue getOctetString() { |
| 355 | + if (contentTag != OCTET_STRING) { |
| 356 | + return null; |
| 357 | + } else { |
| 358 | + return new DerValue(data, contentStart, contentStart + contentLen); |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + DerValue getSequence() { |
| 363 | + if (contentTag != SEQUENCE) { |
| 364 | + return null; |
| 365 | + } else { |
| 366 | + return new DerValue(data, contentStart, contentStart + contentLen); |
| 367 | + } |
| 368 | + } |
| 369 | + |
| 370 | + String getGeneralNameURI() { |
| 371 | + // GeneralName ::= CHOICE { |
| 372 | + // otherName [0] AnotherName, |
| 373 | + // rfc822Name [1] IA5String, |
| 374 | + // dNSName [2] IA5String, |
| 375 | + // x400Address [3] ORAddress, |
| 376 | + // directoryName [4] Name, |
| 377 | + // ediPartyName [5] EDIPartyName, |
| 378 | + // uniformResourceIdentifier [6] IA5String, |
| 379 | + // iPAddress [7] OCTET STRING, |
| 380 | + // registeredID [8] OBJECT IDENTIFIER } |
| 381 | + if (contentTag == 6) { |
| 382 | + // we're only interested in URIs, which are encoded as 7-bit ASCII |
| 383 | + return new String(getRawData()); |
| 384 | + } else { |
| 385 | + return null; |
| 386 | + } |
| 387 | + } |
| 388 | + |
| 389 | + List<DerValue> getSequenceElements() { |
| 390 | + List<DerValue> result = new ArrayList<>(); |
| 391 | + iterateSequence((e, r) -> { |
| 392 | + result.add(e); |
| 393 | + }, result); |
| 394 | + return result; |
| 395 | + } |
| 396 | + |
| 397 | + <T> void iterateSequence(BiConsumer<DerValue, T> consumer, T value) { |
| 398 | + int sequenceStart = contentStart; |
| 399 | + int sequenceEnd = contentStart + contentLen; |
| 400 | + DerValue sequenceData = getSequence(); |
| 401 | + if (sequenceData == null) { |
| 402 | + return; |
| 403 | + } |
| 404 | + int i = sequenceStart; |
| 405 | + while (i < sequenceEnd) { |
| 406 | + DerValue element = new DerValue(data, i, sequenceEnd); |
| 407 | + i = element.contentStart + element.contentLen; |
| 408 | + consumer.accept(element, value); |
| 409 | + } |
| 410 | + } |
| 411 | + } |
| 412 | + |
295 | 413 | @TruffleBoundary
|
296 | 414 | private static PTuple parseCRLPoints(X509Certificate cert, PythonObjectFactory factory) throws IOException {
|
297 | 415 | List<String> result = new ArrayList<>();
|
298 | 416 | byte[] bytes = cert.getExtensionValue(OID_CRL_DISTRIBUTION_POINTS);
|
299 |
| - if (bytes != null) { |
300 |
| - DerValue val = new DerValue(bytes); |
301 |
| - bytes = val.getOctetString(); |
302 |
| - CRLDistributionPointsExtension cdpe; |
303 |
| - try { |
304 |
| - cdpe = new CRLDistributionPointsExtension(false, bytes); |
305 |
| - } catch (IOException ex) { |
306 |
| - // just ignore |
307 |
| - LOGGER.log(Level.FINER, "", ex); |
308 |
| - return null; |
309 |
| - } |
310 |
| - List<DistributionPoint> points = cdpe.get("points"); |
311 |
| - if (points != null) { |
312 |
| - for (DistributionPoint point : points) { |
313 |
| - GeneralNames fullName = point.getFullName(); |
| 417 | + if (bytes == null) { |
| 418 | + return null; |
| 419 | + } |
| 420 | + DerValue data = new DerValue(bytes).getOctetString(); |
| 421 | + if (data == null) { |
| 422 | + return null; |
| 423 | + } |
| 424 | + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint |
| 425 | + data.iterateSequence((element, r) -> { |
| 426 | + // DistributionPoint ::= SEQUENCE { |
| 427 | + // distributionPoint [0] DistributionPointName OPTIONAL, |
| 428 | + // reasons [1] ReasonFlags OPTIONAL, |
| 429 | + // cRLIssuer [2] GeneralNames OPTIONAL } |
| 430 | + DerValue dp = element.getSequence(); |
| 431 | + if (dp != null) { |
| 432 | + DerValue dpn = dp.getContextTag(0); |
| 433 | + if (dpn != null) { |
| 434 | + // DistributionPointName ::= CHOICE { |
| 435 | + // fullName [0] GeneralNames, |
| 436 | + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } |
| 437 | + DerValue fullName = dp.getContextTag(0); |
314 | 438 | if (fullName != null) {
|
315 |
| - List<GeneralName> names = fullName.names(); |
316 |
| - if (names != null) { |
317 |
| - for (GeneralName generalName : names) { |
318 |
| - GeneralNameInterface n = generalName.getName(); |
319 |
| - if (n instanceof URIName) { |
320 |
| - result.add(((URIName) n).getURI().toString()); |
321 |
| - } |
| 439 | + fullName.contentTag = DerValue.SEQUENCE; // implicitly a SEQUENCE |
| 440 | + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName |
| 441 | + fullName.iterateSequence((name, r2) -> { |
| 442 | + String nextUri = name.getGeneralNameURI(); |
| 443 | + if (nextUri != null) { |
| 444 | + r2.add(nextUri); |
322 | 445 | }
|
323 |
| - } |
| 446 | + }, r); |
324 | 447 | }
|
325 | 448 | }
|
326 | 449 | }
|
327 |
| - return factory.createTuple(result.toArray(new String[result.size()])); |
328 |
| - } |
329 |
| - return null; |
| 450 | + }, result); |
| 451 | + return factory.createTuple(result.toArray(new String[result.size()])); |
330 | 452 | }
|
331 | 453 |
|
332 | 454 | @TruffleBoundary
|
333 | 455 | private static PTuple parseCAIssuers(X509Certificate cert, PythonObjectFactory factory) throws IOException {
|
334 | 456 | List<String> result = new ArrayList<>();
|
335 | 457 | byte[] bytes = cert.getExtensionValue(OID_AUTHORITY_INFO_ACCESS);
|
336 |
| - if (bytes != null) { |
337 |
| - DerValue val = new DerValue(bytes); |
338 |
| - bytes = val.getOctetString(); |
339 |
| - AuthorityInfoAccessExtension aiae = new AuthorityInfoAccessExtension(false, bytes); |
340 |
| - for (AccessDescription ad : aiae.getAccessDescriptions()) { |
341 |
| - if (ad.getAccessMethod().toString().equals(OID_CA_ISSUERS)) { |
342 |
| - GeneralName gn = ad.getAccessLocation(); |
343 |
| - if (gn != null) { |
344 |
| - GeneralNameInterface n = gn.getName(); |
345 |
| - if (n instanceof URIName) { |
346 |
| - result.add(((URIName) n).getURI().toString()); |
| 458 | + if (bytes == null) { |
| 459 | + return null; |
| 460 | + } |
| 461 | + DerValue data = new DerValue(bytes).getOctetString(); |
| 462 | + if (data == null) { |
| 463 | + return null; |
| 464 | + } |
| 465 | + // AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription |
| 466 | + data.iterateSequence((element, r) -> { |
| 467 | + // AccessDescription ::= SEQUENCE { |
| 468 | + // accessMethod OBJECT IDENTIFIER, |
| 469 | + // accessLocation GeneralName } |
| 470 | + List<DerValue> elements = element.getSequenceElements(); |
| 471 | + if (elements.size() == 2) { |
| 472 | + DerValue accessMethod = elements.get(0).getObjectIdentifier(); |
| 473 | + if (accessMethod != null) { |
| 474 | + if (Arrays.equals(accessMethod.getRawData(), OID_CA_ISSUERS)) { |
| 475 | + String uri = elements.get(1).getGeneralNameURI(); |
| 476 | + if (uri != null) { |
| 477 | + r.add(uri); |
347 | 478 | }
|
348 | 479 | }
|
349 | 480 | }
|
350 | 481 | }
|
351 |
| - return factory.createTuple(result.toArray(new String[result.size()])); |
352 |
| - } |
353 |
| - return null; |
| 482 | + }, result); |
| 483 | + return factory.createTuple(result.toArray(new String[result.size()])); |
354 | 484 | }
|
355 | 485 |
|
356 | 486 | @TruffleBoundary
|
357 | 487 | private static PTuple parseOCSP(X509Certificate cert, PythonObjectFactory factory) {
|
358 |
| - // Inlined from sun.security.provider.certpath.OCSP#getResponderURI |
359 |
| - // Examine the certificate's AuthorityInfoAccess extension |
360 |
| - X509CertImpl certImpl = (X509CertImpl) cert; |
361 |
| - AuthorityInfoAccessExtension aia = certImpl.getAuthorityInfoAccessExtension(); |
362 |
| - if (aia == null) { |
| 488 | + List<String> result = new ArrayList<>(); |
| 489 | + byte[] bytes = cert.getExtensionValue(OID_AUTHORITY_INFO_ACCESS); |
| 490 | + if (bytes == null) { |
363 | 491 | return null;
|
364 | 492 | }
|
365 |
| - |
366 |
| - List<AccessDescription> descriptions = aia.getAccessDescriptions(); |
367 |
| - for (AccessDescription description : descriptions) { |
368 |
| - if (description.getAccessMethod().equals( |
369 |
| - (Object) AccessDescription.Ad_OCSP_Id)) { |
370 |
| - GeneralName generalName = description.getAccessLocation(); |
371 |
| - if (generalName.getType() == GeneralNameInterface.NAME_URI) { |
372 |
| - URIName uri = (URIName) generalName.getName(); |
373 |
| - return factory.createTuple(new String[]{uri.getURI().toString()}); |
| 493 | + DerValue data = new DerValue(bytes).getOctetString(); |
| 494 | + if (data == null) { |
| 495 | + return null; |
| 496 | + } |
| 497 | + // AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription |
| 498 | + data.iterateSequence((element, r) -> { |
| 499 | + // AccessDescription ::= SEQUENCE { |
| 500 | + // accessMethod OBJECT IDENTIFIER, |
| 501 | + // accessLocation GeneralName } |
| 502 | + List<DerValue> elements = element.getSequenceElements(); |
| 503 | + if (elements.size() == 2) { |
| 504 | + DerValue accessMethod = elements.get(0).getObjectIdentifier(); |
| 505 | + if (accessMethod != null) { |
| 506 | + if (Arrays.equals(accessMethod.getRawData(), OID_OCSP)) { |
| 507 | + String uri = elements.get(1).getGeneralNameURI(); |
| 508 | + if (uri != null) { |
| 509 | + r.add(uri); |
| 510 | + } |
| 511 | + } |
374 | 512 | }
|
375 | 513 | }
|
376 |
| - } |
377 |
| - return null; |
| 514 | + }, result); |
| 515 | + return factory.createTuple(result.toArray(new String[result.size()])); |
378 | 516 | }
|
379 | 517 |
|
380 | 518 | public enum LoadCertError {
|
|
0 commit comments