2016年10月19日 星期三

function pointer 用法

 http://ccckmit.wikidot.com/cp:function





=========================================================================

當你在宣告一個變數時是這樣的:
int ImVar;//<-----------------------1
當你在宣告一個函式時卻是這樣:
int ImFun(...);//---------------------2
變數宣告時名稱在最後面,而函式名稱卻在中間,
你會不會覺得這很奇怪?
本來用一個小括號括起來的參數定義就是函式名稱的附屬品
你可以當它是函式名稱的一部份。沒有了它函式名稱就不完整了。
(注意在C++中不同參數的同名函式在編譯器的內部函式名稱是不同的)

typedef int INT;//<------------------3
typedef int *PINT;//<--------------4
typedef int (*PINT);//<--------------5
3式是定義一個int的型態,名為INT
4式是定義一個int的指標型態,名為PINT
5式是定義一個指向int的指標型態,名為PINT
4式和5式的結果是等效的。

現在我們嘗試為函式定義型態:
typedef int IntFun(...);//<------------6
先注意到有關2式的說明,就不應再對為何函式名稱後還有(...)
6式定義一個型態(或返回)int函式,名稱為IntFun。
我們知道,函式名本身俱有隱性指標的性質,所以IntFun和 *IntFun是
等效的。
那麼可以明白的定義IntFun為指標嗎,應該可以的!直觀的感覺是套入
4式:
typedef int * IntFun(...);//<------------7
問題出來了,任何一個編譯器都會把7式解讀為:
定義一個型態(或返回)int *函式,名稱為IntFun。
這不是我們要的,那要如何指定指標('*')給IntFun而不是int呢?
答案就是括號,也就是套入5式而不是4式:
typedef int (*IntFun)(...);//<------------8
這就是原提問要的解答了,唯要注意的是
對型態的定義來說 4式和5式是等效的,
但對函式的定義來說6式和8式才是等效的;
那麼使用6式或8式效好?
一般都使用8弍,它有較好的可讀性,隱式指標總是令人較為困感的。
而且也不敢保證所有的編譯器都可以接受6式的敘述。





==========================================================================

Function Pointer:(指向函數的指標)

int (*pf)(int);

在指標的藝術一書(p.87)中又名Pointer to function。目的是經過宣告後,會有一個指標是指向函數的起始位址,當我們要使用某個函數的時候,只要將此指標指向那個函數,就可使用,其實函數名稱是一個位址,因此只要將函數名稱設定給此指標即可。

使用函數指標的時候必須注意指標的引入與回傳參數是否與原函式匹配,就說原函式引入、回傳的資料型態,引入參數個數,通通要一樣才可以。

例如三個副函式:

(1) int add(int);
(2) float add2(float);
(3) int add3(int,int);

並且宣告一個函式指標:
int (*pf)(int);

則:

(a.) pf = add;     //正確
(b.) pf = add2;   //錯誤,參數資料型態不匹配
(c.) pf = add3;   //錯誤,引入參數個數不匹配

#################################################################################

Function return a pointer:(回傳指標的函數)

int *pf(int); //少了一個括號cf: int (*pf)(int);

表示pf是一個函數,這個函數會引入一個整數型態的參數,經過函數運算後會回傳一個整數型態的指標(回傳的東西是一個位址,假設回傳x,則x可以是指標(x存的內容是指向的位址),也可以是變數的位址,若x是一般變數,則回傳&x;若x是陣列則回傳x。

#################################################################################

typedef與函數指標(from wiki typedef)

Using typedef with function pointers[edit source | editbeta]

Function pointers are somewhat different than all other types because the syntax does not follow the pattern typedef <old type name> <new alias>;. Instead, the new alias for the type appears in the middle between the return type (on the left) and the argument types (on the right). Consider the following code, which does not use a typedef:
int do_math(float arg1, int arg2) {
    return arg2;
}
 
int call_a_func(int (*call_this)(float, int)) {
    int output = call_this(5.5, 7);
    return output;
}
 
int final_result = call_a_func(do_math);
This code can be rewritten with a typedef as follows:
typedef int (*MathFunc)(float, int);
 
int do_math(float arg1, int arg2) {
    return arg2;
}
 
int call_a_func(MathFunc call_this) {
    int output = call_this(5.5, 7);
    return output;
}
 
int final_result = call_a_func(do_math);
Here, MathFunc is the new alias for the type. A MathFunc is a pointer to a function that returns an integer and takes as arguments a float followed by an integer.

typedef int (*MathFunc)(float, int);是別名的宣告,就只是在最前面加上typedef則以後就可以只用MathFunc這樣的名稱來宣告同類型的函數指標。










2016年10月16日 星期日

Security Link

阿里聚安全
http://jaq.alibaba.com/community/index?spm=a313e.7916648.1000000.6.rfNbWG

[轉] Android 應用安全開發之淺談加密演算法

Android開發中,難免會遇到需要加解密一些數據內容存到本地文件、或者通過網絡傳輸到其他服務器和設備的問題,但並不是使用了加密就絕對安全了,如果加密函數使用不正確,加密數據很容易受到逆向破解攻擊。還有很多開發者沒有意識到的加密算法的問題。

1. 需要瞭解的基本概念

密碼學的三大作用:加密( Encryption)、認證(Authentication),鑑定(Identification)
  • 加密:防止壞人獲取你的數據。
  • 認證:防止壞人修改了你的數據而你卻並沒有發現。
  • 鑑權:防止壞人假冒你的身份。
明文、密文、密鑰、對稱加密算法、非對稱加密算法,這些基本概念和加密算法原理就不展開敘述了。

2. Android SDK提供的API

2.1 Android 加密相關API結構

Android SDK使用的API和JAVA提供的基本相似,由 Java Cryptography Architecture (JCA,java加密體系結構) ,Java Cryptography Extension (JCE,Java加密擴展包) ,Java Secure Sockets Extension(JSSE,Java安全套接字擴展包),Java Authentication and Authentication Service(JAAS,Java 鑑別與安全服務)組成。
JCA提供基本的加密框架,如證書、數字簽名、消息摘要和密鑰對產生器,對應的Android API中的以下幾個包:
java.security 
java.security.acl
java.security.cert
java.security.interfaces
java.security.spec
JCE擴展了JCA,提供了各種加密算法、摘要算法、密鑰管理等功能,對應的Android API中的以下幾個包:
javax.crypto 
javax.crypto.interfaces 
javax.crypto.spec
JSSE提供了SSL(基於安全套接層)的加密功能,使用HTTPS加密傳輸使用,對應的Android API主要是java.net.ssl包中。
JAAS 提供了在Java平台上進行用戶身份鑑別的功能。對應的Android API主要在以下幾個包:
javax.security.auth 
javax.security.auth.login 
javax.security.auth.callback 
javax.security.auth.x500
它們其實只是一組接口,實際的算法是可由不同的Provider提供,Android API默認的Provider主要是是Bouncy Castle和OpenSSL。
此外Android API還提供了android.security和android.security.keystore(API 23新增)來管理keychain和keystore。

2.2 Base64編碼算法

Base64編碼算法是一種用64個字符(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)來表示任意二進制數據的方法。在計算機網絡發展的早期,由於「歷史原因」,電子郵件不支持非ASCII碼字符,如果要傳送的電子郵件帶有非ASCII碼字符(諸如中文)或者圖片,用戶收到的電子郵件將會是一堆亂碼,因此發明了Base64編碼算法。至於為何會亂碼?請大家自行Google。在加解密算法中,原始的數據和加密後的數據一般也是二進制數據,為了不傳輸出錯,方便保存或者調試代碼,一般需要對加密後的數據進行base64編碼。
Android提供了Base64編碼的工具類android.util.Base64,可以直接使用,不用自己去實現base64編碼的算法了。 如:
byte[] output = sha.digest();
String result = Base64.encodeToString(output, Base64.DEFAULT);
開發者建議:base64只是一種編碼方式,並不是一種加密算法,不要使用base64來加密數據。

2.3 隨機數生成器

在Android加密算法中需要隨機數時要使用SecureRandom來獲取隨機數。 如:
SecureRandom sr = new SecureRandom();
byte[] output = new byte[16];
sr.nextBytes(output);
注意不要給SecureRandom設置種子。調用seeded constructor或者setSeed(byte[])是不安全的。SecureRandom()默認使用的是dev/urandom作為種子產生器,這個種子是不可預測的。
開發者建議:
  1. 不要使用Random類來獲取隨機數。
  2. 在使用SecureRandom時候,不要設置種子。使用以下函數設置種子都是有風險的:
    SecureRandom.SecureRandom(byte[] seed) 
    SecureRandom.setSeed(long seed) 
    SecureRandom.setSeed(byte[] seed)

2.4 Hash算法

Hash算法是指任意長度的字符串輸入,此算法能給出固定n比特的字符串輸出,輸出的字符串一般稱為Hash值。
具有以下兩個特點:
  • 抗碰撞性:尋找兩個不同輸入得到相同的輸出值在計算上是不可行的,需要大約 的時間去尋找到具有相同輸出的兩個輸入字符串。
  • 不可逆:不可從結果推導出它的初始狀態。
抗碰撞性使Hash算法對原始輸入的任意一點更改,都會導致產生不同的Hash值,因此Hash算法可以用來檢驗數據的完整性。我們經常見到在一些網站下載某個文件時,網站還提供了此文件的hash值,以供我們下載文件後檢驗文件是否被篡改。
不可逆的特性使Hash算法成為一種單向密碼體制,只能加密不能解密,可以用來加密用戶的登錄密碼等憑證。
開發者建議:
  1. 建議使用SHA-256、SHA-3算法。 如使用SHA-256算法對message字符串做哈希:
    ...
    byte[] input = message.getBytes();
    MessageDigest sha = MessageDigest.getInstance("SHA-256");
    sha.update(input);
    byte[] output = sha.digest();
    String result = Base64.encodeToString(output, Base64.DEFAULT);
  2. 不建議使用MD2、MD4、MD5、SHA-1、RIPEMD算法來加密用戶密碼等敏感信息。這一類算法已經有很多破解辦法,例如md5算法,網上有很多查詢的字典庫,給出md5值,可以查到加密前的數據。
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] md5Bytes = md.digest(str.getBytes());
    String result = Base64.encodeToString(md5Bytes, Base64.DEFAULT);
  3. 不要使用哈希函數做為對稱加密算法的簽名。
  4. 注意:當多個字符串串接後再做hash,要非常當心。**
    • 如:字符串S,字符串T,串接做hash,記為 H (S||T)。但是有可能發生以下情況。
    • 如「builtin||securely」 和 「built||insecurely」的hash值是完全一樣的。
如何修改從而避免上述問題產生? 改為H(length(S) || S || T)或者 H(H(S)||H(T))或者H(H(S)||T)。
實際開發過程中經常會對url的各個參數,做詞典排序,然後取參數名和值串接後加上某個SECRET字符串,計算出hash值,作為此URL的簽名,如foo=1, bar=2, baz=3 排序後為bar=2, baz=3, foo=1,做hash的字符串為:SECRETbar2baz3foo1,在參數和值之間沒有分隔符,則」foo=bar」和」foob=ar」的hash值是一樣的,」foo=bar&fooble=baz」和」foo=barfooblebaz」一樣,這樣通過精心構造的惡意參數就有可能與正常參數的hash值一樣,從而騙過服務器的簽名校驗。

2.5 消息認證算法

要確保加密的消息不是別人偽造的,需要提供一個消息認證碼(MAC,Message authentication code)。
  • 消息認證碼是帶密鑰的hash函數,基於密鑰和hash函數。
  • 密鑰雙方事先約定,不能讓第三方知道。
  • 消息發送者使用MAC算法計算出消息的MAC值,追加到消息後面一起發送給接收者。
  • 接收者收到消息後,用相同的MAC算法計算接收到消息MAC值,並與接收到的MAC值對比是否一樣。
開發者建議: 建議使用HMAC-SHA256算法,避免使用CBC-MAC。
HMAC-SHA256例子如下:
//初始化KeyGenerator
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA256");
//產生密鑰
SecretKey secretKey = keyGenerator.generateKey();
//獲取密鑰
byte[] key = secretKey.getEncoded();
Log.d(Base64.encodeToString(key, Base64.DEFAULT));

//還原密鑰
SecretKey restoreSecretKey = new SecretKeySpec(key, "HmacSHA256");
//實例化MAC
Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());
//初始化MAC
mac.init(restoreSecretKey);
//執行摘要
byte[] hmacSHA256Bytes = mac.doFinal(message.getBytes());
result = Base64.encodeToString(hmacSHA256Bytes, Base64.DEFAULT);

2.6 對稱加密算法

在對稱加密算法中,數據發信方將明文(原始數據)和加密密鑰一起經過特殊加密算法處理後,使其變成複雜的加密密文發送出去。收信方收到密文後,若想解讀原文,則需要使用加密用過的密鑰及相同算法的逆算法對密文進行解密,才能使其恢復成可讀明文。在對稱加密算法中,使用的密鑰只有一個,發收信雙方都使用這個密鑰對數據進行加密和解密,這就要求解密方事先必須知道加密密鑰。
該算法的缺點是,如果一旦密鑰洩漏,那麼加密的內容將都不可信了。
開發者建議:
  1. 建議使用AES算法。
  2. DES默認的是56位的加密密鑰,已經不安全,不建議使用。
  3. 注意加密模式不要使用ECB模式。ECB模式不安全,說明問題的經典的三張圖片,如 明文是:
    用ECB加密模式後:
    用CBC加密模式後:
     想更深入的瞭解關於對CBC加密模式的攻擊,可參看:《SSL/TLS協議安全系列:CBC 模式的弱安全性介紹(一)》http://drops.wooyun.org/tips/6619
  4. Android 提供的AES加密算法API默認使用的是ECB模式,所以要顯式指定加密算法為:CBC或CFB模式,可帶上PKCS5Padding填充。AES密鑰長度最少是128位,推薦使用256位。
    //生成KEY
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(256);
    //產生密鑰
    SecretKey secretKey = keyGenerator.generateKey();
    //獲取密鑰
    byte[] keyBytes = secretKey.getEncoded();
    Log.d("AES KEY", Base64.encodeToString(keyBytes, 0));
    
    //還原密鑰
    SecretKey key = new SecretKeySpec(keyBytes, "AES");
    
    //加密
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] encodeResult = cipher.doFinal(plainText.getBytes());
    Log.d("AES encode", Base64.encodeToString(encodeResult, Base64.DEFAULT));
    

2.7 非對稱加密

非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰對數據進行加密,那麼只有用對應的公開密鑰才能解密(這個過程可以做數字簽名)。
非對稱加密主要使用的是RSA算法。
開發者建議:
  1. 注意密鑰長度不要低於512位,建議使用2048位的密鑰長度。使用RSA進行數字簽名的算法,如:
    //生成密鑰
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
    RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
    
    //簽名            
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    Signature signature = Signature.getInstance("SHA256withRSA");
    signature.initSign(privateKey);
    signature.update(src.getBytes());
    byte[] result = signature.sign();
  2. 使用RSA算法做加密,RSA加密算法應使用Cipher.getInstance(RSA/ECB/OAEPWithSHA256AndMGF1Padding),否則會存在重放攻擊的風險。如:
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
    RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
    
    //公鑰加密
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
    KeyFactory keyFactory= KeyFactory.getInstance("RSA");
    PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA256AndMGF1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    byte[] result = cipher.doFinal(src.getBytes());
    ...
    //私鑰解密
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
    KeyFactory keyFactory2 = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = keyFactory2.generatePrivate(pkcs8EncodedKeySpec);
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA256AndMGF1Padding");
    cipher5.init(Cipher.DECRYPT_MODE, privateKey5);
    byte[] result2 = cipher.doFinal(result);

2.8 加密算法PBE

PBE是一種基於口令的加密算法,其特點是使用口令代替了密鑰,而口令由用戶自己掌管,採用隨機數雜湊多重加密等方法保證數據的安全性。
開發者建議:使用基於口令的加密算法PBE時,生成密鑰時要加鹽,鹽的取值最好來自SecureRandom,並指定迭代次數。如:
//初始化鹽
mSalt = new byte[SALT_LENGTH_BYTES];
SecureRandom sr = new SecureRandom();
sr.nextBytes(mSalt);

SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(KEY_GENERATOR_MODE);
keySpec = new PBEKeySpec(password, salt, KEY_GEN_ITERATION_COUNT, KEY_LENGTH_BITS);
secretKey = secretKeyFactory.generateSecret(keySpec);
(以上所有示例算法僅供參考)

3. 總結

幾條原則:
  1. 不要自己設計加密算法和協議,使用業界標準的算法。
  2. 對稱加密算法不要使用ECB模式,不建議使用DES算法。
  3. 要選擇合適長度的密鑰。
  4. 要確保隨機數生成器的種子具有足夠的信息熵。
  5. 不要使用沒有消息認證的加密算法加密消息,無法防重放。
  6. 當多個字符串拼接後做hash,要非常當心。
  7. 當給算法加yan鹽取值時不要太短,不要重複。
  8. 使用初始化向量時IV時,IV為常量的CBC,CFB,GCM等和ECB一樣可以重放,即採用上一個消息的最後一塊密文作為下一個消息的IV,是不安全的。
  9. 密鑰應遵循的原則
    1. 密鑰不能為常量,應隨機,定期更換,如果加密數據時使用的密鑰為常量,則相同明文加密會得到相同的密文,很難防止字典攻擊。
    2. 開發同學要防範密鑰硬編碼的毛病。
而在實際開發中,密鑰如何保存始終是繞不過的坎?如果硬編碼在代碼中容易被逆向,如果放在設備的某個文件,也會被有經驗的破解者逆向找到,在這裡推薦阿里聚安全的安全組件服務,其中的安全加密功能提供了開發者密鑰的安全管理與加密算法實現,保證密鑰的安全性,實現安全的加解密操作。

參考

  1. 《Java加密與解密的藝術》
  2. 《Android Application Secure Design/Secure Coding Guidebook》

瞭解更多技術乾貨和最新安全年報,請點擊這裡