Maven, Ant and sbt, how to pull from a local SSL repo?

One question I'm getting a lot is from people inside offices that use a proxy. Your company will have installed a Root CA on Windows, but Maven/sbt will complain because they use a different trust store. The easiest way to fix it is to make your build tools trust your Windows CA.

-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT
Edit %USERPROFILE%/.mvn/maven.config and add the above line
-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT
Edit c:\Program Files (x86)\sbt\conf\sbtconfig.txt or appropriate installation path

That should cover it. Follow below to learn how to add your own CA to java.

We covered how to put Apache Archiva behind HAProxy with SSL termination. Now we’re covering how to configure your tools of choice to query data from that repository.

Remember that we learned how to install a local Certificate Authority that would make your internal certificates work transparently? Bad news. Java doesn’t trust Windows Trusted Root store, it ships out with its own.

You’ll have to add your Certificate Authority to the Java store. These are AdoptOpenJDK 8 and 11 paths but it’s similar to most Java implementations.

Java CA store is present in a file called cacerts that you can find in the following places:

AdoptOpenJDK\jdk-8.0.252-hotspot\jre\lib\security\cacerts
AdoptOpenJDK\jdk-11.0.6.10\lib\security\cacerts

Now that you’ve pinpointed the location of the store we’re going back to openssl to convert your Certificate Authority certificate to the der format (Distinguished Encoding Rules) that Java prefers.

Keytool to the rescue!

Keytool comes with Java and is the default tool to deal with certificates Java-side.

# list certificates present in this Java keystore
keytool -list -v -keystore lib\cacerts

# show detailed information on a cert by alias
keytool -list -v -keystore lib\cacerts -alias xxxx

Great! Keytool is working so let’s convert the certificate you have for your Certificate Authority ca_key.crt and convert it to der.

# convert your pem CA certificate to der format
openssl x509 -outform der -in ca_cert.pem -out ca_cert.der

# import your CA to Java CA store
keytool -import -file ca_cert.der -keystore lib\cacerts

Note that AdoptOpenJDK default store password is: changeit.

Now that we have Java talking proper SSL to your artifacts repository, let’s configure your editor of choice.

Build Tools by Chris Ralston

Build Tools

For the following examples we’re going to assume that HAProxy is running on 10.1.1.2 and on port 8888 dispatching to an Archiva backend. We’re also assuming that you’ve setup a repository in Archiva called acme-company-repo.

Maven

Maven pretty much takes care of everything by itself. You can set up repositories in pom.xml like this:

<!-- to pull internal artifacts from -->
<repositories>
    <repository>
        <id>acme-company-repo</id>
        <name>ACME Private Repo</name>
        <url>https://10.0.0.2/repository/acme-company-repo</url>
    </repository>
</repositories>
    
<!-- if you're also distributing your project -->
<distributionManagement>
    <id>acme-company-repo</id>
    <name>ACME Private Repo</name>
    <url>https://10.0.0.2/repository/acme-company-repo</url>
</distributionManagement>    

Assuming that the company repository will be password protected, at least for distribution, you should add credentials to your %HOMEPATH%\.m2\settings.xml:

<settings>
    ...
    <servers>
        <server>
            <id>acme-company-repo</id>
            <username>your-username</username>
            <password>your-password</password>
        </server>
    </servers>
</settings>

That’s it! Maven will work fine out of the box.


Ant + Ivy2

As you probably know Apache Ant uses Apache Ivy to support repository management.

For Ivy we’re going to use 3 files, plus build.xml:

1) ivy.xml – list dependencies
2) build.xml – build file with “target” for ivy to get the dependencies
3) ivysettings.xml – define format on how dependencies are stored
4) %HOMEPATH%\.ivy2\repo.properties – to store password

ivy.xml – stores project dependencies:

<ivy-module version="2.0">
    <info organisation="org.yourcompany" module="your-module"/>
    <configurations>
        <conf name="compile" description="Required to compile application"/>
        <conf name="test" description="Required for test only"/>	 	
    </configurations>
        
    <dependencies>
        <!-- required for compiling -->
        <dependency org="com.acme" name="internal-utils" rev="1.2.3" conf="compile->default"/>
        <dependency org="joda-time" name="joda-time" rev="2.9.9" conf="compile->default"/>
            
        <!-- required just for testing -->
        <dependency org="junit" name="junit" rev="4.12" conf="test->default"/>
        <dependency org="org.jmockit" name="jmockit" rev="1.33" conf="test->default"/>
    </dependencies>
</ivy-module>
ivy.xml

build.xml – your project build file now has a resolve target to get the dependencies:

<property file="${user.home}/.ivy2/repo.properties"/>
<target name="resolve" description="retrieve dependencies with ivy">
    <ivy:settings file="ivysettings.xml">
        <credentials host="10.1.1.2" realm="Repository Archiva Managed acme-company-repo Repository" username="${repo.username}" passwd="${repo.password}"/>
    </ivy:settings>

    <ivy:retrieve pattern="${basedir}/lib/[artifact]-[revision].[ext]" conf="compile"/>
    <ivy:retrieve pattern="${basedir}/lib-test/[artifact]-[revision].[ext]" conf="test"/>
</target>
build.xml

Notice that compile dependencies are downloaded to ./lib and testing dependencies to ./lib-test.
Also notice that we link to repo.properties on your user to store login details to repository.

ivysettings.xml – defines how dependencies are stored in your internal repository:

<ivysettings>
    <settings defaultResolver="acme-archiva-repo"/>
    <resolvers>
        <chain name="acme-archiva-repo">
            <ibiblio name="maven2" m2compatible="true"/>
            <ibiblio name="archiva" m2compatible="true" usepoms="true" pattern="[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]" root="http://10.1.1.2:8888/repository/acme-company-repo/"/>
        </chain>
    </resolvers>
</ivysettings>
ivysettings.xml

And the last file %HOMEPATH%.ivy2\repo.properties serves to store your access credentials out of source control.

repo.username=my-username
repo.password=my-password
repo.properties (password always stay out of source control)

That’s all! While a bit more involved you can just do ant resolve and get all the dependencies for your project from the internal server repository.


sbt

Scala’s interactive build tool can also pickup from your internal repository.

build.sbt – where we store our build instructions:

libraryDepdendencies ++= Seq(
    "joda-time" % "joda-time" % "2.9.9"

resolvers += (
    "Repository Archiva Managed acme-company-repo Repository" at "https://10.1.1.2:8888/repository/acme-internal-repo"
)

Notice that the repository name must match Archiva’s realm. The rule of thumb is that archiva will place “Repository Archiva Managed ### Repository”.

%HOMEPATH%.sbt\credentials.sbt, we use a single file for credentials and then link different sbt versions to this file. This ensures credentials are only on one file making everything tighter. Password will also be linked to repository and not to sbt version:

realm="Repository Archiva Managed acme-company-repo Repository"
host=10.1.1.2
user=my-username
password=my-password

%HOMEPATH%.sbt\0.13\plugins\credentials.sbt to link the credentials from sbt 0.13 to common file:

credentials += Credentials(Path.userHome / ".sbt" / "credentials.sbt")

You must create a credentials.sbt file on all sbt versions you’re using.

That’s all! You can start repo’ing away!

If this was helpful in any way, please leave a comment. Very interested to know how we may improve and what we’re going ok. Thanks!