Help verifying RSA signed text with Python - java

Using Java I have created RSA keypairs. Using Java I can use these keys to sign and verify some text. I can also "export" these keys in PEM format and load them into a Python test script. Once in the Python script, I can use these keys to sign and verify some text using M2Crypto.
I have not yet been able to verify in Python the signature I created in Java.
Right now I am just trying to get cross-platform signing and verifying to work.
Here is Java snippet:
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(key.getPrivate());
sig.update("This is a message.".getBytes("UTF8"));
byte[] signatureBytes = sig.sign();
return Base64.encodeBytes(signatureBytes, Base64.DO_BREAK_LINES);
Which generates:
PIp4eLhA941xmpdqu7j60731R9oWSNWcHvwoVADKxABGoUE02eDS0qZ4yQD2vYBdRDXXxHV4UjtW
YQwv9nsOzCBWeDQ0vv6W0dLVfTBuk79On7AALuwnTFr8s0y5ZN5RINvPPR60mwONav26ZbPj4ub3
NZqUS/zkqyO8Z8D2zUjk0pqAhWDGbFBaWPQJBPOY9iRt8GlsAUkGfYGeIx9DNU8aiJmQ3NnUHbs4
5NEr3xydbNJjwK96kkNJ9vyKZRfnNd4eW2UllPiwJSRQgefCQfh79ZuiYeQEuk3HMh7Si4iYl7uU
rWCgYFl4fGV1X/k+BSHR4ZZFWGQ3IPfafYHyNw==
And here is the public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg+N7xQHVHU3VHMZ9VGFdUf6ud3rAL8YS
sfWv2zFMnKAjh6XacwDGX6jJR/0/pyDriRGw/uodBDSFvgn9XEM/srMYtbJ1KQ9R9ChDifixF3Hx
yzbzPRvFoEfZnS/63+d1r0wnafz7xx0eDEYE3TgRKTMikwOsuIOFWa7GhA4BvP7ad79bI5ORJdLu
9Je+k+4dbt0xk2t7YopxYmyU+4zhZMxuthJTr69rXgqhgsCRdK6kFIGm3YuJ1WQcci8OVwH+3o7F
XzJHpSTxH57m6PX5lXaywIDCbUauTpBV3w+0vTeGI/2o+U40qhLBkpZT9GSVKxgXl5a0XxrkwTGn
61XZqQIDAQAB
-----END PUBLIC KEY-----
Then in Python the key is loaded and the signature is attempted to be verified:
from M2Crypto import RSA, EVP
pub_key = RSA.load_pub_key('public_key.pem')
verify_evp = EVP.PKey()
verify_evp.assign_rsa(pub_key)
verify_evp.verify_init()
verify_evp.verify_update("This is a message.")
if verify_evp.verify_final(sig_string.decode('base64')) == 1:
print "Good"
else:
print "Bad"
And this does not verify. I suspect it is some leading or trailing characters or encoding weirdness that I don't understand.
I have no particular attachement to M2Crypto and have played a bit with the gdata.tlslite modules as well.
Please note that the keys are working, and that the text and signature above verify in Java, and the keys (public and private) can be used within Python to sign and verify text. The problem is somewhere in how Java-generated signature and/or the message text is getting into the Python code.
What am I doing wrong?

M2Crypto.EVP defaults to 'sha1' (SHA1) and you're using MD5WithRSA. I think you should switch SHA1WithRSA (MD5 is a very weak algorithm).

Related

Decoding - from Java to Powershell

