download a certificate from a ldap server in java - ldap

Can someone explain to me whether following code is correct to download a certificate ties to a specific person in java? I am getting an exception as "unknown protocol: ldaps".
public void downloadCert() {
String urlStr="ldaps://aServerSomeWhere:636/cn=doe%20john,ou=personnel,o=comany123,c=us?caCertificate;binary";
URL url = null;
try {
url = new URL(urlStr);
URLConnection con = url.openConnection();
InputStream is = con.getInputStream();
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(is);
System.out.println("getVersion: " + cert.getVersion());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}

No it isn't correct. There is no handler for the LDAPS: protocol in the URL/URLConnection system.
You can use JNDI to get the caCertificate attribute of that user, via DirContext.getAttributes().

Related

rest-assured with pfx file always returns 401

From this source: RESTAssured - use .pfx certificate for https call
I created below.
#Test
void testPfxKey() {
// Source: https://stackoverflow.com/questions/42235588/restassured-use-pfx-certificate-for-https-call
FileInputStream instream1=null;
KeyStore keyStore=null;
org.apache.http.conn.ssl.SSLSocketFactory lSchemeSocketFactory=null;
try {
instream1 = new FileInputStream(new File("C:/Path/To/pfxfile.pfx"));
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(instream1, "pfxfilepwd".toCharArray());
X509HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
lSchemeSocketFactory = new org.apache.http.conn.ssl.SSLSocketFactory(keyStore, "pfxfilepwd");
lSchemeSocketFactory.setHostnameVerifier(hostnameVerifier);
} catch (Exception e) {
e.printStackTrace();
}
RestAssured.config = RestAssured.config().sslConfig(new SSLConfig().with().sslSocketFactory(lSchemeSocketFactory).and().allowAllHostnames().relaxedHTTPSValidation());
RestAssured.given().
contentType("application/json").
headers(
"Subscription-Key", "key-value",
"Accept-Encoding", "gzip,deflate"
);
Response response = RestAssured.get("https://endpoint.net/resource/path");
System.out.println(response.getStatusCode());
}
response.getStatusCode() always returns 401. I am expecting a 200. I have checked keyfile path, password and also the enpoint. All seem to be OK. When I run use ReadyAPI then I get a response. Please advice how to resolve this issue. Thanks you all!
I found this issue! I need to send headers with each request. Also .relaxedHTTPSValidation() should NOT be used in this case. We are in fact providing certificates that should be authenticated! Below code works:
#Test
void testPfxKey() {
FileInputStream instream1 = null;
KeyStore keyStore = null;
org.apache.http.conn.ssl.SSLSocketFactory lSchemeSocketFactory = null;
try {
instream1 = new FileInputStream(new File("C:/Path/To/pfxfile.pfx"));
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(instream1, "pfxfilepwd".toCharArray());
X509HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
lSchemeSocketFactory = new org.apache.http.conn.ssl.SSLSocketFactory(keyStore, "pfxfilepwd");
lSchemeSocketFactory.setHostnameVerifier(hostnameVerifier);
} catch (Exception e) {
e.printStackTrace();
}
RestAssured.config = RestAssured.config().sslConfig(new SSLConfig().with().sslSocketFactory(lSchemeSocketFactory).and().allowAllHostnames());
System.out.println(
RestAssured.given().
contentType("application/json").
headers(
"Subscription-Key", "key-value",
"Accept-Encoding", "gzip,deflate"
)
.get("https://endpoint.net/resource/path")
.getStatusCode()
);
}

Java, Apache HttpClient, TLSv1.2 & OpenJDK 7

We have a small group of Tomcat servers running OpenJDK v1.7.0_111. We have plans to upgrade them and migrate them this summer but we've found that a client API we interact with is moving to require TLSv1.2 in the near term. My ultimate desire is to find a configuration change to allow for this.
The application hosted there creates it's SSL context in a pretty straight forward way:
SSLContext sslContext = SSLContexts.createDefault()
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
SSLContexts is from Apache's httpclient library (version 4.4.1) and is also pretty straight forward with how it creates the SSL context:
public static SSLContext createDefault() throws SSLInitializationException {
try {
SSLContext ex = SSLContext.getInstance("TLS");
ex.init((KeyManager[])null, (TrustManager[])null, (SecureRandom)null);
return ex;
} catch (NoSuchAlgorithmException var1) {
throw new SSLInitializationException(var1.getMessage(), var1);
} catch (KeyManagementException var2) {
throw new SSLInitializationException(var2.getMessage(), var2);
}
}
And digging through the SSLConnectionSocketFactory class, it appears that it's simply using the SSLSocket.getEnabledProtocols() method to determine which protocols are available for use. Note that this.supportedProtocols is null in my case.
public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
SSLSocket sslsock = (SSLSocket)this.socketfactory.createSocket(socket, target, port, true);
if(this.supportedProtocols != null) {
sslsock.setEnabledProtocols(this.supportedProtocols);
} else {
String[] allProtocols = sslsock.getEnabledProtocols();
ArrayList enabledProtocols = new ArrayList(allProtocols.length);
String[] arr$ = allProtocols;
int len$ = allProtocols.length;
for(int i$ = 0; i$ < len$; ++i$) {
String protocol = arr$[i$];
if(!protocol.startsWith("SSL")) {
enabledProtocols.add(protocol);
}
}
if(!enabledProtocols.isEmpty()) {
sslsock.setEnabledProtocols((String[])enabledProtocols.toArray(new String[enabledProtocols.size()]));
}
}
The problem I'm having is that while running a few preliminary tests I'm unable to get these clients to connect to an API requiring TLSv1.2.
In the following example I can get the URLConnection code to complete by including the -Dhttps.protocols=TLSv1.2 parameter, but I cannot get the Apache connection to connect.
public static void main(String[] args) throws Exception{
String testURL = "https://testapi.com";
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, null, null);
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
HttpGet httpget = new HttpGet(testURL);
CloseableHttpResponse response = client.execute(httpget);
System.out.println("Response Code (Apache): " + response.getStatusLine().getStatusCode());
}
catch (Exception e){
System.err.println("Apache HTTP Client Failed");
e.printStackTrace();
}
try {
HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(testURL).openConnection();
urlConnection.setSSLSocketFactory(sslcontext.getSocketFactory());
urlConnection.connect();
System.out.println("Response Code (URLConnection): " + urlConnection.getResponseCode());
}
catch (Exception e){
System.err.println("HttpsURLConnection Failed");
e.printStackTrace();
}
}
Along with the -Dhttps.protocols=TLSv1.2 I've tried the -Djdk.tls.client.protocols=TLSv1.2 and the -Ddeployment.security.TLSv1.2=true JVM parameters without any luck.
Does anyone have thoughts to how to enable TLSv1.2 in this configuration without upgrading to v8 or changing the application to specifically request an instance of TLSv1.2?
jdk.tls.client.protocols only works on Java 8 (and presumably 9) which you aren't using.
https.protocols only works by default in HttpsURLConnection which httpclient doesn't use.
deployment.* only applies to JNLP and applets (if any browser still permits applets) which you aren't using.
An answer to your Q as stated, at least for 4.5, assuming you use HttpClientBuilder or HttpClients (which you didn't say), is to use .useSystemProperties() or .createSystem(), respectively; these do use the same system properties as *URLConnection -- or at least many of them including https.protocols. You should check none of the other properties included in this set is configured to do something you don't want. This does require changing the apps, but not changing them 'to specifically request ... TLSv1.2'.
Other than that you can configure the SSLConnectionSocketFactory to specify the exact protocols allowed as in the Q linked by #pvg, or SSLContexts.custom().useProtocol(String).build() to specify the upper bound -- which is enough for your case because offering the range 'up to 1.2' to a server that requires 1.2 will select 1.2.
Here is the recommended way of configuring Apache HttpClient 4.x to use a specific TLS/SSL version
CloseableHttpClient client = HttpClientBuilder.create()
.setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContext.getDefault(), new String[] { "TLSv1.2" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
Vote up to dave_thompson_085's answer

J2EE No subject alternative names present Revenge of the Jedi

Scenario, I'm dealing with a Webserver that it is a mess (no I do not control this part, I have to play the game, this was coded by one of the biggest software vendors in the world)
By default, the webserver has 2 SSL services, each one of those might have a totally different SSL Certificate
Certificate A
Signature Algorithm: sha1WithRSAEncryption
RSA Key Strength: 1024
Subject: *.dummy.nodomain
Issuer: *.dummy.nodomain
Certificate B
Signature Algorithm: sha1WithRSAEncryption
RSA Key Strength: 2048
Subject: vhcalnplcs_NPL_01
Issuer: root_NPL
Following the examples of this page
public List<String> doPostWithSSL(String direction, String dataToSend, String contentType, boolean OverrideSecurityVerifications) {
try {
URL url = new URL(direction);
List<String> webcontent = new ArrayList();
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Host", getHostByUrl(direction));
conn = new UserAgentsLibrary().getRandomUserAgent(conn);
if (contentType != null) {
conn.setRequestProperty("Content-Type", contentType);
} else {
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
}
conn.setDoOutput(true);
if (OverrideSecurityVerifications) {
try {
TrustManager[] trustAllCerts;
trustAllCerts = new TrustManager[]{new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}};
// We want to override the SSL verifications
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
SSLSocketFactory factory = ctx.getSocketFactory();
conn.setDefaultSSLSocketFactory(ctx.getSocketFactory());
HostnameVerifier allHostsValid = (String hostname1, SSLSession session) -> true;
conn.setDefaultHostnameVerifier(allHostsValid);
conn.setSSLSocketFactory(factory);
} catch (KeyManagementException kex) {
System.out.println("[+] Error bypassing SSL Security " + kex.getMessage());
} catch (NoSuchAlgorithmException nsex) {
System.out.println("[+] Error forgeting TLS " + nsex.getMessage());
}
}
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(dataToSend);
wr.flush();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) { //todo+=line+"\n";
webcontent.add(line);
}
wr.close();
rd.close();
return webcontent;
} catch (MalformedURLException mex) {
System.out.println("[+] Error: I received a malformed URL");
return null;
} catch (SSLHandshakeException sslex) {
System.out.println("[+] Error: SSL Handshake Error!" + sslex.getMessage());
return null;
} catch (IOException ioex) {
System.out.println("[+] Error: Input/Output Error!" + ioex.getMessage());
return null;
}
}
I was able to make my program work with certificate B (no issue here) but I cannot make it to work with certificate A (I suspect that the * is causing me trouble)
Things to consider
This is a sample code, do not look for irrelevant details ;)
Yes, I know that this code is vulnerable to MITM attacks and the user is being warned
No, I do not want to add the certificates to my keystore!
I'm using pure J2EE code, I do not wish to use anything that it is not standard
I would like to find a solution that will work for Windows, Mac and Linux
Someone had to have this issue in the past, could you lend me a hand?
I was too tired yesterday.
Replaced conn.setDefaultHostnameVerifier(allHostsValid);
by conn.setHostnameVerifier(allHostsValid);
And now even the cert with the wildcard works!

