热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用DES算法对数据加密

DES算法☆提供高质量的数据保护,防止数据未经授权的泄露和未被察觉的修改☆具有相当高的复杂性,使得破译的开销超过可能获得的利益,同时又要便于理解和掌握☆DES密码体制的安全性应该不依赖于算法的

DES算法

☆提供高质量的数据保护,防止数据未经授权的泄露和未被察觉的修改

☆具有相当高的复杂性,使得破译的开销超过可能获得的利益,同时又要便于理解和掌握

☆DES密码体制的安全性应该不依赖于算法的保密,其安全性仅以加密密钥的保密为基础

☆实现经济,运行有效,并且适用于多种完全不同的应用

 

苹果本身支持DES加密,在项目中引入头文件 CommonCrypto/CommonCryptor.h 即可使用相关函数.

我自己对其进行了封装,支持ARC与非ARC

YXCrypto.h

//
// YXCrypto.h
// 用秘钥给字符串加密或者解密
//
// Created by YouXian on 14-3-18.
// Copyright (c) 2014年 YouXian. All rights reserved.
//

#import

@interface YXCrypto : NSObject

/*!
* 给字符串加密
*/
+ (NSString *)DesEncryptString:(NSString*)src WithKey:(NSString *)key;

/*!
* 给字符串解密
*/
+ (NSString *)DesDecryptString:(NSString*)src WithKey:(NSString *)key;

@end

YXCrypto.m

//
// YXCrypto.m
// 用秘钥给字符串加密或者解密
//
// Created by YouXian on 14-3-18.
// Copyright (c) 2014年 YouXian. All rights reserved.
//

#import "YXCrypto.h"
#import

#if __has_feature(objc_arc)
// ARC
#define Auto_Release(obj)
#define Safe_Release(obj)
#else
// 非ARC
#define Auto_Release(obj) [obj autorelease]
#define Safe_Release(obj) [obj release]; obj = nil
#endif

static YXCrypto *shareInstance = nil;

@implementation YXCrypto

/*!
* 给字符串加密
*/
+ (NSString *)DesEncryptString:(NSString*)src WithKey:(NSString *)key {
NSString
* strRet = @"";

if (shareInstance == nil)
{
shareInstance
= [[YXCrypto alloc] init];
}

// encrypt source content
NSData* bytes = [src dataUsingEncoding:NSUTF8StringEncoding];
NSData
* data = [shareInstance DesCryptWithOperation:kCCEncrypt
bytes:bytes
key:key];

// format bytes to visible string
char* pBuff = (char*)[data bytes];
for (int i=0; i) {
strRet = [strRet stringByAppendingFormat:@"%02X", pBuff[i]& 0xFF];
}
return strRet;
}

/*!
* 给字符串解密
*/
+ (NSString *)DesDecryptString:(NSString*)src WithKey:(NSString *)key
{
if (shareInstance == nil)
{
shareInstance
= [[YXCrypto alloc] init];
}

static unsigned char _map_ch2hex[] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0, 0, 0, 0, 0, 0, 0, // :, ;, <, =, >, ?, @,
0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
};

// decode source content to bytes
unsigned char* bytes = (unsigned char*)malloc((src.length+1)*sizeof(unsigned char));
[[src uppercaseString] getCString:(
char*)bytes maxLength:src.length+1 encoding:NSUTF8StringEncoding];
unsigned
char *p1 = bytes, *p2 = bytes;
int n = src.length/2;
for (int i=0; i) {
*p1 = _map_ch2hex[*p2-'0'] * 0x10 + _map_ch2hex[*(p2+1)-'0'];
p1
++;
p2
+=2;
}
NSData
* data = [NSData dataWithBytes:bytes
length:n];

// decrypt source bytes
NSData* dataOut = [shareInstance DesCryptWithOperation:kCCDecrypt
bytes:data
key:key];
free(bytes);

NSString
* strRet = [[NSString alloc] initWithData:dataOut
encoding:NSUTF8StringEncoding];
Auto_Release(strRet);

return strRet;
}

- (NSData *)DesCryptWithOperation:(CCOperation)operation bytes:(NSData*)bytes key:(NSString *)key {

NSUInteger dataLength
= [bytes length];

size_t bufferSize
= ([bytes length] + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1);
unsigned
char *buffer = (unsigned char *)malloc(bufferSize*sizeof(unsigned char));
memset((
void*)buffer, 0, bufferSize);

size_t numBytesCrypted
= 0;
CCCryptorStatus cryptStatus
= CCCrypt(operation,
kCCAlgorithmDES,
kCCOptionPKCS7Padding
| kCCOptionECBMode,
(
void const*)[key UTF8String],
kCCKeySizeDES,
NULL,
[bytes bytes], dataLength,
(
void*)buffer, bufferSize,
&numBytesCrypted);
NSData
* dataRet = nil;
if (cryptStatus == kCCSuccess) {
dataRet
= [[NSData alloc] initWithBytes:buffer length:numBytesCrypted];
Auto_Release(dataRet);
}
free(buffer);
return dataRet;
}