I have inherited a self-made certification solution based on Java and Microsoft SQL server. We are moving forward to a Venafi solution. The old certificate has to be moved from the old solution to a Venafi pki solution. The designer of the old solution is not here anymore, but I have the decryption part in Java, including the decryption key. I have absolutely no experience in Java, and very limited cryptographic experience in Powershell.
The Java code to decrypt is this:
SecretKeySpec key = new SecretKeySpec(Base64.decode(encryptionkey.getBytes()), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(encrypted);
The password is something like: gTsLrRTFR5Q0cvQZFRuZPw== (Not the actual password) and the certificates in encrypted format is basic hex data like 0x5F4E23E1 ... all in 1024 sizes.
How would I decrypt these certificates in powershell?
I'm not the specialist for powershell so I can just give you some hints to run the decryption part, for tasks like "get all files in folder with ending *.enc"
I'm leaving it to you to find a solution.
Assuming you are having a key in the format
MTIzNDU2Nzg5MDEyMzQ1Ng==
then it is a Base64-encode key. If all of your 30.000 certificates were encrypted with the same key then simply get the hex encoded value of the key
using an online service like https://base64.guru/converter/decode/hex.
Just enter the string above and press convert Base64 to Hex and you receive the key as follows:
31323334353637383930313233343536
Now count the chars - here we have 32 chars that mean it is a 16 byte (128 bit) long key used for AES cryptography. It's not a joke to count them because
the length of the key is important for the decryption task.
Having a certificate-file that was encrypted with a 32-char long hex-key ("cert32.enc") you use openssl (can be used in powershell as well as in many
other scripting languages) with this command (it is important to use the -K-option with a capital K and the hexstring enclosed with quotation marks):
openssl enc -aes-128-ecb -d -in cert32.enc -out cert.pem -K "31323334353637383930313233343536"
This will decode you the original certificate to the file "cert.pem" as follows (it is a sample certificate !):
-----BEGIN CERTIFICATE-----
MIIEETCCAvmgAwIBAgIUP2GufsPxg8R2n6L161b6wauxnGYwDQYJKoZIhvcNAQEL
BQAwgZcxCzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlzb21lU3RhdGUxDTALBgNVBAcM
BGNpdHkxEDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xHzAdBgNV
BAMMFmphdmFjcnlwdG9AYnBsYWNlZC5uZXQxIDAeBgkqhkiG9w0BCQEWEWphdmFj
cnlwdG9AZ214LmRlMB4XDTIwMDcxMTA4MDMxNloXDTIxMDcxMTA4MDMxNlowgZcx
CzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlzb21lU3RhdGUxDTALBgNVBAcMBGNpdHkx
EDAOBgNVBAoMB2NvbXBhbnkxEDAOBgNVBAsMB3NlY3Rpb24xHzAdBgNVBAMMFmph
dmFjcnlwdG9AYnBsYWNlZC5uZXQxIDAeBgkqhkiG9w0BCQEWEWphdmFjcnlwdG9A
Z214LmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv1fayJg6TO7D
NASipOooJu9aYxLfw5/jokbaggOkL7wPQ2+eWk3tby72jMFniWySm6YIkiTbewYo
8WKl94zWTT8xx5pg/eOh28BTQLsi0/s9RF37z+eJA1TjA6TuBesNevm3V310H93Y
BLTA2Mjp/99W/smBhefaeYEkLh6TrZAAi2JtUHrs0FwNREoXrRIfq9monpUpY7lr
YRSy70nEaEyctw6khxeTRVRR97ZdqogLl2oEur9k5NKD8XHJ6A7MYz+asLMYNcnA
0jV02wR+b6etEr1tAtnswdxQ5T6tLrAnoen5v/fXSDnz93L7oRmTPsQJhK55TrrM
G+RWvV8aoQIDAQABo1MwUTAdBgNVHQ4EFgQULdU7CowdOtePac360y4n9aEbP2ow
HwYDVR0jBBgwFoAULdU7CowdOtePac360y4n9aEbP2owDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEAcP6EBsscMFAg0mMTLnd7+7VJzLuodPBAxiMS
zCYtUNS0KPBBR6OrGbHTbbvYd8/VGORpaWORfm0MDLP2kIxLKCYn4l7Wwoou7Idc
+Z+mohQKPwjtHnMZX6HyiCpmDF+qNR7dpOKpIsMahm9zVD8rfySFzr5oDSa7zFSr
MyJKmnz5I+gkUjJKvjYpKPjv7yuENhCbj4roNYK7ztN/vU6yJnFmzOaomP4MxhlE
GDoucjmy+qdGWF/i3Kh8n7zXBxRoBJZSkGqPE2N0PLIJlAFxb9c2QjPu5rmtZiDO
HiFj2Xk7jayMNw3JNVcayHjAcdHkp/u/BgTeqJWZRC+GsMz7ag==
-----END CERTIFICATE-----
Now we are working with the other key length (64 chars long). Having a base64 that looks like
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
you will get the hex-string with 64 characters:
3132333435363738393031323334353637383930313233343536373839303132
Now we are running the openssl-commandline with a similar algorithm (and another filename/key):
openssl enc -aes-256-ecb -d -in cert64.enc -out cert.pem -K "3132333435363738393031323334353637383930313233343536373839303132"
Voila, it's decoding to the same cert.pem as above.
All files are available via my GitHub repo for easy testing:
https://github.com/java-crypto/Stackoverflow/tree/master/Decoding_from_Java_to_Powershell

How can I convert PEM file to string for ssh to ec2 using JSch library

I am trying to SSH to EC2 using JSch Library from Java code. I referred this link in SO How can I use .pem files content as a string in ec2 connection using JSch library and tried couple of things as mentioned below but in vain. Can someone please guide me on how to achieve my objective?
Objective
I have a PEM file like this. I dont want to store my PEM file anywhere in AWS, hence my approach is to extract an equivalent string that I can encode and store in database and decode it from java for passing the parameter to addIdentity method that takes these parameters:
addIdentity(String name, byte[] prvkey, byte[] pubkey, byte[] passphrase)
throws JSchException
-----BEGIN RSA PRIVATE KEY-----
MIIepsdfAIBAAKCAQEAtBk068z
...
xVNdhlDy6asdk9wsdQ==
-----END RSA PRIVATE KEY-----
For my objective, my addIdentity method would be like this I believe:
addIdentity ("username","{privatekey string converted to byte array}",null, null)
I am trying to understand how that string can be formed? I am very new to cryptography, but during this process I learnt that since my PEM has BEGIN RSA PRIVATE KEY, it's PKCS1 format. Does JSch support PKCS1 format or it needs to be converted to PKSC8?
Secondly, I learnt that the body is encoded with Base64, so I even tried decoding the string with Base64 after stripping off all the carriage returns, header and footer, which gave me error like this
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
Below are some of the additional links I tried following up but have not been able to resolve.
JSch getting "invalid privatekey:" while trying to load an RSA private key by KeyPairGenerator
Java - How to decode a Base64 encoded Certificate
Converting a PEM private key file to a JAVA PrivateKey Object
Hope someone can guide me in the right direction.
Thanks!
I figured out the answer.
Below post gave me a direction.
JSch: addIdentity from private key stored on hdfs
To anyone else who is looking to solve a similar requirement, ensure that you are not stripping off the header, footer information. This took most of my time to debug as most of the blogs/SO posts directed towards stripping those characters. In Java, your string must have the carriage returns else you might get a very different byte array.
String x = "-----BEGIN RSA PRIVATE KEY-----\r\n" +
"MIIEpAIBAAKCAQEAtBk\Q/z4QAgk+LN3IUajqjUv7IucsCd4SebbQvah5t4WJ\r\n"
Convert the string to byte array using "US-ASCII" charset. Use following JSch method if you don't have a passphrase:
jsch.addIdentity("username",{bytearray of x},null, null)
Note: ensure that you are passing an unsigned byte array like:
Array (45, 45, 69,...)
and NOT
Array (45, -35, -125,...)

Convert certificate string to byte array

I got a string represents PEM certificate:
-----BEGIN CERTIFICATE-----
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
-----END CERTIFICATE-----
I assigned the above string to String variable String myCertStr.
What is the proper way to convert myCertStr to DER encoded byte[]?
(I am using Java 7, and I am not interested to use 3rd party library for this, I am seeking for a JDK7 way of doing it.)
IMPORTANT
As #dave_thompson_085 has pointed out in the comments, SunJCE CertificateFactory is indeed capable of parsing PEM files.
So you can just use that to get the Certificate object
as detailed at How to load public certificate from pem file..? (which is an earlier answer by #dave on the same topic, so please upvote it, instead of this one, if you find this useful !), and then access its encoded (DER) form.
However, if your PEM file is a raw "RSA PUBLIC KEY" (like the one that was attached to this question), or some other entity which SunJCE implementation can not parse directly, you can still parse and decode it manually, as detailed below.
Technically what you have here is not a certificate, but just a public key.
You can decode it to DER bytes as simple as that:
byte[] derBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----", "").replaceAll("\n", "")
);
Note, that what you will get will be a raw RSA (PKCS#1) key:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
You can use the same technique to decode X.509 certificates or private keys.
E.g. the code to decode the X.509 certificate:
byte[] certificateBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) CERTIFICATE-----", "").replaceAll("\n", "").getBytes("UTF-8")
);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)(certificateFactory.generateCertificate(
new ByteArrayInputStream(certificateBytes)
)
);
UPDATE
The code above uses Java 8 Base64 decoder.
As question has been updated asking for a Java 7 solution,
here is a link to an excellent thread, discussing various options available: Base64 Java encode and decode a string.
E.g. java.xml.bind method described there does not require any extra libraries on Java 7 (which seems to match what OP wants)

