I am attempting to upload a file to web service that is running over SSL using HttpBuilder. I'm doing this from a Gradle build file but I don't believe it really matters. The web service is part of a Grails app, but again, don't think that matters.
My grails app is running over SSL locally using a cert generated by grails when you tell it to run over https. My code that works locally is as follows:
def http = new groovyx.net.http.HTTPBuilder( 'https://localhost:8443/' )
//=== SSL UNSECURE CERTIFICATE ===
def sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, [ new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {null }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
} ] as TrustManager[], new SecureRandom())
def sf = new SSLSocketFactory(sslContext)
def httpsScheme = new Scheme("https", sf, 8443)
http.client.connectionManager.schemeRegistry.register( httpsScheme )
http.request( groovyx.net.http.Method.POST, groovyx.net.http.ContentType.JSON ) { req ->
uri.path = '/a/admin/runtime/upload'
uri.query = [ username: "username", password: "password", version: version]
requestContentType = 'multipart/form-data'
org.apache.http.entity.mime.MultipartEntity entity = new org.apache.http.entity.mime.MultipartEntity()
def file = new File("file.zip")
entity.addPart("runtimeFile", new org.apache.http.entity.mime.content.ByteArrayBody(file.getBytes(), 'file.zip'))
req.entity = entity
}
All that garbage at the top is some code I found that allows HttpBuilder to work with self signed certs. And this is actually working locally. HttpBuilder docs say that most of the time, SSL should "just work". So my code for doing this over SSL with a legit purchased certification is as follows:
def http = new groovyx.net.http.HTTPBuilder( 'https://www.realserver.com/' )
http.request( groovyx.net.http.Method.POST, groovyx.net.http.ContentType.JSON ) { req ->
uri.path = '/a/admin/runtime/upload'
uri.query = [ username: "username", password: "password" ]
requestContentType = 'multipart/form-data'
org.apache.http.entity.mime.MultipartEntity entity = new org.apache.http.entity.mime.MultipartEntity()
def file = new File("file.zip")
entity.addPart("runtimeFile", new org.apache.http.entity.mime.content.ByteArrayBody(file.getBytes(), 'file.zip'))
req.entity = entity
}
When I run this I get the following error:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Any tips or suggestions would be much appreciated.
This is probably because the service is using self-signed certificate for HTTPS. To interact with it correctly, you should import it to the cacerts file of your JRE using keytool. An example command looks like this:
keytool -import -alias serviceCertificate -file ServiceSelfSignedCertificate.cer -keystore {path/to/jre/lib/security/cacerts}
Related
I am trying to send telemetry messages to Azure IoT Hub using the npm mqtt library, instead of using Azure Nodejs SDK/Library.
I am using X509 certificate authentication. The device connection is working fine when I use azure Nodejs SDK/Library and I am able to send telemetry messages.
When trying to use the MQTT library, it is saying unauthorized.
const mqtt = require("mqtt");
const fs = require('fs');
let options = {
cert: fs.readFileSync("device-cert.pem", "utf-8").toString(),
key: fs.readFileSync("device-cert.key", "utf-8").toString(),
passphrase: '1234',
clientId: "device-003",
username: "ih-iot-sample-001.azure-devices.net/device-003/?api-version=2021-04-12",
}
let client = mqtt.connect(
"mqtts://ih-iot-sample-001.azure-devices.net:8883",
options
);
client.on("connect", function () {
console.log("connected");
});
client.on("error", (err) => {
console.log(err);
process.exit(0)
});
Error :
Connection refused: Not authorized
The clientid and the deviceId in the username were wrong. That's why I got this error
I am trying to instantiate a PaymentIntent on the server-side (using Go) just like this example but met with this error Request failed with error: Post "https://api.stripe.com/v1/payment_intents": x509: certificate signed by unknown authority. I have made sure to assign the test secret key like below, but the error still persists. Does it have something to do with SSL certificate? I am testing my app locally using Docker (localhost).
My code:
import (
"github.com/stripe/stripe-go"
"github.com/stripe/stripe-go/paymentintent"
)
func CreateStripePaymentIntent(subtotal float32) (string, error) {
// Create a PaymentIntent with amount and currency
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
fmt.Println(stripe.Key)
params := &stripe.PaymentIntentParams{
Amount: stripe.Int64(int64(subtotal)),
Currency: stripe.String(string(stripe.CurrencyUSD)),
}
pi, err := paymentintent.New(params)
if err != nil {
return "", fmt.Errorf("pi.New: %v", err) // =======> ERROR HERE WHEN CALLING STRIPE API
}
return pi.ClientSecret, nil
}
Since I run the server using scratch Docker image, there is no SSL certificate to use. Just need to copy the certs from first stage (Mine is a multi-staged build) COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
I have created an RSA certification using a generated private/public key using this library-
https://github.com/cbaker6/CertificateSigningRequest
thanks to this library I now have a certificate in a PEM string format.
now I want to create an ssl socket that can use this certificate to connect to a remote server that requires an ssl connection.
so for example this is a pseudo code in node.js:
let options = {
key : this.certs.key,
cert: this.certs.cert,
port: this.port,
host : this.host,
rejectUnauthorized: false,
}
console.debug("Start Connect");
this.client = tls.connect(options, () => {
console.debug(this.host + " connected")
});
where the key and the cert are both a PEM string
I tried BlueSSLService lib, but they only support connecting with cert files, no strings.
I am creating an API for a backend service with Rails 4.
The service needs to upload an image file to an amazon s3 bucket.
I'd like to use a direct upload url, so that the clients manage the uploads to s3 and the server is not kept busy.
Currently I have the following prototypical rails action
def create
filename = params[:filename]
s3_direct_post = S3_BUCKET.presigned_post(key: "offers/#{SecureRandom.uuid}/#{filename}", acl: 'public-read')
s3p = s3_direct_post.fields
url = "#{s3_direct_post.url}/#{filename}?X-Amz-Algorithm=#{s3p['x-amz-algorithm']}&X-Amz-Credential=#{s3p['x-amz-credential']}&X-Amz-Date=#{s3p['x-amz-date']}&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=#{s3p['x-amz-signature']}"
render json: {success: true, url: url}, status: :ok
end
This generates such an url:
https://my-bucket.s3.eu-central-1.amazonaws.com/test.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MYKEY/20150420/eu-central-1/s3/aws4_request&X-Amz-Date=20150420T162603Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=MYSIGNATURE
Now I try to post the test.png to this url with the following:
curl -v -T test.png "url"
and I get the following error response:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>MYKEY</AWSAccessKeyId>...
I believe the problem comes from the fact, that the specified X-Amz-SignedHeaders Header is wrong. I am not sure which headers are used by default from the amazon rails sdk gem.
How should I change my url generation, so that a mobile client can just take the url and post a file to it?
Here is a solution:
In config/initializers/aws.rb:
AWS_CREDS = Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
Aws.config.update({
region: 'eu-central-1',
credentials: AWS_CREDS
})
S3 = Aws::S3::Resource.new('eu-central-1')
S3_BUCKET_NAME = ENV['S3_BUCKET_NAME']
S3_BUCKET = S3.bucket(S3_BUCKET_NAME)
In your model/controller/concern/or whatever:
obj = S3_BUCKET.object("offers/#{user.id}/#{self.id}")
url = obj.presigned_url(:put) # obj.presigned_url(:put, acl: 'public-read') #if you want to make the file public
Then to upload you can use a mobile client or curl:
curl -X PUT -T file_to_upload "url from above"
Note that you will have to add the x-amz-acl: public-read header if you used the public-read acl option:
curl -H "x-amz-acl: public-read" -X PUT -T file_to_upload "url from above"
I am using https://github.com/ruby-ldap/ruby-net-ldap (net-ldap) gem to verify the authenticity of a user in my rails app. But before passing data to the ldap server, I need to verify that I am talking with the same secure server.
Is there a workaround which allows me to verify the certificate in ruby
Additional details: (things I have tried)
The certificate which is passed on to me is same as the one I see when I run
openssl s_client -showcerts -connect "<host>:<port>" </dev/null 2>/dev/null|openssl x509 -outform PEM
I used http://www.ldapsoft.com/ to connect to client's server
Unless I add the certificate file given to me in Security > Manage server certificates, I get a warning saying unknown security certificate
I tried do it manually first in plain ruby (without gem)
But i get following error
test-ssl.rb:23:in `connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (OpenSSL::SSL::SSLError)
Code:
cert_store = OpenSSL::X509::Store.new
cert_store.add_file "server-wildcard.crt"
io = TCPSocket.new("SECURELDAP.MYSITE.EDU","636")
ctx = OpenSSL::SSL::SSLContext.new
#ctx.cert = OpenSSL::X509::Certificate.new(File.read("server-wildcard.crt"))
#ctx.client_ca = OpenSSL::X509::Certificate.new(File.read("server-wildcard.crt"))
#ctx.ca_file = "server-wildcard.crt"
#ctx.ca_path = "./"
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
ctx.cert_store = cert_store
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
conn.connect
I am posting my solution here for the sake of completeness.
net-ldap gem override to support certificate validation
https://gist.github.com/mintuhouse/9931865
Ideal Solution:
Maintain list of trusted root CAs on your server
(If you are lazy like me, have a cron job which will download (weekly maintained by curl) copy from http://curl.haxx.se/ca/cacert.pem)
Override Net::HTTP to always use this trusted certificate list
As of today (late 2016), ruby-net-ldap supports this upstream! However, tls_options needs to be passed with verify_mode set to a value other than the default VERIFY_NONE.
# optional: create/pass your own cert_store
cert_store = OpenSSL::X509::Store.new
cert_store.set_default_paths # or add your own CAdir, &c.
# attributes documented for OpenSSL::SSL::SSLContext are valid here
tls_options = {
verify_mode: OpenSSL::SSL::VERIFY_PEER
cert_store: cert_store
}
ldap = Net::LDAP.new(
:host => host,
:port => port,
:encryption => {
:method => :simple_tls, # could also be :start_tls
:tls_options => tls_options
}
)