Bootstrap

linux crypt函数

linux crypt函数
1. crypt定义
#define _XOPEN_SOURCE /* See feature_test_macros(7) */
#include <unistd.h >
char *crypt(const char *key, const char *salt);

上面是man 3 crypt看到的crypt函数定义。
从定义中看到要想使用crypt函数那么就得定义_XOPEN_SOURCE宏,有一些人只是把unistd.h包含进来,然后发现编译的时候出现crypt未定义的情况。

2. key、salt
在TLPI一书中提到“crypt()算法会接受一个最长可达8字符的密钥(即key),并施以数据加密算法(DES)的一种变体。salt参数指向一个两个字符的字符串,用来扰动(改变)DES算法。该函数返回一个指针,指向长度13个字符的字符串。”,这个对crypt函数的一个看似简单的描述让我顿时产生了两个疑问:

难道crypt函数最多只能处理8字符的密钥吗?是不是意味着crypt(“liulaogen123”, “salt”)==crypt(“liulaogen234”, “salt”)?

salt最多只能是两个字符吗?那这个salt得组合那就非常有限,对于防止碰撞就非常弱了。

“实践是检验真理的唯一标准”,那么我们就用代码来验证我的疑问:

/*************************************************************************
    > File Name: auth.c
    > Author: lxg
    > Mail: [email protected]
    > Created Time: 2015年06月28日 星期日 10时27分19秒
 ************************************************************************/

#define _XOPEN_SOURCE
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        fprintf(stderr, "%s salt key\n", argv[0]);
        return 1;
    }
    char *encrypted = NULL;

    if((encrypted = crypt(argv[2], argv[1])) == NULL)
    {
        fprintf(stderr, "crypt error:%s\n", strerror(errno));
    }

    printf("%s encrypted:%s\n", argv[2], encrypted);

    return 0;
}

lxg@lxg-X240:~/station/TLPI/chapter_8$ ./auth abc liulaogen123
liulaogen123 encrypted:abJHmGH1Hal.6
lxg@lxg-X240:~/station/TLPI/chapter_8$ ./auth abc liulaogen234
liulaogen234 encrypted:abJHmGH1Hal.6
lxg@lxg-X240:~/station/TLPI/chapter_8$ ./auth abc liulaogn234
liulaogn234 encrypted:abjcY/j5FUQVg

从上面的输出来看确实验证了我们的第一个疑问,crypt接受一个最长可达8字符的密钥key,大于8个字符的部分会被丢弃。

lxg@lxg-X240:~/station/TLPI/chapter_8$ ./auth abc liulaogn234
liulaogn234 encrypted:abjcY/j5FUQVg
lxg@lxg-X240:~/station/TLPI/chapter_8$ ./auth abd liulaogn234
liulaogn234 encrypted:abjcY/j5FUQVg
lxg@lxg-X240:~/station/TLPI/chapter_8$ ./auth acd liulaogn234
liulaogn234 encrypted:acJlJNjY0hjOQ

上面的这个示例解答了第二个疑问,crypt的salt指向一个两个字符的字符串,大于2个字符的部分会被丢弃。
上面的两个示例的输出来看无论输入是什么输出的的都是长度为13个字符的字符串。

3. crypt的算法
crypt默认使用的密钥长度为56bit的变体DES算法,我们知道56bit的DES加密在现在日益加强的软、硬件条件下很容易就被破解了,在crypt的man手册中也给出了warning:

Warning: The key space consists of 2**56 equal 7.2e16 possible values. Exhaustive searches of this key space are possible using massively parallel computers. Software, such as crack(1), is available which will search the portion of this key space that is generally used by humans for passwords. Hence, password selection should, at minimum, avoid common words and names. The use of a passwd(1) program that checks for crackable passwords during the selection process is recommended.

那么crypt还有其它的算法吗?因为从上面的key、salt章节和默认使用的DES算法来看这个函数基本是不能用的,我们伟大的linux应该是不会允许这样弱的东西存在的。那么我们接着man手册往下看,在NOTES中我们发现了crypt更强大的部分,在glibc2版本的crypt函数中支持额外的加密算法。
crypt支持的额外加密算法有MD5、Blowfish(某些特定的Linux系统支持)、SHA-256(glibc2.7开始)、SHA-512(glibc2.7开始),怎么来区分这些额外的加密算法呢?那就是通过salt来进行区分,如果salt以“$id$salt$encrypted” 这样的格式那么就会根据id的不同值选择不同的加密算法来代替默认的DES算法,id的格式如下:

          ID  | Method
          ─────────────────────────────────────────────
          1   | MD5
          2a  | Blowfish (not in mainline glibc; added in some
              | Linux distributions)
          5   | SHA-256 (since glibc 2.7)
          6   | SHA-512 (since glibc 2.7)
$5$salt$encrypted 的salt代表crypt函数使用SHA- 256加密算法
$6$salt$encrypted 的salt代表crypt函数使用SHA-512加密算法
$salt$ 中的salt最多支持长度为16字符的字符串,相比默认的DES算法支持最长为2字符有了很大的改进,最后crypt的返回值根据加密算法的不同也有不同长度的字符串返回。

   MD5     | 22 characters
   SHA-256 | 43 characters
   SHA-512 | 86 characters
4. linux密码验证
我们可以查看一下linux中(ubuntu)中密码验证使用的加密算法,怎么来确定呢?其实很简单,查看/etc/shadown文件中密码加密部分就能知道:

$6$at9hFDRi$bTr371ETSBzrRi3e3d229Bedn8g97shPktUcdGk2GlB2Nj2KWTxPmY0wBx A0Xn2395lAgX6ltLnbKvvzgXjhc.

上面是我截取的我电脑中shadow文件root用户的密码部分,从这里可以看出ubuntu使用的是AES-512加密算法。
关于密码推荐大家看这篇文章:passwords
 

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;