Hi guys, I would like to know if with the crystal standard library, it is possible to check a file signature via the openssl library, with a public or a private key.
I need some advices as well, because I am not expert in cryptography. Basically I would like to enforce my package manager security by adding as extra of the checksum process, a signature verification process.
So if I take an example, I upload my file called package.tar.xz on my server. I guess the user should have only the public key isn’t it ?
I would like as well to know how I can investigated when my program download a file , by checking signature website and rejecting self-signed certificates ?
This is the download function. Someone from the forum did the implementation because I know nothing about networking unfortunately :
def downloadFile(link : String, filename : String, fileExtensionName : String)
Ism.recordSystemCall(command: "#{{% @def.receiver %}}.#{{% @def.name %}}")
originalLink = link
downloaded = false
error = String.new
startingTime = Time.monotonic
until downloaded
HTTP::Client.get(link) do |response|
if response.status.redirection?
begin
link = response.headers["location"]
rescue
error = "#{ISM::Default::Software::DownloadSourceRedirectionErrorText1}#{response.status_code}#{ISM::Default::Software::DownloadSourceRedirectionErrorText2}"
Ism.notifyOfDownloadError(link, error)
Ism.exitProgram
end
break
end
filePath = "#{workDirectoryPathNoChroot}/#{filename+fileExtensionName}"
colorizedFileFullName = "#{filename}#{fileExtensionName.colorize(Colorize::ColorRGB.new(255,100,100))}"
colorizedLink = "#{link.colorize(:magenta)}"
lastSpeedUpdate = Time.monotonic
average = 0
bytesLastPeriod = 0
if response.status_code == 200
buffer = Bytes.new(65536)
totalRead = Int64.new(0)
lenght = response.headers["Content-Length"]? ? response.headers["Content-Length"].to_i32 : Int64.new(0)
File.open(filePath, "wb") do |data|
while (pos = response.body_io.read(buffer)) > 0
lapsed = Time.monotonic - lastSpeedUpdate
if lapsed.total_seconds >= 1
div = lapsed.total_nanoseconds / 1_000_000_000
average = (bytesLastPeriod / div).to_i32!
bytesLastPeriod = 0
lastSpeedUpdate = Time.monotonic
end
data.write(buffer[0...pos])
bytesLastPeriod += pos
totalRead += pos
if lenght > 0
text = "\t#{"| ".colorize(:green)} #{colorizedFileFullName} [#{(Int64.new(totalRead*100/lenght).to_s+"%").colorize(:green)}] #{"{".colorize(:green)}#{average.humanize_bytes}/s#{"}".colorize(:green)} (#{colorizedLink})"
else
text = "\t#{"| ".colorize(:green)} #{colorizedFileFullName} [#{"0%".colorize(:green)}] #{"{".colorize(:green)}#{average.humanize_bytes}/s#{"}".colorize(:green)} (#{colorizedLink})"
end
print text+"\r"
end
end
downloaded = true
else
error = "#{ISM::Default::Software::DownloadSourceCodeErrorText}#{response.status_code}"
Ism.notifyOfDownloadError(link, error)
Ism.exitProgram
end
end
end
puts
end
kojix2
November 28, 2024, 1:02am
4
I am no more familiar with this issue than you are, but I found the following library (which you may already know about) from spider-gazelle.
Crystal bindings for OpenSSL RSA
I found the following issue.
opened 08:21PM - 18 Jun 19 UTC
kind:feature
topic:stdlib:crypto
## Summary
I need this feature to [sign some X509 certificates](https://stackov… erflow.com/questions/30814230/dynamically-create-ssl-certs-keys-with-common-ca).
I don’t know much about OpenSSL.
I looked at the [Crystal API documentation](https://crystal-lang.org/api/0.19.4/OpenSSL/X509/Certificate.html), but I didn't see this feature.
(e.g. `OpenSSL::X509::Request`, `OpenSSL::PKey::RSA`).
enhance: OpenSSL::X509::Certificate
(e.g. `version`, `serial`, `public_key`, `subject`, `issuer`...)
and Need more documentation (preferably like ruby).
## In Ruby (Example From StackOverflow)
```ruby
ca = OpenSSL::X509::Certificate.new( File.read( INTERCEPTOR_CA_CERTIFICATE ) )
ca_key = OpenSSL::PKey::RSA.new( File.read( INTERCEPTOR_CA_KEY ) )
keypair = OpenSSL::PKey::RSA.new( 4096 )
req = OpenSSL::X509::Request.new
req.version = 0
req.subject = OpenSSL::X509::Name.parse(
"CN=www.origin-server.com/O=Arachni/OU=Proxy/L=Athens/ST=Attika/C=GR"
)
req.public_key = keypair.public_key
req.sign( keypair, OpenSSL::Digest::SHA1.new )
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = rand( 999999 )
cert.not_before = Time.new
cert.not_after = cert.not_before + (60 * 60 * 24 * 365)
cert.public_key = req.public_key
cert.subject = req.subject
cert.issuer = ca.subject
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = ca
cert.extensions = [
ef.create_extension( 'basicConstraints', 'CA:FALSE', true ),
ef.create_extension( 'extendedKeyUsage', 'serverAuth', false ),
ef.create_extension( 'subjectKeyIdentifier', 'hash' ),
ef.create_extension( 'authorityKeyIdentifier', 'keyid:always,issuer:always' ),
ef.create_extension( 'keyUsage',
%w(nonRepudiation digitalSignature
keyEncipherment dataEncipherment).join(","),
true
)
]
cert.sign( ca_key, OpenSSL::Digest::SHA1.new )
```
## References
* [StackOverflow - Dynamically create SSL certs/keys with common CA
](https://stackoverflow.com/questions/30814230/dynamically-create-ssl-certs-keys-with-common-ca)
* [RubyDoc - Class: OpenSSL::X509::Certificate](https://ruby-doc.org/stdlib-2.4.0/libdoc/openssl/rdoc/OpenSSL/X509/Certificate.html)
it appears that there was no implementation 5 years ago. I don’t know if the situation is better now, but looking at the latest Crystal reference, it seems that there is no such class as PKey even today.
Let’s wait for someone who knows more about it.
I see. Anyway, if it’s really like this, I think I will just perform a Process call to use openssl cli.
It’s just if I can do in a more optimised way, I prefer
Thank you so much
If you’re just concerned about verifying the integrity of a file, would comparing SHA256 hashes be simpler and accomplish the same goal?
Agreed with @Blacksmoke16 here. Signatures only need encryption keys when you’re verifying that a specific entity signed it.
If you need that, use OpenSSL::HMAC
. If you don’t, use Digest::SHA256
.
require "digest/sha256"
require "openssl/hmac"
data = "my data"
key = "my key"
puts OpenSSL::HMAC.hexdigest(:sha256, key, data)
# => 901844ce02ca3634a38f3430eecdb8d21be863ada40c6ff01c38d85ebcbefa70
puts Digest::SHA256.hexdigest(data)
# => b2167b0aa7ef7794740b055ac7a880a52934aa67ef1ca6887ad81dccefd5b9de
naqvis
December 3, 2024, 2:51am
8
I believe question was related to Pretty Good Privacy (PGP) , if that was the case then neither stdlib nor external shard provide PGP compliant implementation. For quick turnaround i would say wrapping pgp
executable (available for almost every system)
for more better cases, it would be good if some shard could either implenent or integrate OpenPGP .
My two cents.