![JAR search and dependency download from the Maven repository](/logo.png)
org.id4me.Id4meResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of relying-party-api Show documentation
Show all versions of relying-party-api Show documentation
ID4me RelyingParty API prototype
The newest version!
/*
* Copyright (C) 2016-2020 OX Software GmbH
* Developed by Peter Höbel [email protected]
* See the LICENSE file for licensing conditions
* SPDX-License-Identifier: MIT
*/
package org.id4me;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.IDN;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.Iterator;
import org.jitsi.dnssec.validator.ValidatingResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xbill.DNS.DClass;
import org.xbill.DNS.Flags;
import org.xbill.DNS.Message;
import org.xbill.DNS.Name;
import org.xbill.DNS.RRset;
import org.xbill.DNS.Rcode;
import org.xbill.DNS.Record;
import org.xbill.DNS.Resolver;
import org.xbill.DNS.Section;
import org.xbill.DNS.SimpleResolver;
import org.xbill.DNS.Type;
/**
* This class implements methods needed to lookup the dns record for ID4me and
* verify DANE.
*
* @author phoebel
*
*/
public class Id4meResolver {
private static final Logger log = LoggerFactory.getLogger(Id4meResolver.class);
private final Resolver vr;
private final boolean dnssecRequired;
/**
* Instanciate the validating resolver object.
*
* @param dnsServer
* a dnssec enabled dns server address to use or if null is localhost
* used
* @param rootKey
* for the dns resolver to validate dnssec
* @param dnssecRequired
* whether DNSSEC is required
* @throws UnknownHostException,
* UnsupportedEncodingException, IOException
*/
public Id4meResolver(String dnsServer, String rootKey, boolean dnssecRequired)
throws UnknownHostException, UnsupportedEncodingException, IOException {
SimpleResolver sr = dnsServer != null ? new SimpleResolver(dnsServer) : new SimpleResolver();
if (dnssecRequired) {
vr = new ValidatingResolver(sr);
((ValidatingResolver) vr).loadTrustAnchors(new ByteArrayInputStream(rootKey.getBytes("ASCII")));
} else {
vr = sr;
}
this.dnssecRequired = dnssecRequired;
log.debug("Id4meResolver created for DNS server: {}", dnsServer);
}
/**
* Lookup the dns for a given id4me and verifies the data. Returns the DNS
* response and a login hint, if the id4me and the dns value are valid.
*
* @param id4me
* the user to logon
* @return the DNS response containing the attributes version, iau and iag; and
* a login hint
* @throws Exception
* if something fails
*/
public Id4meDnsDataWithLoginHint getDataFromDns(String id4me) throws Exception {
if (id4me.indexOf(".") > 0) {
if (!Id4meValidator.isValidUserid(IDN.toASCII(id4me))) {
log.info("ID4me identifier has wrong format: {}", id4me);
throw new Exception("ID4me identifier has wrong format: " + id4me);
}
} else {
if (!Id4meValidator.isValidUserid(id4me)) {
log.info("ID4me identifier has wrong format: {}", id4me);
throw new Exception("ID4me identifier has wrong format: " + id4me);
}
}
String loginHint;
int atPos = id4me.indexOf('@');
if (atPos > 0) {
loginHint = id4me;
String localPart = id4me.substring(0, atPos);
String domain = id4me.substring(atPos + 1, id4me.length());
id4me = "_openid." + sha256(localPart) + "." + domain;
// loginHint = localPart + "." + domain;
} else {
loginHint = id4me;
id4me = "_openid." + id4me;
}
String domain = id4me.endsWith(".") ? id4me : id4me + ".";
domain = IDN.toASCII(domain);
log.info("Get data from DNS: domain: {}", domain);
LookupResponse response = lookupDnssec(domain);
String data = null;
if (response != null)
data = response.getData();
if (response == null || data == null || data.trim().equals("")) {
log.info("No resource record found in DNS for domain: {}", domain);
String[] fields = domain.split("\\.");
if (fields.length > 2) {
int start = 2;
for (; start < fields.length; start++) {
domain = fields[start];
for (int i = start + 1; i < fields.length; i++) {
domain += "." + fields[i];
}
domain = "_openid." + domain + ".";
response = lookupDnssec(domain);
if (response != null)
data = response.getData();
if (data != null && !data.trim().equals("")) {
break;
}
log.info("No resource record found in DNS for domain: {}", domain);
}
if (data == null || data.trim().equals(""))
throw new Exception("No resource record found in DNS for domain: " + domain);
}
}
log.info("Get data from DNS: data retrieved: {}", data);
if (dnssecRequired && !response.isDnssec()) {
log.info("Error getting domain-id data from DNS: DNSSFLAG == false");
throw new Exception("Error getting domain-id data from DNS: DNSSFLAG == false");
}
Id4meDnsData dnsResponse = Id4meDnsResponseParser.parseDnsResponse(data);
return new Id4meDnsDataWithLoginHint(dnsResponse, loginHint);
}
private String sha256(String str) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(str.getBytes());
byte[] digest = md.digest();
String hash = String.format("%064x", new java.math.BigInteger(1, digest));
String sha256 = hash.substring(0, 56);
log.debug("sha256(\"{}\") = \"{}\"", str, sha256);
return sha256;
}
public static class Id4meDnsDataWithLoginHint {
private final Id4meDnsData dnsResponse;
private final String loginHint;
Id4meDnsDataWithLoginHint(Id4meDnsData dnsResponse, String loginHint) {
this.dnsResponse = dnsResponse;
this.loginHint = loginHint;
}
public Id4meDnsData getDnsResponse() {
return dnsResponse;
}
String getLoginHint() {
return loginHint;
}
}
/**
* Perform a dns TXT record lookup with a dnssec validating resolver
*
* @param name
* the domain to lookup
* @return dns record data field and DNSSEC flag
* @throws IOException
*/
private LookupResponse lookupDnssec(String name) throws IOException {
return lookup(vr, name);
}
/**
* Perform a dns TLSA record lookup with a dnssec validating resolver. This data
* is used to validate the DANE status of the current TLS connection.
*
* @param name
* the domain to lookup
* @return dns record data field and DNSSEC flag
* @throws IOException
*/
LookupResponse lookupDane(String name) throws IOException {
return lookupWithType(vr, name, Type.TLSA);
}
@SuppressWarnings("unchecked")
private LookupResponse lookup(Resolver resolver, String name) throws IOException {
log.debug("DNS lookup: {}", name);
Record qr = Record.newRecord(Name.fromConstantString(name), Type.TXT, DClass.IN);
Message response = resolver.send(Message.newQuery(qr));
boolean dnssec = response.getHeader().getFlag(Flags.AD);
String rcode = Rcode.string(response.getRcode());
if (!"NOERROR".equals(rcode)) {
log.warn("DNS lookup: response error: {}", rcode);
return null;
}
RRset[] answer = response.getSectionRRsets(Section.ANSWER);
for (RRset set : answer) {
Iterator rrIter = set.rrs();
while (rrIter.hasNext()) {
final Record rec = rrIter.next();
if (rec.getType() == Type.TXT) {
String data = rec.rdataToString();
log.debug("DNS lookup: response: {}", data);
return new LookupResponse(data.replace("\"", ""), dnssec);
}
}
}
String data = qr.rdataToString();
log.debug("DNS lookup: response: {}", data);
return new LookupResponse(data, dnssec);
}
@SuppressWarnings("unchecked")
private LookupResponse lookupWithType(Resolver resolver, String name, int type) throws IOException {
log.debug("DNS lookup: {}; type: {}", name, type);
Record qr = Record.newRecord(Name.fromConstantString(name), Type.ANY, DClass.IN);
Message response = resolver.send(Message.newQuery(qr));
boolean dnssec = response.getHeader().getFlag(Flags.AD);
String rcode = Rcode.string(response.getRcode());
if (!"NOERROR".equals(rcode)) {
log.warn("DNS lookup: response error: {}", rcode);
return null;
}
RRset[] answer = response.getSectionRRsets(Section.ANSWER);
for (RRset set : answer) {
Iterator rrIter = set.rrs();
while (rrIter.hasNext()) {
final Record rec = rrIter.next();
if (rec.getType() == type) {
String data = rec.rdataToString();
log.debug("DNS lookup: response: {}", data);
return new LookupResponse(data.replace("\"", ""), dnssec);
}
if (rec.getType() == Type.CNAME) {
String data = rec.rdataToString();
log.debug("DNS lookup: response (CNAME): {}", data);
return lookupWithType(resolver, data.replace("\"", ""), type);
}
}
}
String data = qr.rdataToString();
log.debug("DNS lookup: response: {}", data);
return new LookupResponse(data, dnssec);
}
class LookupResponse {
private final String data;
private final boolean dnssec;
LookupResponse(String data, boolean dnssec) {
this.data = data;
this.dnssec = dnssec;
}
String getData() {
return data;
}
boolean isDnssec() {
return dnssec;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy