当前位置

发现 PyCrypto 不如 ctypes 来得方便

至少对于一个C程序员来说是这样..

有一段RSA签名的程序,原本是用 C 调 libcrypto 包装成 jni 由 java 使用的,现在打算在 python 里面实现一个类似的功能,这段 C 代码大概是这样:

  1. int rsa_sign(char *private_keyfile, char *message, int message_len, char *buf, int buf_size)
  2. {
  3.     RSA *key;
  4.     int malloc_size, rsa_size;
  5.     unsigned char *rsa;
  6.  
  7.     BIO *bio;
  8.  
  9.     bio = BIO_new_file(private_keyfile, "r");
  10.     if (NULL == bio) {
  11.         return 0;
  12.     }
  13.     key = (RSA *)PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
  14.     BIO_free(bio);
  15.     if (NULL == key) {
  16.         return 0;
  17.     }
  18.  
  19.     malloc_size = RSA_size(key);
  20.     rsa = (unsigned char *)malloc(malloc_size);
  21.     if (NULL == rsa) {
  22.         RSA_free(key);
  23.         return 0;
  24.     }
  25.  
  26.     if (0 == RSA_sign(NID_md5, message, message_len, rsa, &rsa_size, key)) {
  27.         free(rsa);
  28.         RSA_free(key);
  29.         return 0;
  30.     }
  31.     RSA_free(key);
  32.  
  33.     if (rsa_size/3 * 4 >= buf_size) { /* 结果的 Base64 编码后长度大于 buf_size, 等于也错误(后面需要放 '\0' 的位置) */
  34.         free(rsa);
  35.         return 0;
  36.     }
  37.  
  38.     b64_encode(rsa, rsa_size, buf);
  39.  
  40.     free(rsa);
  41.     return 1;
  42. }

可是找遍 PyCrypto, 楞是没有看到怎么实现 PEM_read_bio_RSAPrivateKey 这样从一个 PEM 格式的私钥文件里加载的功能。然后去看哪些基于 PyCrypto 包装的其他的包,比如 ezPyCrypto, Keyczar(这还是 google 发布的),包括 mail-list 上提供的 patch.... 这些似乎倒是可以 load key 了,可缺省好像也并没有对 NID_MD5 的支持, 绝望~~~~

于是抱着试试的心态用 ctypes 直接去调 libcrypto——以前只是在 PyS60 上玩票似的用过 ctypes,以及在 Win32 下用过,而且那时候 ctypes 还不太成熟——结果发现 python2.5 的 ctypes 竟如此的好用:

  1. import ctypes
  2. import base64
  3.  
  4. crypto_handler = ctypes.cdll.LoadLibrary("/usr/lib/libcrypto.so")
  5. BIO_new_file = crypto_handler.BIO_new_file
  6. PEM_read_bio_RSAPrivateKey = crypto_handler.PEM_read_bio_RSAPrivateKey
  7. RSA_size = crypto_handler.RSA_size
  8. RSA_sign = crypto_handler.RSA_sign
  9. BIO_free = crypto_handler.BIO_free
  10. RSA_free = crypto_handler.RSA_free
  11. NID_md5 = 4
  12.  
  13. bio = BIO_new_file("/yourpathfile.key", "r")
  14. key = PEM_read_bio_RSAPrivateKey(bio, 0, 0, 0)
  15. r = BIO_free(bio)
  16. if r != 1:
  17.     # break here
  18.     print 'BIO_free error'
  19.  
  20. rsa_size = RSA_size(key)
  21. rsa = ctypes.create_string_buffer(rsa_size)
  22. sign_size = ctypes.create_string_buffer(4)
  23.  
  24. message = "foobar"
  25. if 1 != RSA_sign(NID_md5, message, len(message), rsa, sign_size, key):
  26.     print 'RSA_sign error'
  27. RSA_free(key)
  28.  
  29. print base64.urlsafe_b64encode(rsa.raw)
Topic: