HTTPS in Undertow - servlets

I have a web service that runs on Tomcat and decided to try using Undertow instead. Generally, it's quite good, but I need HTTPS support and cannot enable it. Here is some helloworld code:
DeploymentInfo servletBuilder = deployment()
.setClassLoader(ServletServer.class.getClassLoader())
.setContextPath("/")
.setDeploymentName("AuthenticationService.war")
.addServlet(servlet("WSServlet", WSServlet.class))
.addListener(listener(WSServletContextListener.class))
.setResourceManager(new FileResourceManager(new File("src/main/webapp"), 100));
DeploymentManager manager = defaultContainer().addDeployment(servletBuilder);
manager.deploy();
HttpHandler servletHandler = manager.start();
SSLContext context = createSSLContext(loadKeyStore("server-keystore.jsk"), loadKeyStore("server-truststore.jks"));
PathHandler path = Handlers.path(servletHandler);
Undertow server = Undertow.builder()
.addHttpsListener(8443, "localhost", context)
.setHandler(path)
.build();
server.start();
And the createSSLContext method:
private static SSLContext createSSLContext(final KeyStore keyStore, final KeyStore trustStore) throws Exception {
KeyManager[] keyManagers;
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, /* password */);
keyManagers = keyManagerFactory.getKeyManagers();
TrustManager[] trustManagers;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
And loadKeyStore method:
private static KeyStore loadKeyStore(String name) throws Exception {
final InputStream stream = new FileInputStream(name);
try(InputStream is = stream) {
KeyStore loadedKeystore = KeyStore.getInstance("JKS");
loadedKeystore.load(is, /* password */);
return loadedKeystore;
}
}
The server is starting, but trying to send requests to https://localhost:8443/... has no effect, no logs or exceptions or some reaction. When using http://localhost:8443 it throws exception
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
I am new to all web technologies, so all that may be strange. So what's wrong here?

I have it working. First generate your keys if you've not done so already:
#!/usr/bin/env bash
KEYSTORE=my-keystore.jks
TRUSTSTORE=my-truststore.ts
CERT=my-client.cer
rm $KEYSTORE
rm $CERT
rm $TRUSTSTORE
# these passwords must be copied to the private config file(s)
KEYSTORE_STOREPASS=password1
KEYSTORE_KEYPASS=password2
TRUSTSTORE_STOREPASS=password3
keytool -genkey -alias pomochatserver \
-keyalg RSA -keystore $KEYSTORE \
-dname "cn=localhost, ou=IT, o=Continuent, c=US" \
-storepass $KEYSTORE_STOREPASS -keypass $KEYSTORE_KEYPASS
# enter the *KEYSTORE_STOREPASS* when prompted
keytool -export -alias pomochatserver -file $CERT -keystore $KEYSTORE
keytool -import -trustcacerts -alias pomochatserver -file $CERT \
-keystore $TRUSTSTORE -storepass $TRUSTSTORE_STOREPASS -noprompt
... then in your java code:
public static SSLContext serverSslContext(String password1, String password2, String password3) {
try {
KeyStore keyStore = loadKeyStore("my-keystore.jks", password1);
KeyStore trustStore = loadKeyStore("my-truststore.ts", password3);
return createSSLContext(keyStore, trustStore, password2);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static SSLContext createSSLContext(final KeyStore keyStore, final KeyStore trustStore, String keyStorePassword) throws IOException {
try {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
return sslContext;
} catch (Exception e) {
throw new IOException("Unable to create and initialise the SSLContext", e);
}
}
private static KeyStore loadKeyStore(final String name, String password) throws IOException {
try(InputStream stream = new FileInputStream(name)) {
KeyStore loadedKeystore = KeyStore.getInstance("JKS");
loadedKeystore.load(stream, password.toCharArray());
return loadedKeystore;
} catch (Exception e) {
throw new IOException(String.format("Unable to load KeyStore %s", name), e);
}
}
and finally to create the server
Undertow webAppServer = Undertow.builder()
.addHttpsListener(
port,
hostname,
serverSslContext(password1, password2, password3)
)
.setHandler(handlers, manager.start()))
.build();

I'm not sure, but the self-signed and/or not trusted certificates would cause the issue, but you are supposed to get some exception asbout this
I don't know know what is undertow, but you may check the SSL configs from the doc.
You also need to check if both the server and client supports common cipher(s) and protocol(s)
For instance default package of JRE does not support AES_256.

Related

x509Certificate2 returning null - ssl certificate

I have installed ssl certificate and but when I use certCollection.Find by Extension but it's returning null.
private X509Certificate2 GetCertificateFromStore()
{
X509Certificate2 x509Certificate2;
var aspNetCoreEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (string.Equals(aspNetCoreEnvironment, "Development", StringComparison.OrdinalIgnoreCase))
{
const string aspNetHttpsOid = "1.3.1.1.3.1.1.84.1.1";
const string cnName = "CN=localhost";
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates;
var currentCerts = certCollection.Find(X509FindType.FindByExtension, aspNetHttpsOid, true);
currentCerts = currentCerts.Find(X509FindType.FindByIssuerDistinguishedName, cnName, true);
x509Certificate2 = currentCerts.Count == 0 ? null : currentCerts[0];
}
}
}
Note: I have created a self-signed development certificate with below commands.
PS C:\program files\microsoft sdks\service fabric\clustersetup\secure> .\CertSetup.ps1 -Install -CertSubjectName CN=mytestcert
after installing i can see certificate is created with added into Trusted root.
Am I making some mistake in creating self signed certificate. I am not sure.
You have stored the certificate in StoreLocation.CurrentUser but you code is looking in StoreLocation.LocalMachine.
Also, don't forget to the add proper ACL's.

How to load a certificate with private key from PEM files in .NET standard

I'm trying to load an X509Certificate2 from PEM files in a .NET standard library.
I created a self-signed certificate using openssl like so:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj /CN=localhost -days 365
I loaded the resulting PEM files into embedded string resources within the project and am trying to load them with the following code:
private X509Certificate2 GetCertificate()
{
try
{
byte[] pubPem = System.Text.Encoding.UTF8.GetBytes(Properties.Resources.DefaultPublicPem.Trim());
var cert = new X509Certificate2(pubPem);
var rsa = GetRSAFromPem(Properties.Resources.DefaultPrivatePem.Trim());
cert.PrivateKey = rsa;
return cert;
}
catch (Exception ex)
{
// ignore errors
return null;
}
}
public static RSA GetRSAFromPem(String pemstr)
{
RSA rsaKey = RSA.Create();
Func<RSA, RsaKeyParameters, RSA> MakePublicRCSP = (RSA rcsp, RsaKeyParameters rkp) =>
{
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
rcsp.ImportParameters(rsaParameters);
return rsaKey;
};
Func<RSA, RsaPrivateCrtKeyParameters, RSA> MakePrivateRCSP = (RSA rcsp, RsaPrivateCrtKeyParameters rkp) =>
{
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
rcsp.ImportParameters(rsaParameters);
return rsaKey;
};
PemReader reader = new PemReader(new StringReader(pemstr));
object kp = reader.ReadObject();
// If object has Private/Public property, we have a Private PEM
var hasPrivate = kp.GetType().GetProperty("Private") != null;
var isPrivate = kp is RsaPrivateCrtKeyParameters;
return isPrivate ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)kp) : hasPrivate ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)kp).Private)) : MakePublicRCSP(rsaKey, (RsaKeyParameters)kp);
}
I tested it on Android and it works great.
On iOS I haven't tested yet, but on UWP it fails and I get a PlatformNotSupported Exception while trying to set the PrivateKey on the certificate.
So I'm wondering what's not supported, and whether I'm not doing it right.
According to this, setting the private key on an existing certificate is not supported in .net core:
The PrivateKey property will be back in netstandard2.0 (#12295), but
it will throw on set for .NET Core.
set_PrivateKey has a large amount of nuance in .NET Framework
(depending on how you use it you can end up with side effects that
persist across machine reboots), and mirroring that level of nuance to
platforms other than Windows is awfully tricky, which is why we don't
support it.
The only supported way to have a cert with a private key on .NET Core
is through a PFX/PKCS12 file (or the cert+key pair to already be
associated via X509Store).
So one way to solve this was to merge public-private pair to a PFX file, embed it as a resource, and initialize the X509Certificate2 from that PFX.
Another way, which I ended up using, is to use the method RSACertificateExtensions.CopyWithPrivateKey on UWP.
So basically I ended up building a platform specific interface for loading the certificates. On UWP it was implemented like this:
public class UWPCertificateBuilder : ICertificateBuilder
{
public X509Certificate2 GetCertificate(X509Certificate2 cert, RSA key)
{
return cert.CopyWithPrivateKey(key);
}
}
On Android it was implemented like this:
public class DroidCertificateBuilder : ICertificateBuilder
{
public X509Certificate2 GetCertificate(X509Certificate2 cert, RSA key)
{
cert.PrivateKey = key;
return cert;
}
}
Instead of RSACryptoServiceProvider you should use base RSA class. In .NET Standard and .NET Core on Windows, RSA private key is resolved to RSACng, instead of legacy RSACryptoServiceProvider.
See this thread for more details: X509AsymmetricSecurityKey.GetAsymmetricAlgorithm returns null after .Net 4.7.2 upgrade

JCE cannot authenticate the provider BC in Robolectric

I am trying to test a keystore signing in my app, but cannot pass over loading a keystore from the assets, because the error is thrown:
java.io.IOException: error constructing MAC:
java.lang.SecurityException: JCE cannot authenticate the provider BC
here is the code
#Test
public void testKeyStore() {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream inputStream = RuntimeEnvironment.application.getAssets().open(fileName);
keyStore.load(inputStream, password.toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
}
If not with Robolectric, does anybody has an idea how to run this test?
Thanks
You really don't need Robolectric here. Try next:
#Test
public void testKeyStore() throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
String path = System.getProperty("user.dir") + "src/main/assets/filename.key";
InputStream inputStream = TimerTest.class.getResourceAsStream(path);
keyStore.load(inputStream, "test".toCharArray());
}

Certificate authentication of rest api in Azure with https

I have problems with configuring my asp.net web api service to authenticate requests by client certificates
I do the steps describing in Pro ASP.NET Web Api Security:
I create certificates using makecert.exe
makecert.exe -r -n "CN=MobileTradeDataGateway" -pe -sv MobileTradeDataGateway.pvk -a sha256 -cy authority MobileTradeDataGateway.cer and makecert.exe -iv MobileTradeDataGateway.pvk -ic MobileTradeDataGateway.cer -n "CN=DataGateway1" -pe -sv DataGateway1.pvk -a sha256 -sky exchange DataGateway1.cer -eku 1.3.6.1.5.5.7.3.2
I install MobileTradeDataGateway certificate in server Trusted Root Certification Authorities and in client too. Install DataGateway1 in client personal authority.
Configure site to accept certificates and enable. Enable anonymous authentication.
Create DelegatingHandler and add it to messagehandlers collection in mvc to check certificates.
Call web api method
var certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var collection = certStore.Certificates.Find(X509FindType.FindByIssuerName, "MobileTradeDataGateway", true);
var cert = collection[0];
certStore.Close();
var messageHandler = new WebRequestHandler();
messageHandler.ClientCertificates.Add(cert);
var client = new HttpClient(messageHandler) { BaseAddress = new Uri("...") };
var res = client.GetAsync("/api/orderuploader?number=5").Result;
.
Everything works fine in my local machine and network where my machine is server.
But when I deploy it to azure cloud service I get null
var cert = request.GetClientCertificate(); // here is null
in my custom delegating handler
Off course I enable IIS to accept certificates and correctelly put certificates in Trusted Root Certification Authorities
Any ideas?
Did you also tried getting your certificate by "Thumbprint".
here is a sample code that tries to read a certificate from certificate store.
private X509Certificate2 FindCertificate()
{
X509Store certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
certificateStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = certificateStore.Certificates;
X509Certificate2Collection matchingCertificates = certificates.Find(X509FindType.FindByThumbprint, "CertThumbprint", false);
if (matchingCertificates != null && matchingCertificates.Count > 0)
{
return matchingCertificates[0];
}
throw new ArgumentException("Unable to find a matching certificate in the certificate store. Please modify the search criteria.");
}
this link has more information on how you can read certificate from a web /worker role
Here is the code of my delegatinghandler from web api
public class X509ClientCertificateHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
WebApiEventSource.Log.InvalidHttpsScheme();
return request.CreateResponse(HttpStatusCode.Forbidden);
}
var cert = request.GetClientCertificate(); // here is null!!!
if (cert == null)
{
WebApiEventSource.Log.FailureAuthenticate("certificate is abcent", "", "");
return request.CreateResponse(HttpStatusCode.Unauthorized);
}
var chain =new X509Chain {ChainPolicy = {RevocationMode = X509RevocationMode.NoCheck}};
if (chain.Build(cert) && cert.Issuer.Equals("CN=MobileTradeDataGateway"))
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, cert.Subject.Substring(3))
};
var principal = new ClaimsPrincipal(new[] {new ClaimsIdentity(claims, "X509")});
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
WebApiEventSource.Log.SuccessAuthenticate(cert.SubjectName.Name);
return await base.SendAsync(request, cancellationToken);
}
WebApiEventSource.Log.FailureAuthenticate("certificate is incorrect", cert.IssuerName.Name, cert.SubjectName.Name);
return request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
I think you have missed to upload the certificate to Azure Portal. Please make sure you upload .cer or .pfx certificate to Azure Portal. Let me know if you need help on how to upload etc.

Upload artifacts to Nexus, without Maven

I have a non-Java project that produces a versioned build artifact, and I want to upload this to a Nexus repository. Because the project isn't Java, it doesn't use Maven for builds. And I'd rather not introduce Maven/POM files just to get files into Nexus.
The links on blogs to the Nexus REST API all end up at a sign-in wall, with no "create user" link that I can see.
So, what's the best (or any reasonable) way to upload build artifacts to a Nexus repository without Maven? "bash + curl" would be great, or even a Python script.
Have you considering using the Maven command-line to upload files?
mvn deploy:deploy-file \
-Durl=$REPO_URL \
-DrepositoryId=$REPO_ID \
-DgroupId=org.myorg \
-DartifactId=myproj \
-Dversion=1.2.3 \
-Dpackaging=zip \
-Dfile=myproj.zip
This will automatically generate the Maven POM for the artifact.
Update
The following Sonatype article states that the "deploy-file" maven plugin is the easiest solution, but it also provides some examples using curl:
https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-
Using curl:
curl -v \
-F "r=releases" \
-F "g=com.acme.widgets" \
-F "a=widget" \
-F "v=0.1-1" \
-F "p=tar.gz" \
-F "file=#./widget-0.1-1.tar.gz" \
-u myuser:mypassword \
http://localhost:8081/nexus/service/local/artifact/maven/content
You can see what the parameters mean here: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-
To make the permissions for this work, I created a new role in the admin GUI and I added two privileges to that role: Artifact Download and Artifact Upload. The standard "Repo: All Maven Repositories (Full Control)"-role is not enough.
You won't find this in the REST API documentation that comes bundled with the Nexus server, so these parameters might change in the future.
On a Sonatype JIRA issue, it was mentioned that they "are going to overhaul the REST API (and the way it's documentation is generated) in an upcoming release, most likely later this year".
You can ABSOLUTELY do this without using anything MAVEN related. I personally use the NING HttpClient (v1.8.16, to support java6).
For whatever reason, Sonatype makes it incredibly difficulty to figure out what the correct URLs, headers, and payloads are supposed to be; and I had to sniff the traffic and guess... There are some barely useful blogs/documentation there, however it is either irrelevant to oss.sonatype.org, or it's XML based (and I found out it doesn't even work). Crap documentation on their part, IMHO, and hopefully future seekers can find this answer useful. Many thanks to https://stackoverflow.com/a/33414423/2101812 for their post, as it helped a lot.
If you release somewhere other than oss.sonatype.org, just replace it with whatever the correct host is.
Here is the (CC0 licensed) code I wrote to accomplish this. Where profile is your sonatype/nexus profileID (such as 4364f3bbaf163) and repo (such as comdorkbox-1003) are parsed from the response when you upload your initial POM/Jar.
Close repo:
/**
* Closes the repo and (the server) will verify everything is correct.
* #throws IOException
*/
private static
String closeRepo(final String authInfo, final String profile, final String repo, final String nameAndVersion) throws IOException {
String repoInfo = "{'data':{'stagedRepositoryId':'" + repo + "','description':'Closing " + nameAndVersion + "'}}";
RequestBuilder builder = new RequestBuilder("POST");
Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/profiles/" + profile + "/finish")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Basic " + authInfo)
.setBody(repoInfo.getBytes(OS.UTF_8))
.build();
return sendHttpRequest(request);
}
Promote repo:
/**
* Promotes (ie: release) the repo. Make sure to drop when done
* #throws IOException
*/
private static
String promoteRepo(final String authInfo, final String profile, final String repo, final String nameAndVersion) throws IOException {
String repoInfo = "{'data':{'stagedRepositoryId':'" + repo + "','description':'Promoting " + nameAndVersion + "'}}";
RequestBuilder builder = new RequestBuilder("POST");
Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/profiles/" + profile + "/promote")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Basic " + authInfo)
.setBody(repoInfo.getBytes(OS.UTF_8))
.build();
return sendHttpRequest(request);
}
Drop repo:
/**
* Drops the repo
* #throws IOException
*/
private static
String dropRepo(final String authInfo, final String profile, final String repo, final String nameAndVersion) throws IOException {
String repoInfo = "{'data':{'stagedRepositoryId':'" + repo + "','description':'Dropping " + nameAndVersion + "'}}";
RequestBuilder builder = new RequestBuilder("POST");
Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/profiles/" + profile + "/drop")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Basic " + authInfo)
.setBody(repoInfo.getBytes(OS.UTF_8))
.build();
return sendHttpRequest(request);
}
Delete signature turds:
/**
* Deletes the extra .asc.md5 and .asc.sh1 'turds' that show-up when you upload the signature file. And yes, 'turds' is from sonatype
* themselves. See: https://issues.sonatype.org/browse/NEXUS-4906
* #throws IOException
*/
private static
void deleteSignatureTurds(final String authInfo, final String repo, final String groupId_asPath, final String name,
final String version, final File signatureFile)
throws IOException {
String delURL = "https://oss.sonatype.org/service/local/repositories/" + repo + "/content/" +
groupId_asPath + "/" + name + "/" + version + "/" + signatureFile.getName();
RequestBuilder builder;
Request request;
builder = new RequestBuilder("DELETE");
request = builder.setUrl(delURL + ".sha1")
.addHeader("Authorization", "Basic " + authInfo)
.build();
sendHttpRequest(request);
builder = new RequestBuilder("DELETE");
request = builder.setUrl(delURL + ".md5")
.addHeader("Authorization", "Basic " + authInfo)
.build();
sendHttpRequest(request);
}
File uploads:
public
String upload(final File file, final String extension, String classification) throws IOException {
final RequestBuilder builder = new RequestBuilder("POST");
final RequestBuilder requestBuilder = builder.setUrl(uploadURL);
requestBuilder.addHeader("Authorization", "Basic " + authInfo)
.addBodyPart(new StringPart("r", repo))
.addBodyPart(new StringPart("g", groupId))
.addBodyPart(new StringPart("a", name))
.addBodyPart(new StringPart("v", version))
.addBodyPart(new StringPart("p", "jar"))
.addBodyPart(new StringPart("e", extension))
.addBodyPart(new StringPart("desc", description));
if (classification != null) {
requestBuilder.addBodyPart(new StringPart("c", classification));
}
requestBuilder.addBodyPart(new FilePart("file", file));
final Request request = requestBuilder.build();
return sendHttpRequest(request);
}
EDIT1:
How to get the activity/status for a repo
/**
* Gets the activity information for a repo. If there is a failure during verification/finish -- this will provide what it was.
* #throws IOException
*/
private static
String activityForRepo(final String authInfo, final String repo) throws IOException {
RequestBuilder builder = new RequestBuilder("GET");
Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/repository/" + repo + "/activity")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Basic " + authInfo)
.build();
return sendHttpRequest(request);
}
No need to use these commands .. you can directly use the nexus web Interface in order to upload your JAR using GAV parameters.
So it is very simple.
The calls that you need to make against Nexus are REST api calls.
The maven-nexus-plugin is a Maven plugin that you can use to make these calls. You could create a dummy pom with the necessary properties and make those calls through the Maven plugin.
Something like:
mvn -DserverAuthId=sonatype-nexus-staging -Dauto=true nexus:staging-close
Assumed things:
You have defined a server in your ~/.m2/settings.xml named sonatype-nexus-staging with your sonatype user and password set up - you will probably already have done this if you are deploying snapshots. But you can find more info here.
Your local settings.xml includes the nexus plugins as specified here.
The pom.xml sitting in your current directory has the correct Maven coordinates in its definition. If not, you can specify the groupId, artifactId, and version on the command line.
The -Dauto=true will turn off the interactive prompts so you can script this.
Ultimately, all this is doing is creating REST calls into Nexus. There is a full Nexus REST api but I have had little luck finding documentation for it that's not behind a paywall. You can turn on the debug mode for the plugin above and figure it out however by using -Dnexus.verboseDebug=true -X.
You could also theoretically go into the UI, turn on the Firebug Net panel, and watch for /service POSTs and deduce a path there as well.
In ruby https://github.com/RiotGames/nexus_cli A CLI wrapper around Sonatype Nexus REST calls.
Usage Example:
nexus-cli push_artifact com.mycompany.artifacts:myartifact:tgz:1.0.0 ~/path/to/file/to/push/myartifact.tgz
Configuration is done via the .nexus_cli file.
url: "http://my-nexus-server/nexus/"
repository: "my-repository-id"
username: "username"
password: "password"
You can also use the direct deploy method using curl. You don't need a pom for your file for it but it will not be generated as well so if you want one, you will have to upload it separately.
Here is the command:
version=1.2.3
artifact="myartifact"
repoId=yourrepository
groupId=org.myorg
REPO_URL=http://localhost:8081/nexus
curl -u nexususername:nexuspassword --upload-file filename.tgz $REPO_URL/content/repositories/$repoId/$groupId/$artifact/$version/$artifact-$version.tgz
for those who need it in Java, using apache httpcomponents 4.0:
public class PostFile {
protected HttpPost httppost ;
protected MultipartEntity mpEntity;
protected File filePath;
public PostFile(final String fullUrl, final String filePath){
this.httppost = new HttpPost(fullUrl);
this.filePath = new File(filePath);
this.mpEntity = new MultipartEntity();
}
public void authenticate(String user, String password){
String encoding = new String(Base64.encodeBase64((user+":"+password).getBytes()));
httppost.setHeader("Authorization", "Basic " + encoding);
}
private void addParts() throws UnsupportedEncodingException{
mpEntity.addPart("r", new StringBody("repository id"));
mpEntity.addPart("g", new StringBody("group id"));
mpEntity.addPart("a", new StringBody("artifact id"));
mpEntity.addPart("v", new StringBody("version"));
mpEntity.addPart("p", new StringBody("packaging"));
mpEntity.addPart("e", new StringBody("extension"));
mpEntity.addPart("file", new FileBody(this.filePath));
}
public String post() throws ClientProtocolException, IOException {
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
addParts();
httppost.setEntity(mpEntity);
HttpResponse response = httpclient.execute(httppost);
System.out.println("executing request " + httppost.getRequestLine());
System.out.println(httppost.getEntity().getContentLength());
HttpEntity resEntity = response.getEntity();
String statusLine = response.getStatusLine().toString();
System.out.println(statusLine);
if (resEntity != null) {
System.out.println(EntityUtils.toString(resEntity));
}
if (resEntity != null) {
resEntity.consumeContent();
}
return statusLine;
}
}
If you need a convenient command line interface or python API, look at repositorytools
Using it, you can upload artifact to nexus with command
artifact upload foo-1.2.3.ext releases com.fooware
To make it work, you will also need to set some environment variables
export REPOSITORY_URL=https://repo.example.com
export REPOSITORY_USER=admin
export REPOSITORY_PASSWORD=mysecretpassword
For recent versions of Nexus OSS (>= 3.9.0)
https://support.sonatype.com/hc/en-us/articles/115006744008-How-can-I-programmatically-upload-files-into-Nexus-3-
Example for versions 3.9.0 to 3.13.0:
curl -D - -u user:pass -X POST "https://nexus.domain/nexus/service/rest/beta/components?repository=somerepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/test/" -F "raw.asset1=#test.txt;type=application/json" -F "raw.asset1.filename=test.txt"
You can manually upload the artifact's by clicking on upload artifacts button in the Nexus server and provide the necessary GAV properties for uploading(it's generally the file structure for storing the artifact)
#Adam Vandenberg For Java code to POST to Nexus.
https://github.com/manbalagan/nexusuploader
public class NexusRepository implements RepoTargetFactory {
String DIRECTORY_KEY= "raw.directory";
String ASSET_KEY= "raw.asset1";
String FILENAME_KEY= "raw.asset1.filename";
String repoUrl;
String userName;
String password;
#Override
public void setRepoConfigurations(String repoUrl, String userName, String password) {
this.repoUrl = repoUrl;
this.userName = userName;
this.password = password;
}
public String pushToRepository() {
HttpClient httpclient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost(repoUrl) ;
String auth = userName + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(StandardCharsets.ISO_8859_1));
String authHeader = "Basic " + new String(encodedAuth);
postRequest.setHeader(HttpHeaders.AUTHORIZATION, authHeader);
try
{
byte[] packageBytes = "Hello. This is my file content".getBytes();
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
InputStream packageStream = new ByteArrayInputStream(packageBytes);
InputStreamBody inputStreamBody = new InputStreamBody(packageStream, ContentType.APPLICATION_OCTET_STREAM);
multipartEntityBuilder.addPart(DIRECTORY_KEY, new StringBody("DIRECTORY"));
multipartEntityBuilder.addPart(FILENAME_KEY, new StringBody("MyFile.txt"));
multipartEntityBuilder.addPart(ASSET_KEY, inputStreamBody);
HttpEntity entity = multipartEntityBuilder.build();
postRequest.setEntity(entity); ;
HttpResponse response = httpclient.execute(postRequest) ;
if (response != null)
{
System.out.println(response.getStatusLine().getStatusCode());
}
}
catch (Exception ex)
{
ex.printStackTrace() ;
}
return null;
}
}
You can use curl instead.
version=1.2.3
artifact="artifact"
repoId=repositoryId
groupId=org/myorg
REPO_URL=http://localhost:8081/nexus
curl -u username:password --upload-file filename.tgz $REPO_URL/content/repositories/$repoId/$groupId/$artifact/$version/$artifact-$version.tgz

Resources