@end

使用:

 

 

附录1:https://github.com/alfaromeodev/Cryptor

//
// Cryptor.h
// test
//
// Created by Da Zhang on 11/5/12.
// Copyright 2012 __MyCompanyName__. All rights reserved.
//

#import
#import
#import


@interface Cryptor : NSObject {

}

/*
all the methods below only support utf8 string
*/
+ (NSString *)encodeMD5:(NSString *)str;

+ (NSString *)encodeDES:(NSString *)plainString key:(NSString *)key;
+ (NSString *)decodeDES:(NSString *)decodedString key:(NSString*)key;

+ (NSString *)encodeBase64:(NSString *)plainString;
+ (NSString *)decodeBase64:(NSString *)decodedString;

@end
//
// Cryptor.m
// test
//
// Created by Da Zhang on 11/5/12.
// Copyright 2012 __MyCompanyName__. All rights reserved.
//

#import "Cryptor.h"

@interface Cryptor ()

+ (NSString *)encodeBase64WithData:(NSData *)objData;
+ (NSData *)decodeBase64WithUTF8String:(NSString *)strBase64;
+ (NSString *)parseByte2HexString:(Byte *)bytes;

@end


static const char _base64EncodingTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const short _base64DecodingTable[256] = {
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
-2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};


@implementation Cryptor


#pragma mark private section
+ (NSData *)decodeBase64WithUTF8String:(NSString *)strBase64 {
const char * objPointer = [strBase64 cStringUsingEncoding:NSUTF8StringEncoding];
int intLength = strlen(objPointer);
int intCurrent;
int i = 0, j = 0, k;

unsigned
char * objResult;
objResult
= calloc(intLength, sizeof(char));

// Run through the whole string, converting as we go
while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) {
if (intCurrent == '=') {
if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) {
// the padding character is invalid at this point -- so this entire string is invalid
free(objResult);
return nil;
}
continue;
}

intCurrent
= _base64DecodingTable[intCurrent];
if (intCurrent == -1) {
// we're at a whitespace -- simply skip over
continue;
}
else if (intCurrent == -2) {
// we're at an invalid character
free(objResult);
return nil;
}

switch (i % 4) {
case 0:
objResult[j]
= intCurrent <<2;
break;

case 1:
objResult[j
++] |= intCurrent >> 4;
objResult[j]
= (intCurrent & 0x0f) <<4;
break;

case 2:
objResult[j
++] |= intCurrent >>2;
objResult[j]
= (intCurrent & 0x03) <<6;
break;

case 3:
objResult[j
++] |= intCurrent;
break;
}
i
++;
}

// mop things up if we ended on a boundary
k = j;
if (intCurrent == '=') {
switch (i % 4) {
case 1:
// Invalid state
free(objResult);
return nil;

case 2:
k
++;
// flow through
case 3:
objResult[k]
= 0;
}
}

// Cleanup and setup the return NSData
NSData * objData = [[[NSData alloc] initWithBytes:objResult length:j] autorelease];
free(objResult);
return objData;
}

+ (NSString *)encodeBase64WithData:(NSData *)objData {
const unsigned char * objRawData = [objData bytes];
char * objPointer;
char * strResult;

// Get the Raw Data length and ensure we actually have data
int intLength = [objData length];
if (intLength == 0) return nil;

// Setup the String-based Result placeholder and pointer within that placeholder
strResult = (char *)calloc(((intLength + 2) / 3) * 4, sizeof(char));
objPointer
= strResult;

// Iterate through everything
while (intLength > 2) { // keep going until we have less than 24 bits
*objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
*objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) <<4) + (objRawData[1] >> 4)];
*objPointer++ = _base64EncodingTable[((objRawData[1] & 0x0f) <<2) + (objRawData[2] >> 6)];
*objPointer++ = _base64EncodingTable[objRawData[2] & 0x3f];

// we just handled 3 octets (24 bits) of data
objRawData += 3;
intLength
-= 3;
}

// now deal with the tail end of things
if (intLength != 0) {
*objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
if (intLength > 1) {
*objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) <<4) + (objRawData[1] >> 4)];
*objPointer++ = _base64EncodingTable[(objRawData[1] & 0x0f) <<2];
*objPointer++ = '=';
}
else {
*objPointer++ = _base64EncodingTable[(objRawData[0] & 0x03) <<4];
*objPointer++ = '=';
*objPointer++ = '=';
}
}

// Terminate the string-based result
*objPointer = '\0';

// Return the results as an NSString object
return [NSString stringWithCString:strResult encoding:NSUTF8StringEncoding];
}

