Java – httpclient 403 error when using valid client certificate

I'm trying to automate some tasks on the website using Java I have a valid client for the website (works when I log in using Firefox), but when I try to log in using the HTTP client, I always receive 403 errors Please note that I want my trust store to trust anything (I know it's not safe, but I'm not worried at this time)

This is my code:

KeyStore keystore = getKeyStore();//Implemented somewhere else and working ok
    String password = "changeme";

    SSLContext context = SSLContext.getInstance("SSL");
    KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmfactory.init(keystore,password.tocharArray());
    context.init(kmfactory.getKeyManagers(),new TrustManager[] { new x509trustmanager() {
        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] arg0,String arg1)
                throws CertificateException {
        }
        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] arg0,String arg1)
                throws CertificateException {
        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    } },new SecureRandom());
    SSLSocketFactory sf = new SSLSocketFactory(context);

    Scheme httpsScheme = new Scheme("https",443,sf);
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(httpsScheme);
    ClientConnectionManager cm = new SingleClientConnManager(schemeRegistry);
    HttpClient client = new DefaultHttpClient(cm);

    HttpGet get = new HttpGet("https://theurl.com");
    HttpResponse response = client.execute(get);
    System.out.println(EntityUtils.toString(response.getEntity(),"UTF-8"));

The last statement prints a 403 error What did I miss here?

Solution

Think of my own 403 error because Java SSL did not select my client certificate

I debugged the SSL handshake and found that the server requires a client certificate issued by the permission list, and the issuer of my client certificate is not in the list Therefore, Java SSL cannot find the appropriate certificate in my keystore It seems that the way web browser and Java implement SSL is slightly different, because my browser will actually ask me which certificate to use, no matter what the server certificate requires in terms of the issuer of the client certificate

In this case, the server certificate is the responsibility It is self - signed and the list of publishers it notifies is incomplete This is not perfect with the Java SSL implementation But the server is not mine. I can do nothing but complain about the Brazilian government (their server) No further expiration, this is my job:

First, I used a trustmanager that trusts everything (as I did in my question):

public class MyTrustManager implements x509trustmanager {
    public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {
    }
    public void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException {
    }
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

Then I implemented a key manager that always uses the pkcs12 (. PFX) certificate key I want:

public class MyKeyManager extends X509ExtendedKeyManager {

KeyStore keystore = null;
String password = null;
public MyKeyManager(KeyStore keystore,String password) {
        this.keystore = keystore;
        this.password = password;
}

@Override
public String chooseClientAlias(String[] arg0,Principal[] arg1,Socket arg2) {
    return "";//can't be null
}

@Override
public String chooseServerAlias(String arg0,Socket arg2) {
    return null;
}

@Override
public X509Certificate[] getCertificateChain(String arg0) {
    try {
        X509Certificate[] result = new X509Certificate[keystore.getCertificateChain(keystore.aliases().nextElement()).length];
        for (int i=0; i < result.length; i++){
            result[i] = (X509Certificate) keystore.getCertificateChain(keystore.aliases().nextElement())[i];
        }
        return result ;
    } catch (Exception e) {
    }
    return null;
}

@Override
public String[] getClientAliases(String arg0,Principal[] arg1) {
    try {
        return new String[] { keystore.aliases().nextElement() };
    } catch (Exception e) {
        return null;
    }
}

@Override
public PrivateKey getPrivateKey(String arg0) {
    try {
        return ((KeyStore.PrivateKeyEntry) keystore.getEntry(keystore.aliases().nextElement(),new KeyStore.PasswordProtection(password.tocharArray()))).getPrivateKey();
    } catch (Exception e) {
    }
    return null;
}

@Override
public String[] getServerAliases(String arg0,Principal[] arg1) {
    return null;
}

}

If my PFX also contains its issuer certificate, this will be valid But it didn't (yeah!) Therefore, when I use the above key manager, I receive an SSL handshake error (the peer is not authenticated) If the client sends a certificate chain trusted by the server, the server authenticates the client only Since my certificate (issued by Brazilian institutions) does not include its issuer, its certificate chain only includes itself The server does not like this and refuses to authenticate the client The solution is to manually create a certificate chain:

...
@Override
    //The order matters,your certificate should be the first one in the chain,its issuer the second,its issuer's issuer the third and so on.
public X509Certificate[] getCertificateChain(String arg0) {
            X509Certificate[] result = new X509Certificate[2];
            //The certificate chain contains only one entry in my case
            result[0] = (X509Certificate) keystore.getCertificateChain(keystore.aliases().nextElement())[0];
            //Implement getMyCertificateIssuer() according to your needs. In my case,I read it from a JKS keystore from my database
            result[1] = getMyCertificateIssuer();
            return result;
}
...

After that, just use my custom key and trust manager:

InputStream keystoreContents = null;//Read it from a file,a byte array or whatever floats your boat
            KeyStore keystore = KeyStore.getInstance("PKCS12");
            keystore.load(keystoreContetns,"changeme".tocharArray());
            SSLContext context = SSLContext.getInstance("TLSv1");
            context.init(new KeyManager[] { new MyKeyManager(keystore,"changeme") },new TrustManager[] { new MyTrustManager() },new SecureRandom());
            SSLSocketFactory sf = new SSLSocketFactory(context);
            Scheme httpsScheme = new Scheme("https",sf);
            SchemeRegistry schemeRegistry = new SchemeRegistry();
            schemeRegistry.register(httpsScheme);
            ClientConnectionManager cm = new SingleClientConnManager(schemeRegistry);
            HttpClient client = new DefaultHttpClient(cm);
            HttpPost post = new HttpPost("https://www.someserver.com");
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>