PKI verification across Java and Python

2019-07-13 20:13发布

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?

2条回答
我欲成王,谁敢阻挡
2楼-- · 2019-07-13 20:28

These are different operations. In Python, you need to use hashAndSign. The default happens to be SHA1 hash.

查看更多
趁早两清
3楼-- · 2019-07-13 20:33

Keyczar should work fine on App Engine, and is available in both Java and Python flavours.

查看更多
登录 后发表回答