Skip to content

Instantly share code, notes, and snippets.

@remcotolsma
Created September 19, 2013 18:24
Show Gist options
  • Save remcotolsma/6627728 to your computer and use it in GitHub Desktop.
Save remcotolsma/6627728 to your computer and use it in GitHub Desktop.
This class can be used to encrypt, decrypt, sign and verify data in XML documents using the XMLSec standards. It uses the XMLSec library tools developed by Aleksey Sanin and others to perform the basic cryptographic operations on XML documents to comply with the W3 Consortium XMLSec standards: http://www.w3.org/TR/xmlenc-core/ http://www.w3.org/…
<?
/*
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# http://www.gnu.org/licenses/gpl.txt
#
/*
/*** types of key ***/
define ( 'XMLSEC_AES', 1 );
define ( 'XMLSEC_DES', 2 );
define ('XMLSEC_PRIV_PEM', 8);
define ('XMLSEC_PRIV_DER', 9);
define ('XMLSEC_PUB_PEM', 10);
define ('XMLSEC_PUB_DER', 11);
define ('XMLSEC_PKCS_DER', 20);
define ('XMLSEC_PKCS_PEM', 21);
define ('XMLSEC_ROOT_CA_PEM', 31);
define ('XMLSEC_ROOT_CA_DER',32 );
define ('XMLSEC_UNTRAST_CA_DER', 33 );
define ('XMLSEC_UNTRAST_CA_DER', 34 );
define ('XMLSEC_PUB_CERT_PEM', 40);
define ('XMLSEC_PUB_CERT_DER', 41);
/*** types of symmetric algorithm ***/
define ( 'XMLSEC_AES_128','aes128-cbc' );
define ( 'XMLSEC_AES_256','aes256-cbc' );
define ( 'XMLSEC_AES_192','aes192-cbc' );
define ( 'XMLSEC_DES_128','des128-cbc' );
define ( 'XMLSEC_DES_256','des256-cbc' );
define ( 'XMLSEC_3DES','tripledes-cbc' );
/*** types of dsignature algorithm ***/
define ( 'XMLSEC_DSA_SHA1','dsa-sha1' );
define ( 'XMLSEC_RSA_SHA1','rsa-sha1' );
/*** key Info ***/
define ( 'XMLSEC_X509DATA','X509Data' );
define ( 'XMLSEC_X509CERTIFICATE', 'X509Certificate');
/******
*
* class xmlsec - encrypt and decrypt data, design and verify xml data
* requried the installing libxmlsec from http://www.aleksey.com/xmlsec
*
* @author Alexandr M. Kalendarev <[email protected]>
*
* @access public
* @version 1.0
* @package cryptography.xml.xmlsec
*
******/
class xmlsec
{
/*****
* the full name of the xml keyfile
*****/
var $xmlkeyfilename = 'keys.xml';
var $errorMsg = '';
var $cmd;
var $unlink = true;
var $temp_path = '';
/******
* xmlsec constructor
*
* @parametr $xmlkeyfilename - the full name of the xml keyfile or
* default "keys.xml" in the current directory
* @parametr $temp_path - set writeable directory for temp files
* @parametr $unlink - unlink temp files if true (default) for debugging mode
*
******/
function xmlsec( $xmlkeyfilename ='' , $temp_path = '', $unlink = true)
{
if ( $xmlkeyfilename !='' )
$this->xmlkeyfilename = $xmlkeyfilename;
$this->unlink = $unlink;
if ($temp_path != '') $this->temp_path = $temp_path;
}
/******
* addkey - add key info into xml keyfile.
*
* @parametr $keyfilename - the name of keyfile
* @parametr $keyname - the name of key
* @parametr constant $type - the type of keyfile
* @parametr $cafiles - array of ca filenames: array( ca1_filename, ca2_filename)
******/
function addkey( $keyfilename , $keyname, $type , $cafiles = null )
{
/*
if (!file_exists( $keyfilename )) {
$this->errorMsg = "the keyfile $keyfilename d't exist";
return false;
}
*/
/*
if ( $keyname == '' ){
$this->errorMsg = "the keyname is null string";
return false;
}
*/
if (!preg_match("/^([\w_-])+$/", $keyname ) ){
$this->errorMsg = "the keyname must the alpabetic and numeric string";
return false;
}
$str_cafiles = '';
$keytype = '';
if ( $type == XMLSEC_AES )
$keytype = '--aeskey:'.$keyname;
if ( $type == XMLSEC_DES )
$keytype = '--deskey:'.$keyname;
if ( $type == XMLSEC_PRIV_PEM ){
$keytype = '--privkey-pem:'.$keyname;
if ( is_array($cafiles) )
$str_cafiles = ','.implode(',', $cafiles );
}
if ( $type == XMLSEC_PRIV_DER ){
$keytype = '--privkey-der:'.$keyname;
if ( is_array($cafiles) )
$str_cafiles = ','.implode(',', $cafiles );
}
if ( $type == XMLSEC_PKCS_PEM ){
$keytype = '--pkcs-pem:'.$keyname;
if ( is_array($cafiles) )
$str_cafiles = ','.implode(',', $cafiles );
}
if ( $type == XMLSEC_PKCS_DER ){
$keytype = '--pkcs8-der:'.$keyname;
if ( is_array($cafiles) )
$str_cafiles = ','.implode(',', $cafiles );
}
if ( $type == XMLSEC_PUB_PEM ){
$keytype = '--pubkey-pem:'.$keyname;
}
if ( $type == XMLSEC_ROOT_CA_DER ){
$keytype = '--trusted-der';
}
if ( $type == XMLSEC_ROOT_CA_PEM ){
$keytype = '--trusted-pem';
}
if ( $type == XMLSEC_UNTRAST_CA_DER ){
$keytype = '--untrusted-der' ;
}
if ( $type == XMLSEC_UNTRAST_CA_PEM ){
$keytype = '--untrusted-pem' ;
}
if ( $type == XMLSEC_PUB_CERT_PEM ){
$keytype = '--pubkey-cert-pem:'.$keyname;
}
if ( $type == XMLSEC_PUB_CERT_DER ){
$keytype = '--pubkey-cert-der:'.$keyname;
}
if ($keytype == ''){
$this->errorMsg = "undefine type of key";
return false;
}
$keysfile = '';
if (file_exists( $this->xmlkeyfilename )) {
$keysfile = ' --keys-file '.$this->xmlkeyfilename;
}
$cmd = "xmlsec1 keys $keytype $keyfilename"."$str_cafiles $keysfile $this->xmlkeyfilename 2>&1";
$this->cmd =$cmd;
// die($cmd);
$res = $this->exec( $cmd);
$this->errorMsg = $res ;
return;
}
/*****
* internal function
* set temp path for temp files
*****/
function setPath()
{
if ( $this->temp_path != '') return true;
$temp_path = getenv ( 'TMPDIR');
if ( !isset($temp_path) or $temp_path == '' )
$temp_path = getenv ( 'TMP');
if( isset($temp_path))
$this->temp_path = $temp_path;
else{
$this->errorMsg = "can't define temp path";
return false;
}
return true;
}
/*****
* internal function
* save dom xml into temp file
*****/
function saveXml( &$dom , $prefix )
{
$tmpfname = tempnam($this->temp_path , $prefix );
if (!is_writable($tmpfname)) {
$this->errorMsg = "the file $tmpfname d't writable";
return false;
}
$f = fopen( $tmpfname , 'w' );
if( !f){
$this->errorMsg = "can't create temp file ".$tmpfname;
return false;
}
$xmlstr = $dom->dump_mem(true);
// $doc->dump_file("/tmp/test.xml", false, true);
if (fwrite( $f, $xmlstr ) === FALSE) {
$this->errorMsg = "can't write template file";
fclose( $f);
return false;
}
fclose( $f);
return $tmpfname;
}
/*****
* internal function
* execute command
*****/
function exec( )
{
$p = popen( $this->cmd, 'r');
$read = fread($p , 4096);
pclose($p);
return $read;
}
/******
* encrypt data
* make template for encryption
*
* <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#">
* <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
* <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
* <KeyName />
* </KeyInfo>
* <CipherData>
* <CipherValue />
* </CipherData>
* </EncryptedData>
*
* @parametr $xmldoc - input dom document for encryption
* @parametr constant $type - the type of encryption algorithm
*
* @return true - success and for false see $this->errorMsg
*
******/
function encrypt( &$xmldoc , $type='')
{
$xmldoctpl = domxml_new_doc("1.0" );
// $root = $xmldoctpl->add_root("EncryptedData");
// $root = $xmldoctpl->create_element_ns ( "http://www.w3.org/2001/04/xmlenc#", 'EncryptedData' );
$root = $xmldoctpl->create_element ( 'EncryptedData' );
$root->set_attribute("xmlns", "http://www.w3.org/2001/04/xmlenc#");
$xmldoctpl->append_child($root);
// $xmlstr = $xmldoctpl->dump_mem(true);
$EncryptionMethod = $xmldoctpl->create_element ( 'EncryptionMethod' );
if ( $type==''){
$this->errorMsg = "the algorithm don't assigned";
return false;
}
$aldorithm = '';
if ( $type == XMLSEC_AES_128 )
$aldorithm = 'http://www.w3.org/2001/04/xmlenc#'. XMLSEC_AES_128;
if ( $type == XMLSEC_AES_256 )
$aldorithm = 'http://www.w3.org/2001/04/xmlenc#'.XMLSEC_AES_256;
if ( $type == XMLSEC_AES_192 )
$aldorithm = 'http://www.w3.org/2001/04/xmlenc#'.XMLSEC_AES_192;
if ( $type == XMLSEC_DES_128 )
$aldorithm = 'http://www.w3.org/2001/04/xmlenc#'.XMLSEC_DES_128;
if ( $type == XMLSEC_DES_256 )
$aldorithm = 'http://www.w3.org/2001/04/xmlenc#'.XMLSEC_DES_256;
if ( $type == XMLSEC_3DES )
$aldorithm = 'http://www.w3.org/2001/04/xmlenc#'.XMLSEC_3DES;
if ( $aldorithm == ''){
$this->errorMsg = "the algorithm don't assigned";
return false;
}
$EncryptionMethod->set_attribute("Algorithm", $aldorithm);
$root->append_child( $EncryptionMethod );
// $KeyInfo = $xmldoctpl->create_element_ns ( "http://www.w3.org/2000/09/xmldsig#", 'KeyInfo' ,'ds');
$KeyInfo = $xmldoctpl->create_element ( 'KeyInfo' );
$KeyInfo->set_attribute("xmlns", "http://www.w3.org/2000/09/xmldsig#");
$root->append_child( $KeyInfo );
$KeyName = $xmldoctpl->create_element( 'KeyName' );
$KeyInfo->append_child( $KeyName );
$CipherData = $xmldoctpl->create_element( "CipherData" );
$root->append_child( $CipherData );
$CipherValue = $xmldoctpl->create_element( "CipherValue" );
$CipherData->append_child( $CipherValue );
if ( !$this->setPath() ) return false;
$tmpfname = $this->saveXml( $xmldoctpl , "tmpl");
if( !$tmpfname ) return false;
if( isset($this->temp_path))
$outputName = tempnam($this->temp_path , "out");
else{
$this->errorMsg = "can't define temp path";
return false;
}
$inputName = $this->saveXml( $xmldoc , "in" );
if( !$inputName ) return false;
$keyfile = $this->xmlkeyfilename;
$cmd = "xmlsec1 encrypt --output $outputName --binary-data $inputName --keys-file $keyfile $tmpfname 2>&1";
$this->cmd = $cmd ; // for debugging
$this->exec();
$outDocument = file_get_contents($outputName);
if ( $this->unlink and !unlink($outputName)){
$this->errorMsg = "can't unlink data file ".$outputName;
return false;
}
if ( $this->unlink and !unlink($inputName)){
$this->errorMsg = "can't unlink data file ".$inputName;
return false;
}
if ( $this->unlink and !unlink($tmpfname)){
$this->errorMsg = "can't unlink data file ".$tmpfname;
return false;
}
if ( $outDocument == '' ){
$this->errorMsg = "the error in the crypto conversion ";
$this->errorMsg .= "\n ".$read;
return false;
}
// print_r( $retArr);
// print "\ncode:". $retCode;
return $outDocument;
}
/******
* decrypt data
*
* @parametr $xmldoc - input dom document for decryption
*
* @return xml string if success and for false see $this->errorMsg
*
******/
function decrypt( &$xmldoc )
{
if ( !$this->setPath() )
return false;
$inputName = $this->saveXml( $xmldoc , "in" );
if( !$inputName ) return false;
if( isset($this->temp_path))
$outputName = tempnam($this->temp_path , "out");
else{
$this->errorMsg = "can't define temp path";
return false;
}
$keyfile = $this->xmlkeyfilename;
$cmd = "xmlsec1>$outputName decrypt --keys-file $keyfile $inputName 2>&1";
// #xmlsec>output.txt decrypt --keys-file deskey.xml decrypt.xml
$this->cmd = $cmd;
$this->exec();
$outDocument = file_get_contents($outputName);
if ( $this->unlink and !unlink($outputName)){
$this->errorMsg = "can't unlink data file ".$outputName;
return false;
}
if ( $this->unlink and !unlink($inputName)){
$this->errorMsg = "can't unlink data file ".$inputName;
return false;
}
return $outDocument;
}
/****
* sign xml data
* make template for signature
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue></DigestValue>
</Reference>
</SignedInfo>
<SignatureValue/>
<KeyInfo>
<KeyName/>
</KeyInfo>
</Signature>
*
* @parametr $xmldoc - input dom document for encryption
* @parametr constant $type - the type of digital signature algorithm
* @parametr $keyInfo - array of keyinfo information , for example XMLSEC_X509DATA make tag <X509Data>
* @return true - success and for false see $this->errorMsg
*
**/
function sign($xmldoc, $type , $keyInfoArr =null)
{
$root = $xmldoc->document_element();
$rootDs = $xmldoc->create_element ( 'Signature' );
$rootDs->set_attribute("xmlns", "http://www.w3.org/2000/09/xmldsig#");
$root->append_child($rootDs);
$SignedInfo = $xmldoc->create_element ( 'SignedInfo' );
$rootDs->append_child($SignedInfo);
$SignatureValue = $xmldoc->create_element ( 'SignatureValue' );
$rootDs->append_child($SignatureValue);
$KeyInfo = $xmldoc->create_element ( 'KeyInfo' );
$rootDs->append_child($KeyInfo);
$KeyName = $xmldoc->create_element ( 'KeyName' );
$KeyInfo ->append_child($KeyName);
if ( $keyInfoArr != null && is_array($keyInfoArr) )
{
foreach( $keyInfoArr as $keyInfoEl => $keyInfoE2 ){
$el = '';
if ($keyInfoEl == XMLSEC_X509DATA )
$el = XMLSEC_X509DATA;
if ($el == '') continue;
$el2 = '';
if ($keyInfoE2 == XMLSEC_X509CERTIFICATE )
$el2 = XMLSEC_X509CERTIFICATE;
if ($el2 == '') continue;
$elDom = $xmldoc->create_element ( $el );
$el2Dom = $xmldoc->create_element ( $el2 );
$elDom ->append_child($el2Dom );
$KeyInfo ->append_child($elDom);
}
}
$CanonicalizationMethod = $xmldoc->create_element ( 'CanonicalizationMethod' );
$CanonicalizationMethod -> set_attribute("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
$SignedInfo -> append_child($CanonicalizationMethod);
$SignatureMethod = $xmldoc->create_element ( 'SignatureMethod' );
$algorithm = '';
if ($type == XMLSEC_DSA_SHA1 )
$algorithm = 'http://www.w3.org/2000/09/xmldsig#'.XMLSEC_DSA_SHA1;
if ($type == XMLSEC_RSA_SHA1 )
$algorithm = 'http://www.w3.org/2000/09/xmldsig#'.XMLSEC_RSA_SHA1;
if ( $algorithm == ''){
$this->errorMsg = "undefinit algorithm type: ".$type;
return false;
}
$SignatureMethod -> set_attribute("Algorithm", $algorithm );
$SignedInfo -> append_child($SignatureMethod );
$Reference = $xmldoc->create_element ( 'Reference' );
$Reference -> set_attribute("URI", "");
$SignedInfo -> append_child($Reference );
$Transforms = $xmldoc->create_element ( 'Transforms' );
$Reference -> append_child($Transforms);
$Transform = $xmldoc->create_element ( 'Transform' );
$Transform -> set_attribute("Algorithm", 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' );
$Transforms-> append_child($Transform);
$DigestMethod = $xmldoc->create_element ( 'DigestMethod' );
$DigestMethod -> set_attribute("Algorithm", 'http://www.w3.org/2000/09/xmldsig#sha1' );
$Reference -> append_child($DigestMethod );
$DigestValue = $xmldoc->create_element ( 'DigestValue' );
$Reference -> append_child($DigestValue );
if ( !$this->setPath() ) return false;
$tmplName = $this->saveXml( $xmldoc , "in" );
if( !$tmplName) return false;
if( isset($this->temp_path))
$outputName = tempnam($this->temp_path , "out");
else {
$this->errorMsg = "can't define temp path";
return false;
}
$keyfile = $this->xmlkeyfilename;
$cmd = "xmlsec1 sign --output $outputName --keys-file $keyfile $tmplName 2>&1";
$this->cmd = $cmd ; // for debugging
$this->exec();
$outDocument = file_get_contents($outputName);
if ( $this->unlink and !unlink($outputName)){
$this->errorMsg = "can't unlink data file ".$outputName;
return false;
}
if ( $this->unlink and !unlink($tmplName)){
$this->errorMsg = "can't unlink data file ".$tmplName;
return false;
}
if ( $outDocument == '' ){
$this->errorMsg = "the error in the crypto conversion ";
$this->errorMsg .= "\n ".$read;
return false;
}
return $outDocument;
} // end sign()
/******
* verify data
*
* @parametr $xmldoc - input signed dom document
* @parametr $cert - name of certificate file, default null string, d't using certificate
*
* @return verify result if success and for false see $this->errorMsg
*
******/
function verify( &$xmldoc , $cert ='')
{
if ( !$this->setPath() )
return false;
$inputName = $this->saveXml( $xmldoc , "in" );
if( !$inputName ) return false;
$keyfile = $this->xmlkeyfilename;
if ( $cert =='')
$cmd = "xmlsec1 verify --ignore-manifests --keys-file $keyfile $inputName 2>&1";
else
$cmd = "xmlsec1 verify --ignore-manifests --trusted-pem $cert $inputName 2>&1";
$this->cmd = $cmd;
$res = $this->exec();
if ( $this->unlink and !unlink($inputName)){
$this->errorMsg = "can't unlink data file ".$inputName;
return false;
}
if ( trim($res) =='OK') return true;
$this->errorMsg = $res;
return false;
} // end verify()
} // end class
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment