use of org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod in project XobotOS by xamarin.
the class X509CertSelector method match.
/**
* Returns whether the specified certificate matches all the criteria
* collected in this instance.
*
* @param certificate
* the certificate to check.
* @return {@code true} if the certificate matches all the criteria,
* otherwise {@code false}.
*/
public boolean match(Certificate certificate) {
if (!(certificate instanceof X509Certificate)) {
return false;
}
X509Certificate cert = (X509Certificate) certificate;
if ((certificateEquals != null) && !certificateEquals.equals(cert)) {
return false;
}
if ((serialNumber != null) && !serialNumber.equals(cert.getSerialNumber())) {
return false;
}
if ((issuer != null) && !issuer.equals(cert.getIssuerX500Principal())) {
return false;
}
if ((subject != null) && !subject.equals(cert.getSubjectX500Principal())) {
return false;
}
if ((subjectKeyIdentifier != null) && !Arrays.equals(subjectKeyIdentifier, // are taken from rfc 3280 (http://www.ietf.org/rfc/rfc3280.txt)
getExtensionValue(cert, "2.5.29.14"))) {
return false;
}
if ((authorityKeyIdentifier != null) && !Arrays.equals(authorityKeyIdentifier, getExtensionValue(cert, "2.5.29.35"))) {
return false;
}
if (certificateValid != null) {
try {
cert.checkValidity(certificateValid);
} catch (CertificateExpiredException e) {
return false;
} catch (CertificateNotYetValidException e) {
return false;
}
}
if (privateKeyValid != null) {
try {
byte[] bytes = getExtensionValue(cert, "2.5.29.16");
if (bytes == null) {
return false;
}
PrivateKeyUsagePeriod pkup = (PrivateKeyUsagePeriod) PrivateKeyUsagePeriod.ASN1.decode(bytes);
Date notBefore = pkup.getNotBefore();
Date notAfter = pkup.getNotAfter();
if ((notBefore == null) && (notAfter == null)) {
return false;
}
if ((notBefore != null) && notBefore.compareTo(privateKeyValid) > 0) {
return false;
}
if ((notAfter != null) && notAfter.compareTo(privateKeyValid) < 0) {
return false;
}
} catch (IOException e) {
return false;
}
}
if (subjectPublicKeyAlgID != null) {
try {
byte[] encoding = cert.getPublicKey().getEncoded();
AlgorithmIdentifier ai = ((SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1.decode(encoding)).getAlgorithmIdentifier();
if (!subjectPublicKeyAlgID.equals(ai.getAlgorithm())) {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if (subjectPublicKey != null) {
if (!Arrays.equals(subjectPublicKey, cert.getPublicKey().getEncoded())) {
return false;
}
}
if (keyUsage != null) {
boolean[] ku = cert.getKeyUsage();
if (ku != null) {
int i = 0;
int min_length = (ku.length < keyUsage.length) ? ku.length : keyUsage.length;
for (; i < min_length; i++) {
if (keyUsage[i] && !ku[i]) {
// but certificate does not.
return false;
}
}
for (; i < keyUsage.length; i++) {
if (keyUsage[i]) {
return false;
}
}
}
}
if (extendedKeyUsage != null) {
try {
List keyUsage = cert.getExtendedKeyUsage();
if (keyUsage != null) {
if (!keyUsage.containsAll(extendedKeyUsage)) {
return false;
}
}
} catch (CertificateParsingException e) {
return false;
}
}
if (pathLen != -1) {
int p_len = cert.getBasicConstraints();
if ((pathLen < 0) && (p_len >= 0)) {
// need end-entity but got CA
return false;
}
if ((pathLen > 0) && (pathLen > p_len)) {
// allowed _pathLen is small
return false;
}
}
if (subjectAltNames != null) {
PASSED: try {
byte[] bytes = getExtensionValue(cert, "2.5.29.17");
if (bytes == null) {
return false;
}
List<GeneralName> sans = ((GeneralNames) GeneralNames.ASN1.decode(bytes)).getNames();
if ((sans == null) || (sans.size() == 0)) {
return false;
}
boolean[][] map = new boolean[9][];
// initialize the check map
for (int i = 0; i < 9; i++) {
map[i] = (subjectAltNames[i] == null) ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()];
}
for (GeneralName name : sans) {
int tag = name.getTag();
for (int i = 0; i < map[tag].length; i++) {
if (subjectAltNames[tag].get(i).equals(name)) {
if (!matchAllNames) {
break PASSED;
}
map[tag][i] = true;
}
}
}
if (!matchAllNames) {
// there was not any match
return false;
}
// else check the map
for (int tag = 0; tag < 9; tag++) {
for (int name = 0; name < map[tag].length; name++) {
if (!map[tag][name]) {
return false;
}
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if (nameConstraints != null) {
if (!nameConstraints.isAcceptable(cert)) {
return false;
}
}
if (policies != null) {
byte[] bytes = getExtensionValue(cert, "2.5.29.32");
if (bytes == null) {
return false;
}
if (policies.size() == 0) {
// one policy in it.
return true;
}
PASSED: try {
List<PolicyInformation> policyInformations = ((CertificatePolicies) CertificatePolicies.ASN1.decode(bytes)).getPolicyInformations();
for (PolicyInformation policyInformation : policyInformations) {
if (policies.contains(policyInformation.getPolicyIdentifier())) {
break PASSED;
}
}
return false;
} catch (IOException e) {
// the extension is invalid
return false;
}
}
if (pathToNames != null) {
byte[] bytes = getExtensionValue(cert, "2.5.29.30");
if (bytes != null) {
NameConstraints nameConstraints;
try {
nameConstraints = (NameConstraints) NameConstraints.ASN1.decode(bytes);
} catch (IOException e) {
// the extension is invalid;
return false;
}
if (!nameConstraints.isAcceptable(pathToNames)) {
return false;
}
}
}
return true;
}
use of org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod in project robovm by robovm.
the class X509CertSelector method match.
/**
* Returns whether the specified certificate matches all the criteria
* collected in this instance.
*
* @param certificate
* the certificate to check.
* @return {@code true} if the certificate matches all the criteria,
* otherwise {@code false}.
*/
public boolean match(Certificate certificate) {
if (!(certificate instanceof X509Certificate)) {
return false;
}
X509Certificate cert = (X509Certificate) certificate;
if ((certificateEquals != null) && !certificateEquals.equals(cert)) {
return false;
}
if ((serialNumber != null) && !serialNumber.equals(cert.getSerialNumber())) {
return false;
}
if ((issuer != null) && !issuer.equals(cert.getIssuerX500Principal())) {
return false;
}
if ((subject != null) && !subject.equals(cert.getSubjectX500Principal())) {
return false;
}
if ((subjectKeyIdentifier != null) && !Arrays.equals(subjectKeyIdentifier, // are taken from rfc 3280 (http://www.ietf.org/rfc/rfc3280.txt)
getExtensionValue(cert, "2.5.29.14"))) {
return false;
}
if ((authorityKeyIdentifier != null) && !Arrays.equals(authorityKeyIdentifier, getExtensionValue(cert, "2.5.29.35"))) {
return false;
}
if (certificateValid != null) {
try {
cert.checkValidity(certificateValid);
} catch (CertificateExpiredException e) {
return false;
} catch (CertificateNotYetValidException e) {
return false;
}
}
if (privateKeyValid != null) {
try {
byte[] bytes = getExtensionValue(cert, "2.5.29.16");
if (bytes == null) {
return false;
}
PrivateKeyUsagePeriod pkup = (PrivateKeyUsagePeriod) PrivateKeyUsagePeriod.ASN1.decode(bytes);
Date notBefore = pkup.getNotBefore();
Date notAfter = pkup.getNotAfter();
if ((notBefore == null) && (notAfter == null)) {
return false;
}
if ((notBefore != null) && notBefore.compareTo(privateKeyValid) > 0) {
return false;
}
if ((notAfter != null) && notAfter.compareTo(privateKeyValid) < 0) {
return false;
}
} catch (IOException e) {
return false;
}
}
if (subjectPublicKeyAlgID != null) {
try {
byte[] encoding = cert.getPublicKey().getEncoded();
AlgorithmIdentifier ai = ((SubjectPublicKeyInfo) SubjectPublicKeyInfo.ASN1.decode(encoding)).getAlgorithmIdentifier();
if (!subjectPublicKeyAlgID.equals(ai.getAlgorithm())) {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if (subjectPublicKey != null) {
if (!Arrays.equals(subjectPublicKey, cert.getPublicKey().getEncoded())) {
return false;
}
}
if (keyUsage != null) {
boolean[] ku = cert.getKeyUsage();
if (ku != null) {
int i = 0;
int min_length = (ku.length < keyUsage.length) ? ku.length : keyUsage.length;
for (; i < min_length; i++) {
if (keyUsage[i] && !ku[i]) {
// but certificate does not.
return false;
}
}
for (; i < keyUsage.length; i++) {
if (keyUsage[i]) {
return false;
}
}
}
}
if (extendedKeyUsage != null) {
try {
List keyUsage = cert.getExtendedKeyUsage();
if (keyUsage != null) {
if (!keyUsage.containsAll(extendedKeyUsage)) {
return false;
}
}
} catch (CertificateParsingException e) {
return false;
}
}
if (pathLen != -1) {
int p_len = cert.getBasicConstraints();
if ((pathLen < 0) && (p_len >= 0)) {
// need end-entity but got CA
return false;
}
if ((pathLen > 0) && (pathLen > p_len)) {
// allowed _pathLen is small
return false;
}
}
if (subjectAltNames != null) {
PASSED: try {
byte[] bytes = getExtensionValue(cert, "2.5.29.17");
if (bytes == null) {
return false;
}
List<GeneralName> sans = ((GeneralNames) GeneralNames.ASN1.decode(bytes)).getNames();
if ((sans == null) || (sans.size() == 0)) {
return false;
}
boolean[][] map = new boolean[9][];
// initialize the check map
for (int i = 0; i < 9; i++) {
map[i] = (subjectAltNames[i] == null) ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()];
}
for (GeneralName name : sans) {
int tag = name.getTag();
for (int i = 0; i < map[tag].length; i++) {
if (subjectAltNames[tag].get(i).equals(name)) {
if (!matchAllNames) {
break PASSED;
}
map[tag][i] = true;
}
}
}
if (!matchAllNames) {
// there was not any match
return false;
}
// else check the map
for (int tag = 0; tag < 9; tag++) {
for (int name = 0; name < map[tag].length; name++) {
if (!map[tag][name]) {
return false;
}
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if (nameConstraints != null) {
if (!nameConstraints.isAcceptable(cert)) {
return false;
}
}
if (policies != null) {
byte[] bytes = getExtensionValue(cert, "2.5.29.32");
if (bytes == null) {
return false;
}
if (policies.size() == 0) {
// one policy in it.
return true;
}
PASSED: try {
List<PolicyInformation> policyInformations = ((CertificatePolicies) CertificatePolicies.ASN1.decode(bytes)).getPolicyInformations();
for (PolicyInformation policyInformation : policyInformations) {
if (policies.contains(policyInformation.getPolicyIdentifier())) {
break PASSED;
}
}
return false;
} catch (IOException e) {
// the extension is invalid
return false;
}
}
if (pathToNames != null) {
byte[] bytes = getExtensionValue(cert, "2.5.29.30");
if (bytes != null) {
NameConstraints nameConstraints;
try {
nameConstraints = (NameConstraints) NameConstraints.ASN1.decode(bytes);
} catch (IOException e) {
// the extension is invalid;
return false;
}
if (!nameConstraints.isAcceptable(pathToNames)) {
return false;
}
}
}
return true;
}
use of org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod in project xipki by xipki.
the class ProfileConfCreatorDemo method certprofileQc.
// method certprofileMultipleValuedRdn
private static X509ProfileType certprofileQc() throws Exception {
X509ProfileType profile = getBaseProfile("certprofile qc", X509CertLevel.EndEntity, "5y", false);
// Subject
Subject subject = profile.getSubject();
subject.setIncSerialNumber(false);
List<RdnType> rdnControls = subject.getRdn();
rdnControls.add(createRdn(ObjectIdentifiers.DN_C, 1, 1, new String[] { "DE|FR" }, null, null));
rdnControls.add(createRdn(ObjectIdentifiers.DN_O, 1, 1));
rdnControls.add(createRdn(ObjectIdentifiers.DN_organizationIdentifier, 0, 1));
rdnControls.add(createRdn(ObjectIdentifiers.DN_OU, 0, 1));
rdnControls.add(createRdn(ObjectIdentifiers.DN_SN, 0, 1, new String[] { REGEX_SN }, null, null));
rdnControls.add(createRdn(ObjectIdentifiers.DN_CN, 1, 1));
// Extensions
// Extensions - general
ExtensionsType extensions = profile.getExtensions();
// Extensions - controls
List<ExtensionType> list = extensions.getExtension();
list.add(createExtension(Extension.subjectKeyIdentifier, true, false, null));
list.add(createExtension(Extension.cRLDistributionPoints, false, false, null));
list.add(createExtension(Extension.freshestCRL, false, false, null));
// Extensions - basicConstraints
ExtensionValueType extensionValue = null;
list.add(createExtension(Extension.basicConstraints, true, false, extensionValue));
// Extensions - AuthorityInfoAccess
extensionValue = createAuthorityInfoAccess();
list.add(createExtension(Extension.authorityInfoAccess, true, false, extensionValue));
// Extensions - AuthorityKeyIdentifier
extensionValue = createAuthorityKeyIdentifier(true);
list.add(createExtension(Extension.authorityKeyIdentifier, true, false, extensionValue));
// Extensions - keyUsage
extensionValue = createKeyUsages(new KeyUsageEnum[] { KeyUsageEnum.CONTENT_COMMITMENT }, null);
list.add(createExtension(Extension.keyUsage, true, true, extensionValue));
// Extensions - extenedKeyUsage
extensionValue = createExtendedKeyUsage(new ASN1ObjectIdentifier[] { ObjectIdentifiers.id_kp_timeStamping }, null);
list.add(createExtension(Extension.extendedKeyUsage, true, true, extensionValue));
// privateKeyUsagePeriod
extensionValue = createPrivateKeyUsagePeriod("3y");
list.add(createExtension(Extension.privateKeyUsagePeriod, true, false, extensionValue));
// QcStatements
extensionValue = createQcStatements(false);
list.add(createExtension(Extension.qCStatements, true, false, extensionValue));
return profile;
}
use of org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod in project xipki by xipki.
the class ExtensionsChecker method checkExtensionPrivateKeyUsagePeriod.
// method checkExtensionValidityModel
private void checkExtensionPrivateKeyUsagePeriod(StringBuilder failureMsg, byte[] extensionValue, Date certNotBefore, Date certNotAfter) {
ASN1GeneralizedTime notBefore = new ASN1GeneralizedTime(certNotBefore);
Date dateNotAfter;
CertValidity privateKeyUsagePeriod = certProfile.getPrivateKeyUsagePeriod();
if (privateKeyUsagePeriod == null) {
dateNotAfter = certNotAfter;
} else {
dateNotAfter = privateKeyUsagePeriod.add(certNotBefore);
if (dateNotAfter.after(certNotAfter)) {
dateNotAfter = certNotAfter;
}
}
ASN1GeneralizedTime notAfter = new ASN1GeneralizedTime(dateNotAfter);
org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod extValue = org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod.getInstance(extensionValue);
ASN1GeneralizedTime time = extValue.getNotBefore();
if (time == null) {
failureMsg.append("notBefore is absent but expected present; ");
} else if (!time.equals(notBefore)) {
addViolation(failureMsg, "notBefore", time.getTimeString(), notBefore.getTimeString());
}
time = extValue.getNotAfter();
if (time == null) {
failureMsg.append("notAfter is absent but expected present; ");
} else if (!time.equals(notAfter)) {
addViolation(failureMsg, "notAfter", time.getTimeString(), notAfter.getTimeString());
}
}
use of org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod in project xipki by xipki.
the class ExtensionsChecker method checkExtensions.
// constructor
public List<ValidationIssue> checkExtensions(Certificate cert, X509IssuerInfo issuerInfo, Extensions requestedExtensions, X500Name requestedSubject) {
ParamUtil.requireNonNull("cert", cert);
ParamUtil.requireNonNull("issuerInfo", issuerInfo);
X509Certificate jceCert;
try {
jceCert = X509Util.toX509Cert(cert);
} catch (CertificateException ex) {
throw new IllegalArgumentException("invalid cert: " + ex.getMessage());
}
List<ValidationIssue> result = new LinkedList<>();
// detect the list of extension types in certificate
Set<ASN1ObjectIdentifier> presentExtenionTypes = getExensionTypes(cert, issuerInfo, requestedExtensions);
Extensions extensions = cert.getTBSCertificate().getExtensions();
ASN1ObjectIdentifier[] oids = extensions.getExtensionOIDs();
if (oids == null) {
ValidationIssue issue = new ValidationIssue("X509.EXT.GEN", "extension general");
result.add(issue);
issue.setFailureMessage("no extension is present");
return result;
}
List<ASN1ObjectIdentifier> certExtTypes = Arrays.asList(oids);
for (ASN1ObjectIdentifier extType : presentExtenionTypes) {
if (!certExtTypes.contains(extType)) {
ValidationIssue issue = createExtensionIssue(extType);
result.add(issue);
issue.setFailureMessage("extension is absent but is required");
}
}
Map<ASN1ObjectIdentifier, ExtensionControl> extensionControls = certProfile.getExtensionControls();
for (ASN1ObjectIdentifier oid : certExtTypes) {
ValidationIssue issue = createExtensionIssue(oid);
result.add(issue);
if (!presentExtenionTypes.contains(oid)) {
issue.setFailureMessage("extension is present but is not permitted");
continue;
}
Extension ext = extensions.getExtension(oid);
StringBuilder failureMsg = new StringBuilder();
ExtensionControl extControl = extensionControls.get(oid);
if (extControl.isCritical() != ext.isCritical()) {
addViolation(failureMsg, "critical", ext.isCritical(), extControl.isCritical());
}
byte[] extensionValue = ext.getExtnValue().getOctets();
try {
if (Extension.authorityKeyIdentifier.equals(oid)) {
// AuthorityKeyIdentifier
checkExtensionIssuerKeyIdentifier(failureMsg, extensionValue, issuerInfo);
} else if (Extension.subjectKeyIdentifier.equals(oid)) {
// SubjectKeyIdentifier
checkExtensionSubjectKeyIdentifier(failureMsg, extensionValue, cert.getSubjectPublicKeyInfo());
} else if (Extension.keyUsage.equals(oid)) {
// KeyUsage
checkExtensionKeyUsage(failureMsg, extensionValue, jceCert.getKeyUsage(), requestedExtensions, extControl);
} else if (Extension.certificatePolicies.equals(oid)) {
// CertificatePolicies
checkExtensionCertificatePolicies(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.policyMappings.equals(oid)) {
// Policy Mappings
checkExtensionPolicyMappings(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.subjectAlternativeName.equals(oid)) {
// SubjectAltName
checkExtensionSubjectAltName(failureMsg, extensionValue, requestedExtensions, extControl, requestedSubject);
} else if (Extension.subjectDirectoryAttributes.equals(oid)) {
// SubjectDirectoryAttributes
checkExtensionSubjectDirAttrs(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.issuerAlternativeName.equals(oid)) {
// IssuerAltName
checkExtensionIssuerAltNames(failureMsg, extensionValue, issuerInfo);
} else if (Extension.basicConstraints.equals(oid)) {
// Basic Constraints
checkExtensionBasicConstraints(failureMsg, extensionValue);
} else if (Extension.nameConstraints.equals(oid)) {
// Name Constraints
checkExtensionNameConstraints(failureMsg, extensionValue, extensions, extControl);
} else if (Extension.policyConstraints.equals(oid)) {
// PolicyConstrains
checkExtensionPolicyConstraints(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.extendedKeyUsage.equals(oid)) {
// ExtendedKeyUsage
checkExtensionExtendedKeyUsage(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.cRLDistributionPoints.equals(oid)) {
// CRL Distribution Points
checkExtensionCrlDistributionPoints(failureMsg, extensionValue, issuerInfo);
} else if (Extension.inhibitAnyPolicy.equals(oid)) {
// Inhibit anyPolicy
checkExtensionInhibitAnyPolicy(failureMsg, extensionValue, extensions, extControl);
} else if (Extension.freshestCRL.equals(oid)) {
// Freshest CRL
checkExtensionDeltaCrlDistributionPoints(failureMsg, extensionValue, issuerInfo);
} else if (Extension.authorityInfoAccess.equals(oid)) {
// Authority Information Access
checkExtensionAuthorityInfoAccess(failureMsg, extensionValue, issuerInfo);
} else if (Extension.subjectInfoAccess.equals(oid)) {
// SubjectInfoAccess
checkExtensionSubjectInfoAccess(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_extension_admission.equals(oid)) {
// Admission
checkExtensionAdmission(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_extension_pkix_ocsp_nocheck.equals(oid)) {
// ocsp-nocheck
checkExtensionOcspNocheck(failureMsg, extensionValue);
} else if (ObjectIdentifiers.id_extension_restriction.equals(oid)) {
// restriction
checkExtensionRestriction(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_extension_additionalInformation.equals(oid)) {
// additionalInformation
checkExtensionAdditionalInformation(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_extension_validityModel.equals(oid)) {
// validityModel
checkExtensionValidityModel(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.privateKeyUsagePeriod.equals(oid)) {
// privateKeyUsagePeriod
checkExtensionPrivateKeyUsagePeriod(failureMsg, extensionValue, jceCert.getNotBefore(), jceCert.getNotAfter());
} else if (Extension.qCStatements.equals(oid)) {
// qCStatements
checkExtensionQcStatements(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (Extension.biometricInfo.equals(oid)) {
// biometricInfo
checkExtensionBiometricInfo(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_pe_tlsfeature.equals(oid)) {
// tlsFeature
checkExtensionTlsFeature(failureMsg, extensionValue, requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_xipki_ext_authorizationTemplate.equals(oid)) {
// authorizationTemplate
checkExtensionAuthorizationTemplate(failureMsg, extensionValue, requestedExtensions, extControl);
} else {
byte[] expected;
if (ObjectIdentifiers.id_smimeCapabilities.equals(oid)) {
// SMIMECapabilities
expected = smimeCapabilities.getValue();
} else {
expected = getExpectedExtValue(oid, requestedExtensions, extControl);
}
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension valus", hex(extensionValue), (expected == null) ? "not present" : hex(expected));
}
}
if (failureMsg.length() > 0) {
issue.setFailureMessage(failureMsg.toString());
}
} catch (IllegalArgumentException | ClassCastException | ArrayIndexOutOfBoundsException ex) {
LOG.debug("extension value does not have correct syntax", ex);
issue.setFailureMessage("extension value does not have correct syntax");
}
}
return result;
}
Aggregations