SSL Ldap Connection (ldaps)

I want to connect to ldap over SSL using truststore file.
I'm using the following code:
private DirContext ctxtDir = null;
Attributes attributes = null;
ldap_server_url = "ldaps://" + getLdapHostName() + ":"
+ getPort() + "/";
ldap_base_dn = getBaseDn();
ldap_user = getLogin();
ldap_password = getPwd();
ldap_trust_store_file = "C:\\truststore.jks";
ldap_trust_store_pwd = getStoreJKSPwd();
// Set the parameters
env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, ldap_context_factory);
env.put(Context.PROVIDER_URL, ldap_server_url);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, ldap_user);
env.put(Context.SECURITY_CREDENTIALS, ldap_password);
env.put(Context.SECURITY_PROTOCOL, "SSL");
// Set SSL parameters for Ldaps connection
System.setProperty("javax.net.ssl.trustStore", ldap_trust_store_file);
System.setProperty("javax.net.ssl.trustStorePassword",
ldap_trust_store_pwd);
// Try to establish the connection
try {
// create initial context
ctxtDir = new InitialDirContext(env);
attributes = getLdapattributes(ldap_base_dn);
if (null != attributes) {
isAvailable = true;
}
} catch (Exception e) {
isAvailable = false;
}
The problem is that i don't want to use the location of the truststore file, i want to use the inputstream (file content), is there any way to do that? like when using SSLContext to esbabish a https connection.
Unbound Ldap SDK is best latest LDAP API. It also offers SSLSocketFactory to establish SSL connection.
TrustAllTrustManager manager = new TrustAllTrustManager();
SSLUtil sslUtil = new SSLUtil(manager);
SSLSocketFactory socketFactory;
try {
socketFactory = sslUtil.createSSLSocketFactory("TLSv1");
}
catch (GeneralSecurityException e) {
throw new LDAPException(ResultCode.CANCELED, "An error occured while creating SSL socket factory.");
}
and use this socketFactory as
new RoundRobinServerSet(addressesArray, portsArray, socketFactory);

Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407" via https

I try to connect to a server via https that requires authentication.Moreover, I have an http proxy in the middle that also requires authentication. I use ProxyAuthSecurityHandler to authenticate with the proxy and BasicAuthSecurityHandler to authenticate with the server.
Receiving java.io.IOException: Unable to tunnel through proxy.
Proxy returns "HTTP/1.1 407 Proxy Auth Required"
at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnection.java:1525)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect (AbstractDelegateHttpsURLConnection.java:164)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at org.apache.wink.client.internal.handlers.HttpURLConnectionHandler.processRequest(HttpURLConnectionHandler.java:97)
I noticed that the implementation of ProxyAuthSecurityHandler is expecting response code 407 however, during debug we never get to the second part due to the IOException thrown.
Code snap:
ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);
MyBasicAuthenticationSecurityHandler basicAuthProps = new MyBasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);
if ("true".equals(System.getProperty("setProxy"))) {
configuration.proxyHost(proxyHost);
if ((proxyPort != null) && !proxyPort.equals("")) {
configuration.proxyPort(Integer.parseInt(proxyPort));
}
MyProxyAuthSecurityHandler proxyAuthSecHandler =
new MyProxyAuthSecurityHandler();
proxyAuthSecHandler.setUserName(proxyUser);
proxyAuthSecHandler.setPassword(proxyPass);
configuration.handlers(proxyAuthSecHandler);
}
restClient = new RestClient(configuration);
// create the createResourceWithSessionCookies instance to interact with
Resource resource = getResource(loginUrl);
// Request body is empty
ClientResponse response = resource.post(null);
Tried using wink client versions 1.1.2 and also 1.2.1. the issue repeats in both.
What I found out is that when trying to pass through a proxy using https url we first send CONNECT and only then try to send the request. The proxy server cannot read any headrs we attach to the request, cause it doesn't have the key to decrypt the traffic.
This means that the CONNECT should already have the user/pass to the proxy to pass this stage.
here is a code snap I used - that works for me:
import sun.misc.BASE64Encoder;
import java.io.*;
import java.net.*;
public class ProxyPass {
public ProxyPass(String proxyHost, int proxyPort, final String userid, final String password, String url) {
try {
/* Create a HttpURLConnection Object and set the properties */
URL u = new URL(url);
Proxy proxy =
new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
HttpURLConnection uc = (HttpURLConnection)u.openConnection(proxy);
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(userid, password.toCharArray());
}
return super.getPasswordAuthentication();
}
});
uc.connect();
/* Print the content of the url to the console. */
showContent(uc);
} catch (IOException e) {
e.printStackTrace();
}
}
private void showContent(HttpURLConnection uc) throws IOException {
InputStream i = uc.getInputStream();
char c;
InputStreamReader isr = new InputStreamReader(i);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
public static void main(String[] args) {
String proxyhost = "proxy host";
int proxyport = port;
String proxylogin = "proxy username";
String proxypass = "proxy password";
String url = "https://....";
new ProxyPass(proxyhost, proxyport, proxylogin, proxypass, url);
}
}
if you are using wink - like I do, you need to set the proxy in the ClientConfig and before passing it to the RestClient set the default authenticator.
ClientConfig configuration = new ClientConfig();
configuration.connectTimeout(timeout);
BasicAuthenticationSecurityHandler basicAuthProps = new BasicAuthenticationSecurityHandler();
basicAuthProps.setUserName(user);
basicAuthProps.setPassword(password);
configuration.handlers(basicAuthProps);
if (proxySet()) {
configuration.proxyHost(proxyHost);
if ((proxyPort != null) && !proxyPort.equals("")) {
configuration.proxyPort(Integer.parseInt(proxyPort));
}
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(proxyUser), proxyPass.toCharArray());
}
return super.getPasswordAuthentication();
}
});
}
restClient = new RestClient(configuration);
Resource resource = getResource(loginUrl);
// Request body is empty
ClientResponse response = resource.post(null);
if (response.getStatusCode() != Response.Status.OK.getStatusCode()) {
throw new RestClientException("Authentication failed for user " + user);
}
If Ilana Platonov's answer doesn't work, try editing the variables :
jdk.http.auth.tunneling.disabledSchemes
jdk.http.auth.proxying.disabledSchemes