RSA encryption in Java, decrypt in PHP

Assume I have the following Java code to generate a Public-private keypair:
KeyPairGenerator generator = KeyPairGenerator.getInstance ("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize (1024, random);
KeyPair pair = generator.generateKeyPair();
RSAPrivateKey priv = (RSAPrivateKey)pair.getPrivate();
RSAPublicKey pub = (RSAPublicKey)pair.getPublic();
// Sign a message
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign (priv);
dsa.update ("Hello, World".getBytes(), 0, "Hello, World".length());
byte[] out = dsa.sign();
/* save the signature in a file */
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(out);
sigfos.close();
How would one go about and decrypt the file "sig" in PHP? I've read the post: https://stackoverflow.com/a/1662887/414414 which supplies a function to convert a DER file to PEM (Assume I also save the public key from Java).
I have tried something like:
$key = openssl_pkey_get_public ("file://pub_key.pem");
$data = null;
openssl_public_decrypt ( file_get_contents ("sig"), $data, $key);
echo $data, "\n";
It successfully decrypts the message, but it is many weird characters.
Our scenario is a Java client that is sending messages to a PHP server, but encrypts the data with a private key. PHP knows about the public key, which it should use to decrypt and validate the message.
I've read a lot of posts regarding this issue here on SO, but I've come to realize that this is a bit specific issue, especially if there's different algorithms in use, etc. So sorry if this may be a duplicate.
Any feedbacks are greatly appreciated!
an "RSA signature" is usually more than just "encrypt with private key, decrypt with public key", since Public key protocols like PKCS#1 also specify padding schemes, and all signature schemes will encrypt a digest of the message, instead of the full message. I cannot find any documentation if java's signature scheme uses the signature padding scheme specified in PKCS#1, but my suspicion is that it is.
If it is, you will instead want to use the openssl_verify method in PHP, documented here. This will return a 0 or 1 if the signature is invalid or valid, respectively.
In the event that Java does not use a padding scheme, your issue is that the data encrypted in the signature is a hash of the message, instead of the message itself (you can see in the Java code that it uses the SHA-1 hash algorithm). So on the PHP side, you will need to take the sha1 hash of your message using the sha1 method with $raw_output set to true, and compare those strings to ensure your message is valid.
From the snippet
$key = openssl_pkey_get_public ("file://pub_key.pem");
It looks like you're referencing the public key, which would be the wrong one to decrypt. Double check ?

PKI verification across Java and Python

I am trying to implement a PKI verification scheme, where a message string is signed with a private key on server, the signature is stored on the client along with the message string. The client then verifies the signature using a public key.
The restrictions of my environment are, the server is Google App Engine and the client is a Java program. I have played with Java-only and Python-only solutions of PKI verification and got them to work, however when doing one operation in Python and another in Java is posing problem, mainly due to Key file format restrictions and my limited understanding of cryptography terminology.
One of the biggest limitations is crypto support in GAE. The only library supported is PyCrypto and this library can't read public/private keys stored in PEM, DER or X509 formats. As far as I could find, only M2Crypto supports reading from these files, but it can't be used inside GAE because it's a wrapper around openssl, so not a pure python solution. Even if I could find a way to translate the public/private keys from PEM/DER/X509 to the format that PyCrypto understands, that will work for me. But I couldn't find any way to do it. Any ideas there?
I found one possible solution in the form of tlslite. tlslite could read a private key from PEM file and create a signature. Here is the code.
from tlslite.utils.cryptomath import bytesToBase64
from tlslite.utils.keyfactory import parsePEMKey
s = open('private.pem').read()
key = parsePEMKey(s)
doc = 'Sample text'
bytes = array('B')
bytes.fromstring(doc)
print bytesToBase64(key.sign(bytes))
The corresponding Java code I used to verify the signature is.
String signAlgo = "SHA1WithRSAEncryption";
// read public key from public.der
byte[] encodedKey = new byte[294]; // shortcut hardcoding
getAssets().open("public.der").read(encodedKey);
// create public key object
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
// read signature (created by python code above)
byte[] encodedSig = new byte[345];
getAssets().open("signature.txt").read(encodedSig);
byte[] decodedSig = Base64.decodeBase64(encodedSig);
// Do verification
Signature verifyalg = Signature.getInstance(signAlgo);
verifyalg.initVerify(pk);
verifyalg.update(message.getBytes());
Log.d(TAG, "Verif : "+verifyalg.verify(decodedSig));
The verification fails.
I suspected if the tlslite is using different algorithm for signature creation than what the java code expects.
So I tried to find that out.
On python side
print key.getSigningAlgorithm()
gave me
pkcs1-sha1
on Java side, I tried to find all supported algorithms with this code:
Set<String> algos = java.security.Security.getAlgorithms("Signature");
for(String algo : algos) {
Log.d(TAG, algo);
}
That gave me
MD4WithRSAEncryption
RSASSA-PSS
SHA1withDSA
SHA1withRSA/ISO9796-2
1.2.840.113549.1.1.10
SHA512withRSA/PSS
MD5withRSA/ISO9796-2
DSA
SHA512WithRSAEncryption
SHA224withRSA/PSS
NONEWITHDSA
SHA256withRSA/PSS
SHA224WithRSAEncryption
SHA256WithRSAEncryption
SHA1withRSA/PSS
SHA1WithRSAEncryption
SHA384withRSA/PSS
SHA384WithRSAEncryption
MD5WithRSAEncryption
I tried all the SHA1 values on the Java side. But none helped to verify the signature generated by tlslite with pkcs1-sha1 algo. Any idea about this mapping?
These are different operations. In Python, you need to use hashAndSign. The default happens to be SHA1 hash.
Keyczar should work fine on App Engine, and is available in both Java and Python flavours.

Categories

Resources