Skip to content

Commit 2a83aa1

Browse files
committed
manually decode the data in x509 certs to remove sun.security imports
1 parent 59b7e78 commit 2a83aa1

File tree

2 files changed

+209
-71
lines changed

2 files changed

+209
-71
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/ASN1Helper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ public class ASN1Helper {
6868
static final String ASN1_EMAIL = "EMAIL";
6969

7070
static final String OID_CRL_DISTRIBUTION_POINTS = "2.5.29.31";
71-
static final String OID_OCSP = "1.3.6.1.5.5.7.48.1";
72-
static final String OID_CA_ISSUERS = "1.3.6.1.5.5.7.48.2";
71+
static final byte[] OID_OCSP = new byte[]{/* 1, 3, 6, */ 1, 5, 5, 7, 48, 1};
72+
static final byte[] OID_CA_ISSUERS = new byte[]{/* 1, 3, 6, */ 1, 5, 5, 7, 48, 2};
7373
static final String OID_AUTHORITY_INFO_ACCESS = "1.3.6.1.5.5.7.1.1";
7474

7575
private static final Map<String, String> javaToName = new HashMap<>();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/CertUtils.java

Lines changed: 207 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.ssl;
4242

43-
import static com.oracle.graal.python.builtins.modules.SSLModuleBuiltins.LOGGER;
4443
import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.JAVA_X509_CA_ISSUERS;
4544
import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.JAVA_X509_CRL_DISTRIBUTION_POINTS;
4645
import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.JAVA_X509_ISSUER;
@@ -54,6 +53,7 @@
5453
import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.OID_AUTHORITY_INFO_ACCESS;
5554
import static com.oracle.graal.python.builtins.objects.ssl.ASN1Helper.OID_CA_ISSUERS;
5655
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;
5757

5858
import java.io.BufferedReader;
5959
import java.io.ByteArrayInputStream;
@@ -89,12 +89,12 @@
8989
import java.time.ZonedDateTime;
9090
import java.time.format.DateTimeFormatter;
9191
import java.util.ArrayList;
92+
import java.util.Arrays;
9293
import java.util.Base64;
9394
import java.util.Collection;
9495
import java.util.Collections;
9596
import java.util.Date;
9697
import java.util.List;
97-
import java.util.logging.Level;
9898

9999
import javax.crypto.interfaces.DHPrivateKey;
100100
import javax.crypto.interfaces.DHPublicKey;
@@ -106,21 +106,11 @@
106106
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
107107
import com.oracle.graal.python.nodes.ErrorMessages;
108108
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
109+
import com.oracle.graal.python.util.BiConsumer;
109110
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
110111
import com.oracle.truffle.api.TruffleFile;
111112
import com.oracle.truffle.api.nodes.Node;
112113

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-
124114
public final class CertUtils {
125115

126116
private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
@@ -292,89 +282,237 @@ private static PTuple parseSubjectAltName(X509Certificate certificate, PythonObj
292282
return null;
293283
}
294284

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+
295413
@TruffleBoundary
296414
private static PTuple parseCRLPoints(X509Certificate cert, PythonObjectFactory factory) throws IOException {
297415
List<String> result = new ArrayList<>();
298416
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);
314438
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);
322445
}
323-
}
446+
}, r);
324447
}
325448
}
326449
}
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()]));
330452
}
331453

332454
@TruffleBoundary
333455
private static PTuple parseCAIssuers(X509Certificate cert, PythonObjectFactory factory) throws IOException {
334456
List<String> result = new ArrayList<>();
335457
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);
347478
}
348479
}
349480
}
350481
}
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()]));
354484
}
355485

356486
@TruffleBoundary
357487
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) {
363491
return null;
364492
}
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+
}
374512
}
375513
}
376-
}
377-
return null;
514+
}, result);
515+
return factory.createTuple(result.toArray(new String[result.size()]));
378516
}
379517

380518
public enum LoadCertError {

0 commit comments

Comments
 (0)