+ (NSString *)parseByte2HexString:(Byte *)bytes {
NSMutableString
*hexStr = [[NSMutableString alloc]init];
int i = 0;
if(bytes) {
while (bytes[i] != '\0') {
NSString
*hexByte = [NSString stringWithFormat:@"%x",bytes[i] & 0xff];///16进制数
if([hexByte length]==1) [hexStr appendFormat:@"0%@", hexByte];
else [hexStr appendFormat:@"%@", hexByte];
i
++;
}
}
//NSLog(@"bytes 的16进制数为:%@",hexStr);
return hexStr;
}


#pragma mark public section
+ (NSString *)encodeMD5:(NSString *)str {

const char *cStr = [str UTF8String];

unsigned
char result[CC_MD5_DIGEST_LENGTH];

CC_MD5( cStr, strlen(cStr), result );

return [NSString

stringWithFormat:
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",

result[
0], result[1],

result[
2], result[3],

result[
4], result[5],

result[
6], result[7],

result[
8], result[9],

result[
10], result[11],

result[
12], result[13],

result[
14], result[15]

];

}

+ (NSString *)encodeBase64:(NSString *)plainString {
return [Cryptor encodeBase64WithData:[plainString dataUsingEncoding:NSUTF8StringEncoding]];
}

+ (NSString *)decodeBase64:(NSString *)decodedString {
NSData
* objData = [Cryptor decodeBase64WithUTF8String:decodedString];
return [[NSString alloc] initWithData:objData encoding:NSUTF8StringEncoding];
}

+ (NSString *)encodeDES:(NSString *)plainString key:(NSString *)key {
NSString
*ciphertext = nil;
const char *textBytes = [plainString UTF8String];
NSUInteger dataLength
= strlen(textBytes);
unsigned
char buffer[1024];
memset(buffer,
0, sizeof(char));
Byte iv[]
= {1,2,3,4,5,6,7,8};
size_t numBytesEncrypted
= 0;
CCCryptorStatus cryptStatus
= CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
[key UTF8String],
kCCKeySizeDES,
iv,
textBytes,
dataLength,
buffer,
1024,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSData
*data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
ciphertext
= [[Cryptor encodeBase64WithData:data] autorelease];
}
return ciphertext;
}

+ (NSString *)decodeDES:(NSString*)decodedString key:(NSString*)key {
NSData
* cipherData = [Cryptor decodeBase64WithUTF8String:decodedString];
unsigned
char buffer[1024];
memset(buffer,
0, sizeof(char));
size_t numBytesDecrypted
= 0;
Byte iv[]
= {1,2,3,4,5,6,7,8};
CCCryptorStatus cryptStatus
= CCCrypt(kCCDecrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding,
[key UTF8String],
kCCKeySizeDES,
iv,
[cipherData bytes],
[cipherData length],
buffer,
1024,
&numBytesDecrypted);
NSString
* plainText = nil;
if (cryptStatus == kCCSuccess) {
NSData
* data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
plainText
= [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
}
return plainText;
}

@end

附录2:AES加密

NSData+AES.h + NSData+AES.m

#import 

/**
* Adds AES encryption and decryption capabilities to NSData.
*/
@interface NSData (AES)

/**
* Encrypt NSData using AES256 with a given symmetric encryption key.
* @param key The symmetric encryption key
*/
- (NSData *)AES256EncryptWithKey:(NSString *)key;

/**
* Decrypt NSData using AES256 with a given symmetric encryption key.
* @param key The symmetric encryption key
*/
- (NSData *)AES256DecryptWithKey:(NSString *)key;

@end
#import "NSData+AES.h"
#import

@implementation NSData (AES)

- (NSData *)AES256EncryptWithKey:(NSString *)key
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

NSUInteger dataLength
= [self length];

//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);

size_t numBytesEncrypted
= 0;
CCCryptorStatus cryptStatus
= CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL
/* initialization vector (optional) */,
[self bytes], dataLength,
/* input */
buffer, bufferSize,
/* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}

free(buffer);
//free the buffer;
return nil;
}

- (NSData *)AES256DecryptWithKey:(NSString *)key {
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

NSUInteger dataLength
= [self length];

//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);

size_t numBytesDecrypted
= 0;
CCCryptorStatus cryptStatus
= CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL
/* initialization vector (optional) */,
[self bytes], dataLength,
/* input */
buffer, bufferSize,
/* output */
&numBytesDecrypted);

if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}

free(buffer);
//free the buffer;
return nil;
}

@end

 

 

 

 

 

 

 

 

 

 

 

 

 


推荐阅读
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • ECMA262规定typeof操作符的返回值和instanceof的使用方法
    本文介绍了ECMA262规定的typeof操作符对不同类型的变量的返回值,以及instanceof操作符的使用方法。同时还提到了在不同浏览器中对正则表达式应用typeof操作符的返回值的差异。 ... [详细]
author-avatar
手机用户2502909811
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有