For supporting HTTPS connections through a Java 1.6 API to remote hosts using TLS 1.2, we have developed a customized TLS SocketConnection factory based on Bouncy Castle Libraries (v. 1.53)
It's very easy to use, just:
String httpsURL = xxxxxxxxxx
URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection )myurl.openConnection();
con.setSSLSocketFactory(new TSLSocketConnectionFactory());
InputStream ins = con.getInputStream();
During testing, I connect different web and remote hosts exposed into SSLabs Tests
90% of the time this works fine! But there are some cases in which we get an annoying error: "Internal TLS error, this could be an attack" . It has been checked that there is no attack. That's a common error based on the treatment of internal BouncyCastle exceptions. I'm trying to find a common pattern to those remote host that fails with little luck.
Updated:
Updating some code for extra information, we get this:
org.bouncycastle.crypto.tls.TlsFatalAlert: illegal_parameter(47)
at org.bouncycastle.crypto.tls.AbstractTlsClient.checkForUnexpectedServerExtension(AbstractTlsClient.java:56)
at org.bouncycastle.crypto.tls.AbstractTlsClient.processServerExtensions(AbstractTlsClient.java:207)
at org.bouncycastle.crypto.tls.TlsClientProtocol.receiveServerHelloMessage(TlsClientProtocol.java:773)
The extension type i get is this:
ExtensionType:11
ExtensionData:
Acording to ExtensionType class, "ec_point_formats". This causes "UnexpectedServerExtension" --> The "UnexpectedServerExtension" causes a --> TlsFatalAlert: illegal_parameter , and at last this a "Internal TLS error, this could be an attack"
Any advise to log or trace this strange TLS Errors....???? As i say, this code works 90%...but with some remote host i get this errof
The trick consists in overriding startHandShake to use Bouncy's TLSClientProtocol:
Override ClientExtensions to include "host" ExtensionType. Just ExtensionType.server_name ( maybe any more Extension to include?)
Create a TlsAuthentication to include remoteCerts on Socket's
peerCertificate .Also optionally check if remote certs are in
default keystore (cacerts,etc..)
I share the code of TLSSocketConnectionFactory:
public class TLSSocketConnectionFactory extends SSLSocketFactory {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SECURE RANDOM
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
private SecureRandom _secureRandom = new SecureRandom();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public Socket createSocket(Socket socket, final String host, int port, boolean arg3)
throws IOException {
if (socket == null) {
socket = new Socket();
}
if (!socket.isConnected()) {
socket.connect(new InetSocketAddress(host, port));
}
final TlsClientProtocol tlsClientProtocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), _secureRandom);
return _createSSLSocket(host, tlsClientProtocol);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SOCKET FACTORY METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public String[] getDefaultCipherSuites() {
return null;
}
#Override
public String[] getSupportedCipherSuites(){
return null;
}
#Override
public Socket createSocket( String host,
int port) throws IOException,UnknownHostException{
return null;
}
#Override
public Socket createSocket( InetAddress host,
int port) throws IOException {
return null;
}
#Override
public Socket createSocket( String host,
int port,
InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
return null;
}
#Override
public Socket createSocket( InetAddress address,
int port,
InetAddress localAddress,
int localPort) throws IOException{
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SOCKET CREATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private SSLSocket _createSSLSocket(final String host , final TlsClientProtocol tlsClientProtocol) {
return new SSLSocket() {
private java.security.cert.Certificate[] peertCerts;
#Override
public InputStream getInputStream() throws IOException {
return tlsClientProtocol.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return tlsClientProtocol.getOutputStream();
}
#Override
public synchronized void close() throws IOException {
Log.to("util").info("\\\n::::::Close Socket");
tlsClientProtocol.close();
}
#Override
public void addHandshakeCompletedListener(HandshakeCompletedListener arg0) {
}
#Override
public boolean getEnableSessionCreation() {
return false;
}
#Override
public String[] getEnabledCipherSuites() {
return null;
}
#Override
public String[] getEnabledProtocols() {
return null;
}
#Override
public boolean getNeedClientAuth(){
return false;
}
#Override
public SSLSession getSession() {
return new SSLSession() {
#Override
public int getApplicationBufferSize() {
return 0;
}
#Override
public String getCipherSuite() {
throw new UnsupportedOperationException();
}
#Override
public long getCreationTime() {
throw new UnsupportedOperationException();
}
#Override
public byte[] getId() {
throw new UnsupportedOperationException();
}
#Override
public long getLastAccessedTime() {
throw new UnsupportedOperationException();
}
#Override
public java.security.cert.Certificate[] getLocalCertificates() {
throw new UnsupportedOperationException();
}
#Override
public Principal getLocalPrincipal() {
throw new UnsupportedOperationException();
}
#Override
public int getPacketBufferSize() {
throw new UnsupportedOperationException();
}
#Override
public X509Certificate[] getPeerCertificateChain()
throws SSLPeerUnverifiedException {
return null;
}
#Override
public java.security.cert.Certificate[] getPeerCertificates()throws SSLPeerUnverifiedException {
return peertCerts;
}
#Override
public String getPeerHost() {
throw new UnsupportedOperationException();
}
#Override
public int getPeerPort() {
return 0;
}
#Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return null;
//throw new UnsupportedOperationException();
}
#Override
public String getProtocol() {
throw new UnsupportedOperationException();
}
#Override
public SSLSessionContext getSessionContext() {
throw new UnsupportedOperationException();
}
#Override
public Object getValue(String arg0) {
throw new UnsupportedOperationException();
}
#Override
public String[] getValueNames() {
throw new UnsupportedOperationException();
}
#Override
public void invalidate() {
throw new UnsupportedOperationException();
}
#Override
public boolean isValid() {
throw new UnsupportedOperationException();
}
#Override
public void putValue(String arg0, Object arg1) {
throw new UnsupportedOperationException();
}
#Override
public void removeValue(String arg0) {
throw new UnsupportedOperationException();
}
};
}
#Override
public String[] getSupportedProtocols() {
return null;
}
#Override
public boolean getUseClientMode() {
return false;
}
#Override
public boolean getWantClientAuth() {
return false;
}
#Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener arg0) {
}
#Override
public void setEnableSessionCreation(boolean arg0) {
}
#Override
public void setEnabledCipherSuites(String[] arg0) {
}
#Override
public void setEnabledProtocols(String[] arg0) {
}
#Override
public void setNeedClientAuth(boolean arg0) {
}
#Override
public void setUseClientMode(boolean arg0) {
}
#Override
public void setWantClientAuth(boolean arg0) {
}
#Override
public String[] getSupportedCipherSuites() {
return null;
}
#Override
public void startHandshake() throws IOException {
Log.to("util").info("TSLSocketConnectionFactory:startHandshake()");
tlsClientProtocol.connect(new DefaultTlsClient() {
#SuppressWarnings("unchecked")
#Override
public Hashtable<Integer, byte[]> getClientExtensions() throws IOException {
Hashtable<Integer, byte[]> clientExtensions = super.getClientExtensions();
if (clientExtensions == null) {
clientExtensions = new Hashtable<Integer, byte[]>();
}
//Add host_name
byte[] host_name = host.getBytes();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DataOutputStream dos = new DataOutputStream(baos);
dos.writeShort(host_name.length + 3);
dos.writeByte(0); //
dos.writeShort(host_name.length);
dos.write(host_name);
dos.close();
clientExtensions.put(ExtensionType.server_name, baos.toByteArray());
return clientExtensions;
}
#Override
public TlsAuthentication getAuthentication()
throws IOException {
return new TlsAuthentication() {
#Override
public void notifyServerCertificate(Certificate serverCertificate) throws IOException {
try {
KeyStore ks = _loadKeyStore();
Log.to("util").info(">>>>>>>> KeyStore : "+ks.size());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<java.security.cert.Certificate> certs = new LinkedList<java.security.cert.Certificate>();
boolean trustedCertificate = false;
for ( org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCertificateList()) {
java.security.cert.Certificate cert = cf.generateCertificate(new ByteArrayInputStream(c.getEncoded()));
certs.add(cert);
String alias = ks.getCertificateAlias(cert);
if(alias != null) {
Log.to("util").info(">>> Trusted cert\n" + c.getSubject().toString());
if (cert instanceof java.security.cert.X509Certificate) {
try {
( (java.security.cert.X509Certificate) cert).checkValidity();
trustedCertificate = true;
Log.to("util").info("Certificate is active for current date\n"+cert);
} catch(CertificateExpiredException cee) {
R01FLog.to("r01f.util").info("Certificate is expired...");
}
}
} else {
Log.to("util").info(">>> Unknown cert " + c.getSubject().toString());
Log.to("util").fine(""+cert);
}
}
if (!trustedCertificate) {
throw new CertificateException("Unknown cert " + serverCertificate);
}
peertCerts = certs.toArray(new java.security.cert.Certificate[0]);
} catch (Exception ex) {
ex.printStackTrace();
throw new IOException(ex);
}
}
#Override
public TlsCredentials getClientCredentials(CertificateRequest arg0)
throws IOException {
return null;
}
/**
* Private method to load keyStore with system or default properties.
* #return
* #throws Exception
*/
private KeyStore _loadKeyStore() throws Exception {
FileInputStream trustStoreFis = null;
try {
String sysTrustStore = null;
File trustStoreFile = null;
KeyStore localKeyStore = null;
sysTrustStore = System.getProperty("javax.net.ssl.trustStore");
String javaHome;
if (!"NONE".equals(sysTrustStore)) {
if (sysTrustStore != null) {
trustStoreFile = new File(sysTrustStore);
trustStoreFis = _getFileInputStream(trustStoreFile);
} else {
javaHome = System.getProperty("java.home");
trustStoreFile = new File(javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "jssecacerts");
if ((trustStoreFis = _getFileInputStream(trustStoreFile)) == null) {
trustStoreFile = new File(javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts");
trustStoreFis = _getFileInputStream(trustStoreFile);
}
}
if (trustStoreFis != null) {
sysTrustStore = trustStoreFile.getPath();
} else {
sysTrustStore = "No File Available, using empty keystore.";
}
}
String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType")!=null?System.getProperty("javax.net.ssl.trustStoreType"):KeyStore.getDefaultType();
String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider")!=null?System.getProperty("javax.net.ssl.trustStoreProvider"):"";
if (trustStoreType.length() != 0) {
if (trustStoreProvider.length() == 0) {
localKeyStore = KeyStore.getInstance(trustStoreType);
} else {
localKeyStore = KeyStore.getInstance(trustStoreType, trustStoreProvider);
}
char[] keyStorePass = null;
String str5 = System.getProperty("javax.net.ssl.trustStorePassword")!=null?System.getProperty("javax.net.ssl.trustStorePassword"):"";
if (str5.length() != 0) {
keyStorePass = str5.toCharArray();
}
localKeyStore.load(trustStoreFis, (char[]) keyStorePass);
if (keyStorePass != null) {
for (int i = 0; i < keyStorePass.length; i++) {
keyStorePass[i] = 0;
}
}
}
return (KeyStore)localKeyStore;
} finally {
if (trustStoreFis != null) {
trustStoreFis.close();
}
}
}
private FileInputStream _getFileInputStream(File paramFile) throws Exception {
if (paramFile.exists()) {
return new FileInputStream(paramFile);
}
return null;
}
};
}
});
}
};//Socket
}
}
If you look at RFC 4492 5.2, you'll see that the server CAN send the "ec_point_formats" extension, but is only supposed to do so "when negotiating an ECC cipher suite". If you want TLSClient to just ignore the extra extension instead of raising an exception, I suggest overriding TlsClient.allowUnexpectedServerExtension(...) to allow ec_point_formats in the same way the default implementation allows elliptic_curves:
protected boolean allowUnexpectedServerExtension(Integer extensionType, byte[] extensionData)
throws IOException
{
switch (extensionType.intValue())
{
case ExtensionType.ec_point_formats:
/*
* Exception added based on field reports that some servers send Supported
* Point Format Extension even when not negotiating an ECC cipher suite.
* If present, we still require that it is a valid ECPointFormatList.
*/
TlsECCUtils.readSupportedPointFormatsExtension(extensionData);
return true;
default:
return super.allowUnexpectedServerExtension(extensionType, extensionData);
}
}
If this is a widespread problem, we might consider adding this case to the default implementation.
For logging, there are the (TLSPeer) methods notifyAlertRaised and notifyAlertReceived that you can override on your TLSClient implementation.
i am posting this so maybe some people might find this helpful - i was also getting invalid_parameter(47) exception from my BC client code.
this error started to happen when we upgraded BC from version 1.62 (bcprov- and bctls-jdk15on-162 JARs) to 1.71 (bcprov-, bctls-, bcpkix-, bcutil-jdk15to18-171).
my stacktrace was different from the above problem, though:
org.bouncycastle.tls.TlsFatalAlert: illegal_parameter(47)
at org.bouncycastle.tls.TlsClientProtocol.processServerHello(Unknown Source)
at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
...
I had some trouble figuring out how to debug the internal BC TLS code. Тhe provided JARs are compiled without the debug info - I had to download the source files from Maven Central and build from them, creating a new project in Netbeans with existing sources - the only Java version it can be built with is 8, but the end result could be referenced by my Java 6 client code.
Having compiled bctls with debug info, and referencing it from my client, I could actually land in the code that was throwing the exception:
protected void processServerHello(ServerHello serverHello)
throws IOException
{
...
if (sessionServerExtensions != null && !sessionServerExtensions.isEmpty())
{
{
/*
* RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
* and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
* ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
* client.
*/
boolean serverSentEncryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(sessionServerExtensions);
if (serverSentEncryptThenMAC && !TlsUtils.isBlockCipherSuite(securityParameters.getCipherSuite()))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
securityParameters.encryptThenMAC = serverSentEncryptThenMAC;
}
...
}
I don't really know what encrypt-then-MAC and MAC-then-encrypt is, and I didn't want to get deep into their meaning. After some searching, I figured out that possibly I had to limit the list of the cipher suites used for the communication - because possibly some cipher suite that is chosen by default forces the server into some condition where it behaves differently from the RFC specification - and yes, this at least worked:
TrustManagerFactory tmf = ...
SSLContext sc = SSLContext.getInstance("TLSv1.2", BouncyCastleJsseProvider.PROVIDER_NAME);
sc.init(null, tmf.getTrustManagers(), new SecureRandom());
SSLConnectionSocketFactory ret = new SSLConnectionSocketFactory(sc,
new String[] { "TLSv1.2" },
new String[] { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256" },
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// the created SSLConnectionSocketFactory is then used to create socketFactoryRegistry
// which is then passed to constructor of Apache HttpComponents' PoolingHttpClientConnectionManager
// which is used to create a new instance of Apache HttpClient
Other useful stuff that I learned is
there are sites, e.g. https://www.ssllabs.com/ssltest that analyze the available protocols and cipher suites that a server supports.
the list of the cipher suites above is not exactly arbitrary - I took it from this stackoverflow q&a.
Related
I am using LDAP authentication using Java . I am using the following code but its throwing error
Exception in thread "main" javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e, v2580
Java Code i am using
package test;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
public class LoginLDAP {
public static void main(String[] args) throws Exception {
Map<String,String> params = createParams(args);
// firstname.lastname#mydomain.com
// String domainName = params.get("domain"); // mydomain.com or empty
String url="ldap://ip here:389";
String principalName="username here";
String domainName ="domain name";
if (domainName==null || "".equals(domainName)) {
int delim = principalName.indexOf('#');
domainName = principalName.substring(delim+1);
}
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
props.put(Context.PROVIDER_URL, url);
props.put(Context.SECURITY_PRINCIPAL, principalName);
props.put(Context.SECURITY_CREDENTIALS, "password here");
if (url.toUpperCase().startsWith("LDAPS://")) {
props.put(Context.SECURITY_PROTOCOL, "ssl");
props.put(Context.SECURITY_AUTHENTICATION, "simple");
props.put("java.naming.ldap.factory.socket", "test.DummySSLSocketFactory");
}
InitialDirContext context = new InitialDirContext(props);
try {
SearchControls ctrls = new SearchControls();
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = context.search(toDC(domainName),"(& (userPrincipalName="+principalName+")(objectClass=user))", ctrls);
if(!results.hasMore())
throw new AuthenticationException("Principal name not found");
SearchResult result = results.next();
System.out.println("distinguisedName: " + result.getNameInNamespace() ); // CN=Firstname Lastname,OU=Mycity,DC=mydomain,DC=com
Attribute memberOf = result.getAttributes().get("memberOf");
if(memberOf!=null) {
for(int idx=0; idx<memberOf.size(); idx++) {
System.out.println("memberOf: " + memberOf.get(idx).toString() ); // CN=Mygroup,CN=Users,DC=mydomain,DC=com
//Attribute att = context.getAttributes(memberOf.get(idx).toString(), new String[]{"CN"}).get("CN");
//System.out.println( att.get().toString() ); // CN part of groupname
}
}
} finally {
try { context.close(); } catch(Exception ex) { }
}
}
/**
* Create "DC=sub,DC=mydomain,DC=com" string
* #param domainName sub.mydomain.com
* #return
*/
private static String toDC(String domainName) {
StringBuilder buf = new StringBuilder();
for (String token : domainName.split("\\.")) {
if(token.length()==0) continue;
if(buf.length()>0) buf.append(",");
buf.append("DC=").append(token);
}
return buf.toString();
}
private static Map<String,String> createParams(String[] args) {
Map<String,String> params = new HashMap<String,String>();
for(String str : args) {
int delim = str.indexOf('=');
if (delim>0) params.put(str.substring(0, delim).trim(), str.substring(delim+1).trim());
else if (delim==0) params.put("", str.substring(1).trim());
else params.put(str, null);
}
return params;
}
package test;
import java.io.*;
import java.net.*;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.*;
import javax.net.ssl.*;
public class DummySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory socketFactory;
public DummySSLSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{ new DummyTrustManager()}, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch ( Exception ex ){ throw new IllegalArgumentException(ex); }
}
public static SocketFactory getDefault() { return new DummySSLSocketFactory(); }
#Override public String[] getDefaultCipherSuites() { return socketFactory.getDefaultCipherSuites(); }
#Override public String[] getSupportedCipherSuites() { return socketFactory.getSupportedCipherSuites(); }
#Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException {
return socketFactory.createSocket(socket, string, i, bln);
}
#Override public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, i);
}
#Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, i, ia, i1);
}
#Override public Socket createSocket(InetAddress ia, int i) throws IOException {
return socketFactory.createSocket(ia, i);
}
#Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
return socketFactory.createSocket(ia, i, ia1, i1);
}
}
class DummyTrustManager implements X509TrustManager {
#Override public void checkClientTrusted(X509Certificate[] xcs, String str) {
// do nothing
}
#Override public void checkServerTrusted(X509Certificate[] xcs, String str) {
/*System.out.println("checkServerTrusted for authType: " + str); // RSA
for(int idx=0; idx<xcs.length; idx++) {
X509Certificate cert = xcs[idx];
System.out.println("X500Principal: " + cert.getSubjectX500Principal().getName());
}*/
}
#Override public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}
data 52e - Returns when username is valid but password/credential is invalid.
I am guessing that you are passing the password to Microsoft Active Directory in plain text which usually will not work.
We have a "updateUserPassword" method in our "JNDI Examples" repository that should help.
i want use java and ldaps to connect ldapService. but its wrong.
the problem is :
nested exception is javax.naming.CommunicationException: 192.168.174.145:636 [Root exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present]
if i use hostname:636 it is success . i do not know why .can you help me ? very thanks
public class SslLdapContextSource extends LdapContextSource {
#Override
protected Hashtable<String, Object> getAnonymousEnv() {
Hashtable<String, Object> anonymousEnv = super.getAnonymousEnv();
anonymousEnv.put("java.naming.security.protocol", "ssl");
anonymousEnv.put("java.naming.ldap.factory.socket", CustomSSLSocketFactory.class.getName());
anonymousEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
return anonymousEnv;
}
}
public class CustomSslSocketFactory extends SSLSocketFactory {
private SSLSocketFactory socketFactory;
public CustomSslSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{new DummyTrustmanager()}, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
public static SocketFactory getDefault() {
return new CustomSslSocketFactory();
}
#Override
public String[] getDefaultCipherSuites() {
return socketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return socketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket socket, Senter code heretring string, int num, boolean bool) throws IOException {
return socketFactory.createSocket(socket, string, num, bool);
}
#Override
public Socket createSocket(String string, int num) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, num);
}
#Override
public Socket createSocket(String string, int num, InetAddress netAdd, int i) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, num, netAdd, i);
}
#Override
public Socket createSocket(InetAddress netAdd, int num) throws IOException {
return socketFactory.createSocket(netAdd, num);
}
#Override
public Socket createSocket(InetAddress netAdd1, int num, InetAddress netAdd2, int i) throws IOException {
return socketFactory.createSocket(netAdd1, num, netAdd2, i);
}
public static class DummyTrustmanager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] cert, String string) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] cert, String string) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}
}
#Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSourceTarget());
}
#Bean
public LdapContextSource contextSourceTarget() {
if(!useSSL){
String urls = "ldap://"+url+":"+port;
LdapContextSource ldapContextSource = new LdapContextSource();
ldapContextSource.setUrl(urls);
//ldapContextSource.setBase(base);
ldapContextSource.setUserDn(username);
ldapContextSource.setPassword(password);
ldapContextSource.setReferral(referral);
ldapContextSource.afterPropertiesSet();
return ldapContextSource;
}else{
String urls = "ldaps://"+url+":"+port;
SslLdapContextSource contextSource = new SslLdapContextSource();
contextSource.setUrl(urls);
contextSource.setUserDn(username);
contextSource.setPassword(password);
contextSource.setPooled(false);
contextSource.afterPropertiesSet();
return contextSource;
}
}
i want use ldaps://192.168.174.145:636 to connect ldapService.but now i only can use ldaps://test:636 to connect ldapService.
192.168.174.145 and test is same computer
When an SSL/TLS connection is established various checks are performed as part of the server authentication.
I.e.
Is the server certificate valid?
Is the server certificate issued by a trusted certificate authority?
Does the certificate belong to the server the client is connecting too?
For the last check either the Subject CN of the Subject DN of the server cert or values of the Subject Alternative Name (IP / DNS) extension of the server cert are checked. Please also see RFC5280
I am using bouncy-castle library to make a TLS-Handshake with an Web-Server and grab the public certificate. Below is my code
private org.bouncycastle.asn1.x509.Certificate[] certificateList;
public static void main(String... args) {
new BCMain().testBCTLS();
}
private void testBCTLS() {
try {
Socket s = new Socket(InetAddress.getByName(WEB_SERVER), WEB_SERVER_PORT);
//TlsProtocolHandler tlsHandler = new TlsProtocolHandler(s.getInputStream(), s.getOutputStream());
TlsClientProtocol protocol = new TlsClientProtocol(s.getInputStream(), s.getOutputStream(), new SecureRandom());
TlsClient client = new DefaultTlsClient() {
private Boolean connectionStatus = Boolean.FALSE;
#Override
public TlsAuthentication getAuthentication() throws IOException {
return new ServerOnlyTlsAuthentication() {
public void notifyServerCertificate(Certificate serverCertificate)
throws IOException {
certificateList = serverCertificate.getCertificateList();
}
};
}
#Override
public Hashtable getClientExtensions() throws IOException {
Hashtable clientExtensions = super.getClientExtensions();
clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);
Vector<ServerName> serverNames = new Vector(1);
serverNames.add(new ServerName(NameType.host_name, SNI_HOST_NAME));
TlsExtensionsUtils.addServerNameExtension(clientExtensions, new ServerNameList(serverNames));
return clientExtensions;
}
public Boolean getConnectionStatus() {
return connectionStatus;
}
};
protocol.connect(client);
if (this.certificateList!=null) {
org.bouncycastle.asn1.x509.Certificate certificate = certificateList[0];
System.out.println(certificate.getSubject());
}
InputStream is = protocol.getInputStream();
System.out.println(is);
} catch (Exception e) {
e.printStackTrace();
}
}
I wanted to extract Subject Alternative Names from that Public certificate
The X509Certificate of JDK has method to extract SubjectAlternativeNames .. But I want to get the same from the bouncy-castle certificate.
Can some one help on this please ?
I was able to extract Subject-Alternative-Names using X509CertificateHolder and JcaX509CertificateConverter classes from BouncyCastle Library .. In continuation to the above code
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
if (this.certificateList!=null) {
org.bouncycastle.asn1.x509.Certificate certificate = certificateList[0];
X509CertificateHolder holder = new X509CertificateHolder(certificate.getEncoded());
X509Certificate x509Certificate = new JcaX509CertificateConverter().getCertificate(holder);
Collection<List<?>> sanCollections = x509Certificate.getSubjectAlternativeNames();
}
I am trying to use bouncy castle for DTLS Handshake.
I have generated key by following this link. I am working by extending DefaultTlsClient. It can generate client_hello packet. But when the server_hello packet arrives it gives org.bouncycastle.crypto.tls.TlsFatalAlert: internal_error(80) Caused by: java.lang.IllegalArgumentException: unknown HashAlgorithm. Can anyone give any hint?
Update:
From Wireshark: In the Certificate Request, there are 9 Signature Hash Algorithms. One of them is rsa_pss_sha256(0x0804). In the public static Digest createHash(short hashAlgorithm) function in TlsUtils.java there is no matching for it. That's why it is giving Unknown hash Algorithm. What does that mean? Using Bouncy Castle, is it possible to establish DTLS with that server?
Here is the code for loading the keystore:
public static void initKeyStore() {
char password[] = "bbtone".toCharArray();
if( !isKeystoreLoaded) {
try {
FileInputStream fis = new FileInputStream("bbtone");
KeyMgmt key = new KeyMgmt();
key.open(fis, password);
fis.close();
crt = key.getCRT("bbtone").getEncoded();
fingerprintSHA256 = KeyMgmt.fingerprintSHA256(crt);
ArrayList<byte[]> chain = new ArrayList<byte[]>();
chain.add(crt);
java.security.cert.Certificate root = key.getCRT("root");
if (root != null) {
chain.add(root.getEncoded());
}
privateKey = key.getKEY("bbtone", password).getEncoded();
initDTLS(chain, privateKey, false);
isKeystoreLoaded = true;
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Generating private key and certificate:
public static boolean initDTLS(java.util.List<byte []> certChain, byte privateKey[], boolean pkRSA) {
try {
org.bouncycastle.asn1.x509.Certificate x509certs[] = new org.bouncycastle.asn1.x509.Certificate[certChain.size()];
for (int i = 0; i < certChain.size(); ++i) {
x509certs[i] = org.bouncycastle.asn1.x509.Certificate.getInstance(certChain.get(i));
}
dtlsCertChain = new org.bouncycastle.crypto.tls.Certificate(x509certs);
if (pkRSA) {
RSAPrivateKey rsa = RSAPrivateKey.getInstance(privateKey);
dtlsPrivateKey = new RSAPrivateCrtKeyParameters(rsa.getModulus(), rsa.getPublicExponent(),
rsa.getPrivateExponent(), rsa.getPrime1(), rsa.getPrime2(), rsa.getExponent1(),
rsa.getExponent2(), rsa.getCoefficient());
} else {
dtlsPrivateKey = PrivateKeyFactory.createKey(privateKey);
}
return true;
} catch (Exception e) {
return false;
}
}
Starting DTLS Handshake:
public void startDTLS() {
socket.connect(recvPacket.getAddress(), recvPacket.getPort());
try {
dtlsClient = new DTLSClientProtocol(new SecureRandom());
} catch (Exception e) {
e.printStackTrace();
dtlsClient = null;
return;
}
tlsClient = new DefaultTlsClient2() {
protected TlsSession session;
public TlsSession getSessionToResume()
{
return this.session;
}
public ProtocolVersion getClientVersion() {
return ProtocolVersion.DTLSv12;
}
public ProtocolVersion getMinimumVersion() {
return ProtocolVersion.DTLSv10;
}
public Hashtable getClientExtensions() throws IOException {
//see : http://bouncy-castle.1462172.n4.nabble.com/DTLS-SRTP-with-bouncycastle-1-49-td4656286.html
logger.debug("Extending getClientExtensions\n");
Hashtable table = super.getClientExtensions();
if (table == null) table = new Hashtable();
//adding the protection profiles
int[] protectionProfiles = {
SRTPProtectionProfile.SRTP_AES128_CM_HMAC_SHA1_80 //this is the only one supported for now
// SRTPProtectionProfile.SRTP_AES128_CM_HMAC_SHA1_32
// SRTPProtectionProfile.SRTP_NULL_HMAC_SHA1_32
// SRTPProtectionProfile.SRTP_NULL_HMAC_SHA1_80
};
byte mki[] = new byte[0]; //do not use mki
UseSRTPData srtpData = new UseSRTPData(protectionProfiles, mki);
TlsSRTPUtils.addUseSRTPExtension(table, srtpData);
return table;
}
public TlsAuthentication getAuthentication() throws IOException {
return new TlsAuthentication() {
public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
throws IOException
{
//info only
}
public TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
throws IOException
{
short[] certificateTypes = certificateRequest.getCertificateTypes();
if (certificateTypes == null) return null;
boolean ok = false;
for(int a=0;a<certificateTypes.length;a++) {
if (certificateTypes[a] == ClientCertificateType.rsa_sign) {
ok = true;
break;
}
}
if (!ok) return null;
SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
Vector sigAlgs = certificateRequest.getSupportedSignatureAlgorithms();
if (sigAlgs != null)
{
for (int i = 0; i < sigAlgs.size(); ++i)
{
SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm) sigAlgs.elementAt(i);
if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
{
signatureAndHashAlgorithm = sigAlg;
break;
}
}
if (signatureAndHashAlgorithm == null)
{
return null;
}
}
return new DefaultTlsSignerCredentials(context, dtlsCertChain, dtlsPrivateKey, signatureAndHashAlgorithm);
}
};
}
public void notifyHandshakeComplete() throws IOException
{
logger.debug("SRTPChannel:DTLS:Client:Handshake complete");
super.notifyHandshakeComplete();
TlsSession newSession = context.getResumableSession();
if (newSession != null)
{
this.session = newSession;
}
getKeys();
}
};
try {
logger.debug("SRTPChannel:connecting to DTLS server");
dtlsTransport = dtlsClient.connect(tlsClient, new UDPTransport(socket, 1500 - 20 - 8));
} catch (Exception e) {
e.printStackTrace();
}
}
Error:
org.bouncycastle.crypto.tls.TlsFatalAlert: internal_error(80)
at org.bouncycastle.crypto.tls.DTLSClientProtocol.connect(DTLSClientProtocol.java:75)
at processor.ClientMediaHandler.startDTLS(ClientMediaHandler.java:459)
at processor.ClientMediaHandler.run(ClientMediaHandler.java:538)
Caused by: java.lang.IllegalArgumentException: unknown HashAlgorithm
at org.bouncycastle.crypto.tls.TlsUtils.createHash(TlsUtils.java:1184)
at org.bouncycastle.crypto.tls.DeferredHash.checkTrackingHash(DeferredHash.java:203)
at org.bouncycastle.crypto.tls.DeferredHash.trackHashAlgorithm(DeferredHash.java:68)
at org.bouncycastle.crypto.tls.TlsUtils.trackHashAlgorithms(TlsUtils.java:1358)
at org.bouncycastle.crypto.tls.DTLSClientProtocol.clientHandshake(DTLSClientProtocol.java:241)
at org.bouncycastle.crypto.tls.DTLSClientProtocol.connect(DTLSClientProtocol.java:60)
... 2 more
I have solved the problem using DTLS1.0. It can now finish the handshake.
I replaced
public ProtocolVersion getClientVersion() {
return ProtocolVersion.DTLSv12;
}
with the following code:
public ProtocolVersion getClientVersion() {
return ProtocolVersion.DTLSv10;
}
#Rashed, downgrading TLS version is not a good choice. There is already comment from #bbaldino but I think it should be added as answer - version 1.60 of bouncy castle has a bug. Instead of downgrading TLS, you should upgrade bouncy castle to at least 1.61
https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
I'm doing research on GenericObjectPool by putting Cipher in pool so it can be reused.
GenericObjectPool<Cipher> pool;
CipherFactory factory = new CipherFactory();
this.pool = new GenericObjectPool<Cipher>(factory);
pool.setMaxTotal(10);
pool.setBlockWhenExhausted(true);
pool.setMaxWaitMillis(30 * 1000);
CipherFactory
public class CipherFactory extends BasePooledObjectFactory<Cipher> {
private boolean running = false;
#Override
public Cipher create() throws Exception {
return Cipher.getInstance("DESede/CBC/NoPadding");
}
#Override
public PooledObject<Cipher> wrap(Cipher arg0) {
return new DefaultPooledObject<Cipher>(arg0);
}
#Override
public boolean validateObject(PooledObject<Cipher> p) {
//Ensures that the instance is safe to be returned by the pool
return true;
}
#Override
public void destroyObject(PooledObject<Cipher> p) {
//Destroys an instance no longer needed by the pool.
System.out.println("destroying");
}
#Override
public void activateObject(PooledObject<Cipher> p) throws Exception { //Reinitialize an instance to be returned by the pool
setRunning(true);
}
#Override
public void passivateObject(PooledObject<Cipher> p) throws Exception { // reset the object after the object returns to the pool
setRunning(false);
}
public void setRunning(boolean running) {
this.running = running;
}
//
}
This is how I implement ObjectPool in my Example class
public Key a(byte[] afyte) throws Exception {
Cipher cipher = null;
cipher = pool.borrowObject(); //get the object from the pool
try {
System.out.println("****************** After borrow ****************");
printPool();
cipher.init(Cipher.DECRYPT_MODE, mkkey, algParamSpec);
byte[] de = cipher.doFinal(afyte);
SecretKey mk = new SecretKeySpec(de, "DESede");
return mk;
} catch (Exception e) {
pool.invalidateObject(cipher);
cipher = null;
} finally {
if (null != cipher) {
pool.returnObject(cipher);
System.out.println("****************** After return ****************");
printPool();
}
}
return (Key) cipher;
}
printPool
public void printPool() {
System.out.println("Pool for cipher with instances DESede/CBC/NoPadding");
System.out.println("Active [" + pool.getNumActive() + "]"); //Return the number of instances currently borrowed from this pool
System.out.println("Idle [" + pool.getNumIdle() + "]"); //The number of instances currently idle in this pool
System.out.println("Total Created [" + pool.getCreatedCount() + "]");
}
Am I on the right path ? Is it possible to increase pool size ?
Edit
The answer from #http works fine to me. But if I have another method encryptECB(Key key, byte[] b), how should I write ?
Any help would be appreciated !
You are on the right track. When constructing the GenericObjectPool, you can use the constructor that accepts a GenericObjectPoolConfig object which contains all the configuration values for your object pool. The example below would let your pool grow to 20 connections before it was exhausted...
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMinIdle(2);
config.setMaxIdle(5);
config.setMaxTotal(20);
GenericObjectPool<Cipher> pool;
CipherFactory factory = new CipherFactory();
this.pool = new GenericObjectPool<Cipher>(factory, config);
GenericeObjectPoolConfig also has a setBlockWhenExhausted method to specify the behaviour when the pool has reached the maxTotal connections. See https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/BaseObjectPoolConfig.html#setBlockWhenExhausted-boolean- for details.
A pattern I implement when using commons pool is to create 2 interfaces, one for your pooled object and one for your factory...
public interface PooledCipher extends java.io.Closeable {
byte[] doFinal(byte[] bytes) throws Exception;
SecretKeySpec getSecretKeySpec(byte[] bytes) throws Exception;
}
public interface CipherFactory {
PooledCipher getCipher() throws Exception;
void close();
}
CipherFactory implementation...
public class CipherFactoryImpl extends BasePooledObjectFactory<PooledCipher>
implements CipherFactory {
private final GenericObjectPoolConfig config;
private final GenericObjectPool<PooledCipher> pool;
private final String transformation;
private final int opmode;
private final Key key;
private final AlgorithmParameters params;
private final String secretKeySpecAlgorithm;
public CipherFactoryImpl(GenericObjectPoolConfig config, String transformation, int opmode, Key key, AlgorithmParameters params, String secretKeySpecAlgorithm) {
this.config = config;
this.pool = new GenericObjectPool<PooledCipher>(this, config);
this.transformation = transformation;
this.opmode = opmode;
this.key = key;
this.params = params;
this.secretKeySpecAlgorithm = secretKeySpecAlgorithm
}
#Override
public PooledCipher create() throws Exception {
return new PooledCipherImpl(pool, transformation, opmode, key, params, secretKeySpecAlgorithm);
}
#Override
public PooledCipher getCipher() throws Exception {
return pool.borrowObject();
}
#Override
public void destroyObject(PooledObject<PooledCipher> p) throws Exception {
try {
PooledCipherImpl cipherImpl = (PooledCipherImpl)p.getObject();
// do whatever you need with cipherImpl to destroy it
} finally {
super.destroyObject(p);
}
}
#Override
public void close() {
pool.close();
}
#Override
public PooledObject<PooledCipher> wrap(PooledCipher cipher) {
return new DefaultPooledObject<PooledCipher>(cipher);
}
}
PooledCipher implementation...
public class PooledCipherImpl implements PooledCipher {
private final ObjectPool<PooledCipher> pool;
private final Cipher cipher;
private final String secretKeySpecAlgorithm;
private boolean destroyOnClose = false;
public PooledCipherImpl(ObjectPool<PooledCipher> pool, String transformation, int opmode, Key key, AlgorithmParameters params, String secretKeySpecAlgorithm) {
this.pool = pool;
this.cipher = Cipher.getInstance(transformation);
this.cipher.init(opmode, key, params);
this.secretKeySpecAlgorithm = secretKeySpecAlgorithm;
}
#Override
public byte[] doFinal(byte[] bytes) throws Exception {
try {
return cipher.doFinal(bytes);
} catch (Exception e) {
destroyOnClose = true;
throw e;
}
}
#Override
public SecretKeySpec getSecretKeySpec(byte[] bytes) {
return new SecretKeySpec(doFinal(bytes), secretKeySpecAlgorithm);
}
#Override
public void close() throws IOException {
try {
if (destroyOnClose) {
pool.destroyObject(this);
} else {
pool.returnObject(this);
}
} catch (Exception e) {
throw new IOException(e);
}
}
}
Then you construct your CipherFactory like this...
String transformation = "DESede/CBC/NoPadding";
String secretKeySpecAlgorithm = "DESede";
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// set up the poolConfig here
poolConfig.setMaxTotal(20);
CipherFactory cipherFactory = new CipherFactoryImpl(poolConfig, transformation, Cipher.DECRYPT_MODE, mkkey, algParamSpec, secretKeySpecAlgorithm);
And use it like this...
public Key unwrapKey(byte[] tmkByte) throws Exception {
try (PooledCipher cipher = cipherFactory.getCipher()) {
return cipher.getSecretKeySpec(tmkByte);
}
}
Also you can reuse the PooledCipher and CipherFactory interfaces to create other implementations, such as JCA.