From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:31:47 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <class> <name>RSAKey</name> <environment>Security</environment> <super>Security.EncryptionKey</super> <private>false</private> none n <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSAKey
Abstract superclass for RSA keys. Instance Variables: n <LargeInteger> modulus, n=p*q, limits the size of the keys and the data to be encrypted
</comment> <class> <name>RSAPublicKey</name> <environment>Security</environment> <super>Security.RSAKey</super> <private>false</private> none e <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSAPublicKey
Public RSA key can be used for two purposes: it can encrypt data for later decryption with corresponding private key; and it can verify a signature for specific data which was previously generated using corresponding private key. Instance Variables: e <LargeInteger> public exponent, usually one of 3, 17, 65537
</comment> <class> <name>RSAPrivateKey</name> <environment>Security</environment> <super>Security.RSAKey</super> <private>false</private> none d e p q dP dQ qInv otherFactors <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSAPrivateKey
Private RSA keys can be used for two purposes: it can decrypt data previously encrypted with corresponding public key; and it can generate a signature for specific data which can later be verified using corresponding public key. For standardized export/import and secure storage of private keys use the PKCS8 package. Instance Variables: d <LargeInteger> private exponent, of large bit-length e <LargeInteger> public exponent, usually one of 3, 17, 65537 p <LargeInteger> the first factor of n q <LargeInteger> the second factor of n dP <LargeInteger> sub-exponent1, dP = d mod (p - 1) dQ <LargeInteger> sub-exponent2, dQ = d mod (q - 1) qInv <LargeInteger> CRT coefficient, qInv = 1/q mod p otherFactors <Array> additional factors of n, triplets including the factor, its sub-exponent and CRT coefficient
</comment> <class> <name>RSAError</name> <environment>Security</environment> <super>Core.Error</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSAError
Common superclass of errors generated by the RSA algorithm.
</comment> <class> <name>RSAMessageTooLong</name> <environment>Security</environment> <super>Security.RSAError</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSAMessageTooLong
This error signals that an attempted encryption or signing operation has failed because the date that was provided exceeds the range granted by the bit-length of the key used for the operation.
</comment> <class> <name>RSA</name> <environment>Security</environment> <super>Security.AsymmetricCipher</super> <private>false</private> none privateKey publicKey hash <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSA
This class implements the RSA algorithm as described in PKCS#1. It provides both encryption/decryption and signing/verification. Note that the same basic algorithm is used for both encryption and signing, therefore the result of signing is also a byte array, rather than a special "signature" object. Note that the PKCS#1 standard allows different hash functions to be used to compute a message digest for signing. Therefore one is expected to initialize the algorithm with one of #useSHA or #useMD5. Signing some data using MD5 digest would look as follows: signatureBytes := RSA new useMD5; privateKey: anRSAPrivateKey; sign: dataBytes. To verify the signature use following: isAuthenticBoolean := RSA new useMD5; publicKey: anRSAPublicKey; verify: signatureBytes of: dataBytes If the algorithm is not configured with specific hash function, then the argument of the #sign: message is expected to be an externally computed digest of the original message. The RSA algorithm will skip the hashing step in that case. Same applies to the verification operation. This feature is used by SSL protocol for example, which does the hashing in its own way. Encryption operations don't use hashing at all, so the #useMD5 or #useSHA are not necessary. Encryption is used in the usual way. encryptedBytes := RSA new publicKey: anRSAPublicKey; encrypt: plainBytes plainMessageBytes := RSA new privateKey: anRSAPrivateKey; decrypt: encryptedMessageBytes Instance Variables: privateKey <RSAPrivateKey> private key to use for decryption or signing publicKey <RSAPublicKey> public key to use for encryption or signature verification hash <Hash> a hash algorithm to use for signing
</comment> <class> <name>RSAKeyGenerator</name> <environment>Security</environment> <super>Security.KeyGenerator</super> <private>false</private> none mask q p n e d e <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSAKeyGenerator
The usual key sizes start from 512 bits and go up from there. The longer the key the more expensive are the cryptographic operations. Note however that 512 bit keys are considered weak given today's computers. The key generator employs a random number generator and an efficient primality test. The random generator is expected to generate large intergers of 160 bit size, a properly seeded DSSRandom is acceptable. The primality test is allowed to be probabilistic but the probability of false prime has to be less than 1/4^50. A properly initialized MillerRabin algorithm is acceptable. The probabilistic primality test can share its random generator with the key generator. The generator produces a couple of parameters, signalling various events along the way for observation. Finally it packages them up as RSAPublicKey and RSAPrivateKey, which are accessible using the #privateKey and #publicKey messages. To force the generator to produce a new pair of keys it has to be cleared using the #flush message. | generator public private | generator := RSAKeyGenerator keySize: 1024. private := generator privateKey. public := generator publicKey. generator flush Instance Variables: mask <LargeInteger> a mask used to enforce the size of the prime factors p and q q <LargeInteger> a large prime of bit-length aproximately keySize // 2 p <LargeInteger> a large prime of bit-length aproximately keySize // 2 n <LargeInteger> modulus, n=p*q, of bit-length keySize e <LargeInteger> public exponent, usually one of 3, 17, 65537 d <LargeInteger> private exponent, of large bit-length Class Instance Variables: e <Integer> default public exponent value (usually something like 3, 17, 65537)
</comment> <class> <name>RSANotImplemented</name> <environment>Security</environment> <super>Security.RSAError</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSANotImplemented
This error signals that the hashing algorithm used for the signing operation is not supported.
</comment> <class> <name>RSADecodingError</name> <environment>Security</environment> <super>Security.RSAError</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>RSA</package> </attributes> </class> <comment> Security.RSADecodingError
This error signals a problem decoding and encrypted message. It means that the decryption step failed usually due to corrupt cipher-text or due to a wrong key being used to decrypt.
</comment> <name>ASN1dictionaryForHashSigning</name> <environment>Security.RSA</environment> <private>false</private> <constant>false</constant> <category>constants</category> <attributes> <package>RSA</package> </attributes> <methods> Security.RSAKey <category>accessing</category>
n ^n
n: aValue n := aValue
</methods> <methods> Security.RSAKey class <category>deprecated</category>
generateKeyPairSize: numberOfBits "self generateKeyPairSize: 128" "From Bruce Schneier's Applied cryptography second edition page 467. the variables are the same as those in his book. Note depending on Key size this could take a while. Returns and array of size 2. First positon is the private key second position is the public key. Note this method generates the most secure kind of Keys that are also optimized for speed which is why the selection process takes so long" | n p q e d selectionBlock diffProduct temp dp dq euclid firstCoefficient privateKey | Transcript cr; show: 'Starting Key generation. if you chose to generate large keys > than 768 you may want to get some coffee.'; cr; show: 'Selecting First Random Prime'. q := 0. p := Integer generatePrimeBits: numberOfBits. selectionBlock := [ [ q := Integer generatePrimeBits: numberOfBits. p = q ] whileTrue ]. [ "Try values of e that will make our encryption go faster. First try 65537 as recommended by x.509 and PKCS #1 then try 3 or 17. If not then we should chose another value for q" Transcript cr; show: 'Selecting Second Random Prime'. selectionBlock value. n := p * q. diffProduct := p - 1 * (q - 1). Transcript cr; show: 'Verifying Key Pairs.'. #(65537 3 17) detect: [ :eValue | euclid := EuclidAlgorithm with: eValue with: diffProduct. euclid lehmer. euclid firstCoefficient > 10 ifTrue: [e := eValue. d := euclid firstCoefficient. true] ifFalse: [false]] ifNone: [e := nil. Transcript cr; show: 'Rejecting Second Prime']. e == nil ] whileTrue. Transcript cr; show: 'Calculating Chinese Remainder theorem values to speed up decryption'. p < q ifTrue: [ temp := q. q := p. p := temp]. dp := d \\ (p - 1). dq := d \\ (q - 1). euclid := EuclidAlgorithm with: q with: p. euclid lehmer. firstCoefficient := euclid firstCoefficient. privateKey := RSAPrivateKey d: d n: n. privateKey dp: dp. privateKey dq: dq. privateKey e: e. privateKey p: p. privateKey q: q. privateKey coefficient: firstCoefficient. Transcript cr; show: 'Finished'. ^Array with: privateKey with: (RSAPublicKey e: e n: n)
get1024BitKeys ^self generateKeyPairSize: 1024
get128BitKeys ^self generateKeyPairSize: 128
get2048BitKeys ^self generateKeyPairSize: 2048
get4096BitKeys ^self generateKeyPairSize: 4096
get768BitKeys ^self generateKeyPairSize: 768
</methods> <methods> Security.RSAPublicKey <category>accessing</category>
e ^e
e: aValue e := aValue
</methods> <methods> Security.RSAPublicKey <category>api</category>
exponentPowerOf: aSignatureInteger ^aSignatureInteger raisedTo: self e modulo: self n
</methods> <methods> Security.RSAPublicKey class <category>instance creation</category>
e: e n: n ^self new e: e; n: n; yourself
</methods> <methods> Security.RSAPrivateKey <category>accessing</category>
d ^d
d: aValue d := aValue
dp ^dP ifNil: [ dP := d \\ ( p - 1 ) ]
dp: exp dP := exp
dq ^dQ ifNil: [ dQ := d \\ (q - 1) ]
dq: exp dQ := exp
e ^e
e: aNumber e := aNumber
otherFactors ^otherFactors
otherFactors: aSequenceOfTriplets "aSequenceOfTriplets is a sequenceable collection of #(ri di ti), where ri is a factor, di is its exponent and ti is its CRT coefficient" otherFactors := aSequenceOfTriplets
p ^p
p: aNumber p := aNumber
q ^q
q: aNumber q := aNumber
qInv ^qInv ifNil: [ "qInv = 1 / q mod p" qInv := (EuclidAlgorithm with: q with: p) doBasicAlgorithm; firstCoefficient. qInv < 0 ifTrue: [ qInv := qInv \\ p ]. qInv ]
qInv: aNumber qInv := aNumber
</methods> <methods> Security.RSAPrivateKey <category>accessing-aliases</category>
coefficient ^self qInv
coefficient: aNumber self qInv: aNumber
exponent1 ^self dp
exponent1: aNumber self dp: aNumber
exponent2 ^self dq
exponent2: aNumber self dq: aNumber
</methods> <methods> Security.RSAPrivateKey <category>testing</category>
isSecondForm ^p notNil and: [ q notNil ]
</methods> <methods> Security.RSAPrivateKey <category>services</category>
exponentPowerOf: aLargeInteger ^( self isSecondForm and: [ "We do not support multi-factor (>2) keys yet" otherFactors isNil ] ) ifTrue: [ self fastExponentPowerOf: aLargeInteger ] ifFalse: [ self simpleExponentPowerOf: aLargeInteger ]
fastExponentPowerOf: aLargeInteger "If we know factorization of the modulus n, the power can be computed several times faster exploiting Chinese Remainder Theorem. The description of the computation itself is included in PKCS#1 version 2.0 and up. It's referred to as the computation using the 'second form' of a private key." | h m m1 m2 | "Split the the integer into the CRT factors and perform the exponentiation" m1 := (aLargeInteger \\ p) raisedTo: self dp modulo: p. m2 := (aLargeInteger \\ q) raisedTo: self dq modulo: q. "Convert the result back to an integer using Garner's formula" h := (((m1 - m2) \\ p) * self qInv) \\ p. m := h * q + m2. "This is what I believe the rest of the algorithm should look like for n with more than 2 factors. But I'm not sure, the description in PKCS#1 v2.1 is pretty poor in that regard. Given that such keys are rarely used it's safer to claim non-support for now. This is reflected in the isSecondForm test. otherFactors notNil ifTrue: [ | R prevRi | R := p. prevRi := q. otherFactors do: [ :triplet || ri di ti mi | ri := triplet first. di := triplet at: 2. ti := triplet last. mi := (aMessageInteger \\ ri) raisedTo: di modulo: ri. R := R * prevRi. h := (((mi - m) \\ ri) * ti) \\ ri. m := h * R + m. prevRi := ri ] ]." ^m
simpleExponentPowerOf: aLargeInteger ^aLargeInteger raisedTo: d modulo: n
</methods> <methods> Security.RSAPrivateKey class <category>instance creation</category>
d: d n: n ^self new d: d; n: n; yourself
</methods> <methods> Security.RSA <category>deprecated</category>
breakByteArray: aByteArray intoBlocksOf: blockSize | collectionSize collection start end | collectionSize := aByteArray size // blockSize. aByteArray size \\ blockSize = 0 ifFalse: [collectionSize := collectionSize + 1]. collection := OrderedCollection new: collectionSize. start := 0. 1 to: collectionSize do: [:index | | tmpString | end := start + blockSize. end > aByteArray size ifTrue: [end := aByteArray size]. tmpString := aByteArray copyFrom: start + 1 to: end. collection add: tmpString. start := end ]. ^collection
decrypt: aMessageByteArray withKey: anRSAPrivateKey "decrypt a block per PKCS 1 of the RSA spec this includes the padding rules specified in theat document and it is block type 1 in the SSL document finnaly I understood how they linked together" "this is the wrapper method so you can hand over an array that is the entire message ussually a byte array that isn't nessesarily sized for N so I break the lot up into sub arrays that are sized and then decrypt each of those and then hand back a byteArray the represents the orginal message. the decrypttion will automagically break it up into the maximum sized blocks and then decrypt the message and hand back a byteArray representing the original message." | nlength collection decipherCollection offset totalMessageSize messageArray | nlength := anRSAPrivateKey n digitLength. ^aMessageByteArray size > nlength ifFalse: ["if the incomingMessage is the right size the go ahead and encipher the array and return the result." self decryptRSAES: aMessageByteArray withKey: anRSAPrivateKey] ifTrue: ["then we need to break up the array into a collection of arrays that are no larger than blockSize and encrypt each one and assemble them back together into one ByteArray and return that" collection := self breakByteArray: aMessageByteArray intoBlocksOf: nlength. decipherCollection := OrderedCollection new: collection size. collection keysAndValuesDo: [ :index :eachArray | decipherCollection add: (self decryptRSAES: aMessageByteArray withKey: anRSAPrivateKey)]. offset := 1. totalMessageSize := decipherCollection inject: 0 into: [:subTotal :next | subTotal + next size]. messageArray := ByteArray new: totalMessageSize. offset := 1. decipherCollection keysAndValuesDo: [ :index :eachArray | messageArray replaceBytesFrom: offset to: offset + nlength - 1 with: (decipherCollection at: index) startingAt: 1. offset := offset + nlength]. messageArray]
decryptRSADP: aMessageInteger withKey: anRSAPrivateKey | h m1 m2 m mq mp p q cp cq | ^anRSAPrivateKey isSecondForm ifFalse: [ aMessageInteger raisedTo: anRSAPrivateKey d modulo: anRSAPrivateKey n ] ifTrue: ["this is an optimization for PrivateKey to do the decryption faster using the Chinese remainder theorem, to do this the privateKey needs the additional values for the second form of the key. the old ancient chinese secret laying beneath the sheets." p := anRSAPrivateKey p. q := anRSAPrivateKey q. cp := aMessageInteger \\ p. cq := aMessageInteger \\ q. mq := cq raisedTo: anRSAPrivateKey dq modulo: q . mp := cp raisedTo: anRSAPrivateKey dp modulo: p . m := ((((mp - mq) \\ p) * anRSAPrivateKey qInv) \\ p) * q + mq. m1 := aMessageInteger raisedTo: anRSAPrivateKey dp modulo: anRSAPrivateKey p . m2 := aMessageInteger raisedTo: anRSAPrivateKey dq modulo: anRSAPrivateKey q . h := anRSAPrivateKey qInv * (m1 - m2) \\ anRSAPrivateKey p. m := m2 + (h * anRSAPrivateKey q). m]
decryptRSAES: aByteArray withKey: anRSAPrivateKey "decrypt a block per PKCS 1 of the RSA spec this includes the padding rules specified in theat document and it is block type 1 in the SSL document finaly I understood how they linked together" | k cipherMessage encodeDecryptedMessage | cipherMessage := aByteArray asLargePositiveInteger. k := anRSAPrivateKey n digitLength. cipherMessage digitLength ~= k ifTrue: [self class decodingErrorSignal raise]. encodeDecryptedMessage := self decryptRSADP: cipherMessage withKey: anRSAPrivateKey. ^self decodeByEME_PKCS1_V1_5: encodeDecryptedMessage size: k - 1
encodeByEMSA_PKCS1_V1_5: aMessageArray size: emLen hash: aHashIdentifier "This is a slightly extended version of the PKCS#1 v1.5 'encoding method for signatures with appendix' (EMSA). The extension is to support RSA signing for SSL, where the digest is precomputed by the SSL rules and passed in as the aMessageArray value. This is indicated by the nil value of the aHashIdentifier in which case the hashing part of this method is skipped and only the type 1 padding is applied. Another slight diversion from the spec is that as an optimization this method returns a large integer instead of a byte array, since that's what the result gets always converted to anyway. So this method actually corresponds to the following in terms of PKCS#1: OS2IP(EMSA-PKCS1-v1_5-Encode(aMessageArray, emLen)" | T int | T := aHashIdentifier isNil ifTrue: [ aMessageArray ] ifFalse: [ | hashID hashClass | hashID := self class perform: aHashIdentifier. hashClass := self class hashClassFor: (hashID). (self class getTFor: hashID), (hashClass byteHash: aMessageArray) ]. (emLen < (T size + 10)) ifTrue: [ self class messageTooLongSignal raise]. "Now produce the EM = 01 || PS || 00 || T , where PS is emLen- || T || - 2 bytes of 16rFF. In BIG ENDIAN !" int := LargePositiveInteger basicNew: emLen. " Our LargeIntegers are little endian, so we have to reverse the bytes" T with: (T size to: 1 by: -1) do: [:each :index | int basicAt: index put: each]. int basicAt: T size + 1 put: 0. T size + 2 to: emLen - 1 do: [ :ind | int basicAt: ind put: 255]. int basicAt: emLen put: 1. ^int
encrypt: aMessageByteArray withKey: anRSAPublicKey "encrypt a block per PKCS 1 of the RSA spec this includes the padding rules specified in theat document and it is block type 1 in the SSL document finnaly I understood how they linked together" "this is the wrapper method so you can hand over an array that is the entire message ussually a byte array that isn't nessesarily sized for N so I break the lot up into sub arrays that are sized and then encrypt each of those and then hand back a byteArray the represents the cipher message the decrypttion will automagically break it up into the maximum sized blocks and then decrypt the message and hand back a byteArray." | nlength blockSize collection cipherCollection offset | nlength := anRSAPublicKey n digitLength. blockSize := nlength - 11. ^aMessageByteArray size > blockSize ifFalse: ["if the incomingMessage is the right size the go ahead and encipher the array and return the result." self encryptRSAES: aMessageByteArray withKey: anRSAPublicKey] ifTrue: ["then we need to break up the array into a collection of arrays that are no larger than blockSize and encrypt each one and assemble them back together into one ByteArray and return that" collection := self breakByteArray: aMessageByteArray intoBlocksOf: blockSize. cipherCollection := ByteArray new: collection size * nlength. offset := 1. collection keysAndValuesDo: [ :index :eachArray | cipherCollection replaceBytesFrom: offset to: offset + nlength - 1 with: (self encryptRSAES: aMessageByteArray withKey: anRSAPublicKey) startingAt: 1. offset := offset + nlength]. cipherCollection]
encryptRSAEP: alargeInt withKey: anRSAPublicKey ^alargeInt raisedTo: anRSAPublicKey e modulo: anRSAPublicKey n
encryptRSAES: aByteArray withKey: anRSAPublicKey "encrypt a block per PKCS 1 of the RSA spec this includes the padding rules specified in theat document and it is block type 1 in the SSL document fiannly I understood how they linked together" "First check to see if the message length is too long it has to be no greater than k - 11 where K is the length in bytes of n this allows us to pad ten bytes in the format we need. pass this to the baseRSAencryption that carries out the multiplication / encryption" | k encodeMessage ciperMessage | k := anRSAPublicKey n digitLength. encodeMessage := self encodeByEME_PKCS1_V1_5: aByteArray size: k - 1. ciperMessage := self encryptRSAEP: encodeMessage withKey: anRSAPublicKey. ^self i2osp: ciperMessage intendedSize: k
sign: aMessageArray withKey: anRSAPrivateKey andHash: hashIdentifier "The result is a signature of aMessageArray generated using anRSAPrivateKey and the hash function specified by the hashIndentifier (must be one of the identifiers defined on the class side). If the hashIdentifier is nil the aMessageArray is expected to be already a digest of apropriate size (generally up to the key modulus n byte length - 1) in which case the hashing step of the signing procedure is skipped." ^self signRSASSA: aMessageArray withKey: anRSAPrivateKey hash: hashIdentifier
signRSASP1: aMessageInteger withKey: anRSAPrivateKey aMessageInteger > (anRSAPrivateKey n - 1) ifTrue: [self class messageTooLongSignal raise]. ^aMessageInteger raisedTo: anRSAPrivateKey d modulo: anRSAPrivateKey n "^anRSAPrivateKey isSecondForm ifFalse: [aMessageInteger mod: anRSAPrivateKey n exp: anRSAPrivateKey d] ifTrue: [s1 := aMessageInteger mod: anRSAPrivateKey dp exp: anRSAPrivateKey p. s2 := aMessageInteger mod: anRSAPrivateKey dq exp: anRSAPrivateKey q. h := anRSAPrivateKey qInv * (s1 - s2). s := s2 + (h * anRSAPrivateKey q). s]"
signRSASSA: aMessageArray withKey: anRSAPrivateKey hash: hashIdentifier ^(self signRSASP1: ( self encodeByEMSA_PKCS1_V1_5: aMessageArray size: anRSAPrivateKey n digitLength - 1 hash: hashIdentifier) withKey: anRSAPrivateKey ) asBigEndianByteArray
verify: signatureArray isSignatureOf: aMessageArray withKey: anRSAPublicKey andHash: hashIdentifier "Denies or confirms that signatureArray is a signature of aMessageArray. The anRSAPublicKey and the hash function specified by the hashIndentifier (must be one of the identifiers defined on the class side) are inputs into the verification algorithm. If the hashIdentifier is nil the aMessageArray is expected to be already a digest of apropriate size (generally up to the key modulus n byte length - 1) in which case the hashing step of the signing procedure is skipped." ^self verifyRSASSA: signatureArray withKey: anRSAPublicKey withMessageArray: aMessageArray hash: hashIdentifier
verifyRSASSA: signatureArray withKey: anRSAPublicKey withMessageArray: aMessageArray hash: hashIdentifier | encodedMessage | encodedMessage := self encodeByEMSA_PKCS1_V1_5: aMessageArray size: anRSAPublicKey n digitLength - 1 hash: hashIdentifier. ^self verifyRSAVP1: signatureArray asLargePositiveInteger withKey: anRSAPublicKey withMessageArray: encodedMessage
verifyRSAVP1: aSignatureInteger withKey: anRSAPublicKey " asPer the spec we return the message representative as a large int" (aSignatureInteger > (anRSAPublicKey n - 1) and: [aSignatureInteger > 0]) ifTrue: [self class messageTooLongSignal raise]. ^aSignatureInteger raisedTo: anRSAPublicKey e modulo: anRSAPublicKey n
verifyRSAVP1: aSignatureInteger withKey: anRSAPublicKey withMessageArray: aMessageInteger "return true if the test passes false if it does not" ^aMessageInteger = (self verifyRSAVP1: aSignatureInteger withKey: anRSAPublicKey)
</methods> <methods> Security.RSA <category>accessing</category>
hashAlgorithm ^hash
hashAlgorithm: aHash hash := aHash
hashIdentifier: aHashIdentifier | hashClass | hashClass := self class hashClassFor: (self class perform: aHashIdentifier). self hashAlgorithm: hashClass new
privateKey ^privateKey
privateKey: anRSAPrivateKey privateKey := anRSAPrivateKey
publicKey ^publicKey
publicKey: anRSAPublicKey publicKey := anRSAPublicKey
useMD5 self hashAlgorithm: MD5 new
useSHA self hashAlgorithm: SHA new
</methods> <methods> Security.RSA <category>private</category>
getRandom | random generator| random := 0. generator := Random fastest seed: (Timestamp now asMilliseconds bitAnd: 65535) . "make sure we have only non zero values" [random = 0] whileTrue:[ random := (generator next * 256) floor]. ^random
</methods> <methods> Security.RSA <category>PKCS1 signing</category>
signRSASP1: aMessageInteger (privateKey n - 1 < aMessageInteger) ifTrue: [RSAMessageTooLong raiseSignal]. ^privateKey exponentPowerOf: aMessageInteger
signRSASSA: aMessageArray | k | privateKey isNil ifTrue: [ RSAError raiseSignal: ( #NeedPrivateKeyToSign << #security >> 'Private key needed for signing!') ]. k := privateKey n digitLength. ^self i2osp: (self signRSASP1: ( self encodeByEMSA_PKCS1_V1_5: aMessageArray size: k - 1)) intendedSize: k
verifyRSASSA: aSignatureArray of: aMessageArray | k | publicKey isNil ifTrue: [ RSAError raiseSignal: ( #NeedPublicKeyToVerify << #security >> 'Public key needed for signature verification!') ]. k := publicKey n digitLength. ^ (self encodeByEMSA_PKCS1_V1_5: aMessageArray size: k - 1 ) = (self verifyRSAVP1: (self os2ip: aSignatureArray))
verifyRSAVP1: aSignatureInteger (aSignatureInteger > (publicKey n - 1) and: [aSignatureInteger > 0]) ifTrue: [self class messageTooLongSignal raise]. ^publicKey exponentPowerOf: aSignatureInteger
</methods> <methods> Security.RSA <category>PKCS1 encryption</category>
decryptRSADP: aMessageInteger ^privateKey exponentPowerOf: aMessageInteger
decryptRSAES: aByteArray | k messageInteger encodedMessage | privateKey isNil ifTrue: [ RSAError raiseSignal: ( #NeedPrivateKeyToDecrypt << #security >> 'Private key needed for decryption!') ]. k := privateKey n digitLength. messageInteger := self os2ip: aByteArray. messageInteger digitLength ~= k ifTrue: [RSADecodingError raiseSignal]. encodedMessage := self decryptRSADP: messageInteger. ^self decodeByEME_PKCS1_V1_5: encodedMessage size: k - 1
encryptRSAEP: aMessageInteger ^publicKey exponentPowerOf: aMessageInteger
encryptRSAES: aByteArray | k encodedMessage encryptedMessage | publicKey isNil ifTrue: [ RSAError raiseSignal: ( #NeedPublicKeyToEncrypt << #security >> 'Public key needed for encryption!') ]. k := publicKey n digitLength. encodedMessage := self encodeByEME_PKCS1_V1_5: aByteArray size: k - 1. encryptedMessage := self encryptRSAEP: encodedMessage. ^self i2osp: encryptedMessage intendedSize: k
</methods> <methods> Security.RSA <category>PKCS1 conversions</category>
i2osp: largeInt intendedSize: size "This will convert the large integer to a byte array of specified size. In most cases this will be very simple since the result of the mod:exp: operation will be of the correct size. However occassionally it will be shorter, usually just a byte shorter. We have to check and if it isn't the right length we have to pad with leading zeros." ^largeInt asBigEndianByteArrayPaddedTo: size
os2ip: aByteArray ^aByteArray asLargePositiveInteger
</methods> <methods> Security.RSA <category>services</category>
decrypt: aByteArray "Decrypt aByteArray using the previously assigned private key." ^self decryptRSAES: aByteArray
encrypt: aByteArray "Encrypts aByteArray using the previously assigned public key" ^self encryptRSAES: aByteArray
sign: aMessageArray "Compute a signature of aMessageArray. If the 'hash' instance variable is nil then aMessageArray is expected to be already a digest of apropriate size (generally up to the key modulus n byte length - 1) in which case the hashing step of the signing procedure is skipped." ^self signRSASSA: aMessageArray
verify: aSignatureArray of: aMessageArray "Denies or confirms that aSignatureArray is a signature of aMessageArray. If the 'hash' instance variable is nil then aMessageArray is expected to be already a digest of apropriate size (generally up to the key modulus n byte length - 1) in which case the hashing step of the signing procedure is skipped." ^self verifyRSASSA: aSignatureArray of: aMessageArray
</methods> <methods> Security.RSA <category>PKCS1 encoding methods</category>
decodeByEME_PKCS1_V1_5: emInteger size: emLen "This is EME-PKCS1-v1_5-Decode as defined in section 9.1.2.2 of PKCS#1 v2.0. emInteger <LargePositiveInteger> EM, message to be decoded emLen <Integer> the length of the encoded message ^<ByteArray> M, the decoded message A slight diversion from the spec is that as an optimization we directly decode the large integer instead of converting it to a byte array first. So this method actually corresponds to the following in terms of PKCS#1: EME-PKCS1-v1_5-Decode(I2OSP(emInteger, emLen))" | padStart answer | emInteger digitLength < 10 ifTrue: [ RSADecodingError new messageText: (#RSAMessageTooShort << #security >> 'Encoded message is too short!'); parameter: emInteger; raise ]. (emInteger basicAt: emLen) = 2 ifFalse: [ RSADecodingError new messageText: (#RSAInvalidPaddingPrefix << #security >> 'Invalid padding prefix!'); raise ]. padStart := emLen - 1. [ padStart isZero or: [ (emInteger basicAt: padStart) isZero ] ] whileFalse: [ padStart := padStart - 1]. padStart isZero ifTrue: [ RSADecodingError new messageText: (#RSAMissingPaddingMarker << #security >> 'Missing padding marker!'); raise ]. padStart > (emLen - 9) ifTrue: [ RSADecodingError new messageText: (#RSAPaddingTooShort << #security >> 'Padding is too short!'); raise ]. answer := ByteArray new: padStart - 1. 1 to: padStart - 1 do: [ :i | answer at: i put: (emInteger basicAt: padStart - i)]. ^answer
encodeByEME_PKCS1_V1_5: mBytes size: emLen "This is EME-PKCS1-v1_5-Encode as defined in 9.1.2.1 of PKCS#1 v2.0. mBytes <ByteArray> M, message to be encoded, must be at most emLen-10 bytes long emLen <Integer> intended byte length of the encoded message ^<LargePositiveInteger> EM, the encoded message A slight diversion from the spec is that as an optimization this method returns a large integer instead of a byte array, since that's what the result gets always converted to anyway. So this method actually corresponds to the following in terms of PKCS#1: OS2IP(EME-PKCS1-v1_5-Encode(aByteArray, emLen)" | em padStart random | mBytes size > (emLen - 10) ifTrue: [ RSAMessageTooLong new messageText: (#RSAMessageTooLong << #security >> 'Message is too long!'); parameter: mBytes; raise ]. em := LargePositiveInteger basicNew: emLen. mBytes with: (mBytes size to: 1 by: -1) do: [:each :index | "convert to largeinteger reserving the pad in the beginning ot the large int" em basicAt: index put: each]. padStart := mBytes size + 1. em basicAt: padStart put: 0. "fill with some randoms they don't have to be cryptographic secure so our will do" random := Random fastest seed: (Time microsecondClock bitAnd: 65535). padStart + 1 to: emLen -1 do: [ :i | em basicAt: i put: (random next * 255) floor + 1 "must be non-zero" ]. em basicAt: emLen put: 2. ^em
encodeByEMSA_PKCS1_V1_5: aByteArray size: emLen "This is a slightly extended version of the PKCS#1 v1.5 'encoding method for signatures with appendix' (EMSA). The extension is to support RSA signing for SSL, where the digest is precomputed by the SSL rules and passed in as the aMessageArray value. This is indicated by the nil value of the 'hash' instance variable, in which case the hashing part of this method is skipped and only the type 1 padding is applied. Another slight diversion from the spec is that as an optimization this method returns a large integer instead of a byte array, since that's what the result gets always converted to anyway. So this method actually corresponds to the following in terms of PKCS#1: OS2IP(EMSA-PKCS1-v1_5-Encode(aMessageArray, emLen)" | T int | T := hash isNil ifTrue: [ aByteArray ] ifFalse: [ hash derEncodedDigestInfo, (hash byteHash: aByteArray) ]. (T size + 10 > emLen) ifTrue: [ RSAMessageTooLong new messageText: (#RSAMessageTooLong << #security >> 'Message is too long!'); parameter: aByteArray; raise ]. "Now produce the EM = 01 || PS || 00 || T , where PS is emLen- || T || - 2 bytes of 16rFF. In BIG ENDIAN !" int := LargePositiveInteger basicNew: emLen. " Our LargeIntegers are little endian, so we have to reverse the bytes" T with: (T size to: 1 by: -1) do: [:each :index | int basicAt: index put: each]. int basicAt: T size + 1 put: 0. T size + 2 to: emLen - 1 do: [ :ind | int basicAt: ind put: 255]. int basicAt: emLen put: 1. ^int
</methods> <methods> Security.RSA class <category>hash Identifiers</category>
md2 ^#md2
md2WithRSAEncryption ^#md2
md5 ^#md5
md5WithRSAEncryption ^#md5
sha1 ^#SHA1
sha1WithRSAEncryption ^#SHA1
sha256 ^#SHA256
sha256WithRSAEncryption ^#SHA256
sha384 ^#SHA384
sha384WithRSAEncryption ^#SHA384
sha512 ^#SHA512
sha512WithRSAEncryption ^#SHA512
</methods> <methods> Security.RSA class <category>constants</category>
decodingErrorSignal ^RSADecodingError
errorSignal ^RSAError
messageTooLongSignal ^RSAMessageTooLong
notImplementedSignal ^RSANotImplemented
</methods> <methods> Security.RSA class <category>accessing</category>
getTFor: aHashIdentifier ^ASN1dictionaryForHashSigning at: aHashIdentifier ifAbsent: [self errorSignal raise]
hashClassFor: anIdentifer anIdentifer = self md2 ifTrue: [self notImplementedSignal raise]. "will be at some point" anIdentifer = self sha256 ifTrue: [self notImplementedSignal raise]. "will be at some point" anIdentifer = self sha384 ifTrue: [self notImplementedSignal raise]. "will be at some point" anIdentifer = self sha512 ifTrue: [self notImplementedSignal raise]. "will be at some point" anIdentifer = self md5 ifTrue: [^MD5]. anIdentifer = self sha1 ifTrue: [^SHA]
</methods> <methods> Security.RSA class <category>initialize</category>
initialize self intializeASN1dictionaryForHashSigning.
intializeASN1dictionaryForHashSigning "Per PKCS1 these are the digestInfo asn1 encode values for md5 md2 and sha1" ASN1dictionaryForHashSigning := Dictionary new. ASN1dictionaryForHashSigning at: #MD2 put: #[48 32 48 12 6 8 42 134 72 134 247 13 2 2 5 0 4 16]. "Not sure where this comes from, but it's the lowercase one (#md5) that seems to be used by OpenSSL" ASN1dictionaryForHashSigning at: #MD5 put: #[48 32 0 12 6 8 42 134 72 134 247 13 2 5 5 0 4 16]. ASN1dictionaryForHashSigning at: #md2 put: #[48 32 48 12 6 8 42 134 72 134 247 13 2 2 5 0 4 16]. ASN1dictionaryForHashSigning at: #md5 put: #[48 32 48 12 6 8 42 134 72 134 247 13 2 5 5 0 4 16]. ASN1dictionaryForHashSigning at: #SHA1 put: #[48 33 48 9 6 5 43 14 3 2 26 5 0 4 20]. ASN1dictionaryForHashSigning at: #SHA256 put: #[48 49 48 13 6 9 96 134 72 1 101 3 4 2 1 5 0 4 32]. ASN1dictionaryForHashSigning at: #SHA384 put: #[48 65 48 13 6 9 96 134 72 1 101 3 4 2 2 5 0 4 48]. ASN1dictionaryForHashSigning at: #SHA512 put: #[48 81 48 13 6 9 96 134 72 1 101 3 4 2 3 5 0 4 64]
</methods> <methods> Security.RSAKeyGenerator <category>accessing</category>
d ^d
e ^e ifNil: [ e := self class e ]
e: anInteger " anInteger <Integer> default public exponent value (usually something like 3, 17, 65537)" e := anInteger
n ^n
p ^p
q ^q
</methods> <methods> Security.RSAKeyGenerator <category>initialization</category>
keySize: bitLength random: aRandom primalityTest: pTest " bitLength <SmallInteger> the bit-length of the modulus n aRandom <Random> a random seed generator producing large intergers pTest <PrimalityTest> a probabilistic primality test" super keySize: bitLength random: aRandom primalityTest: pTest. mask := (1 bitShift: bitLength // 2 - 1) bitOr: 1.
</methods> <methods> Security.RSAKeyGenerator <category>private</category>
generateKeys | product euclid | q ifNil: [ self generateQ ]. [ product := (p - 1) * (q - 1). euclid := EuclidAlgorithm with: self e with: product. euclid doBasicAlgorithm. euclid gcd = 1 ] whileFalse: [ self triggerEvent: #rejectingQ: with: q. p := q. self generateQ ]. d := euclid firstCoefficient \\ product. self triggerEvent: #generatedD:usingE: with: d with: e. n := p * q. self triggerEvent: #generatedN: with: n.
generateP [ p := self nextRandom bitOr: mask. self triggerEvent: #tryingP: with: p. primalityTest value: p ] whileFalse. self triggerEvent: #foundP: with: p
generateQ p ifNil: [ self generateP ]. [ q := self nextRandom bitOr: mask. self triggerEvent: #tryingQ: with: q. (primalityTest value: q) and: [ p ~= q ] ] whileFalse. self triggerEvent: #foundQ: with: q
nextRandom | bitSize x | "Let's assume that the bit sizes are byte aligned" bitSize := keySize // 16 "half of the modulus in bytes". x := random next. [ x basicSize < bitSize ] whileTrue: [ | chunk | chunk := random next. x := (x bitShift: (chunk basicSize * 8)) bitOr: chunk ]. ^(x basicSize > bitSize) ifTrue: [ x bitShift: (bitSize - x basicSize) * 8 ] ifFalse: [ x ]
</methods> <methods> Security.RSAKeyGenerator <category>services</category>
flush "Flush all the parameters to make sure next generation creates new ones" q := p := n := e := d := nil
privateKey n ifNil: [ self generateKeys ]. ^(RSAPrivateKey d: d n: n) e: self e; p: self p; q: self q; yourself
publicKey n ifNil: [ self generateKeys ]. ^RSAPublicKey e: e n: n
</methods> <methods> Security.RSAKeyGenerator class <category>accessing</category>
defaultE ^65537
e ^e ifNil: [^self defaultE]
e: anInteger " anInteger <Integer> default public exponent value (usually something like 3, 17, 65537) self e: 3 " e := anInteger
</methods> <methods> Security.RSAKeyGenerator class <category>events</category>
eventsTriggered ^#( tryingQ: foundQ: tryingP: foundP: rejectingQ: generatedD:usingE: generatedN:)
</methods> <methods> Security.SHA <category>rsa</category>
derEncodedDigestInfo ^#[48 33 48 9 6 5 43 14 3 2 26 5 0 4 20].
</methods> <methods> Security.MD5 <category>rsa</category>
derEncodedDigestInfo ^#[48 32 48 12 6 8 42 134 72 134 247 13 2 5 5 0 4 16]
</methods> <initialize> Security.RSA </initialize> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>AsymmetricCipher</name> <environment>Security</environment> <super>Core.Object</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>CiphersBase</package> </attributes> </class> <class> <name>SHA</name> <environment>Security</environment> <super>Security.SHAHash</super> <private>false</private> none t k <imports></imports> <category>Security-Hashes</category> <attributes> <package>SHA</package> </attributes> </class> <class> <name>MD5</name> <environment>Security</environment> <super>Security.Hash</super> <private>false</private> none a b c d aa bb cc dd f ff m <imports></imports> <category>Security-Hashes</category> <attributes> <package>MD5</package> </attributes> </class> <class> <name>EncryptionKey</name> <environment>Security</environment> <super>Core.Object</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>CiphersBase</package> </attributes> </class> <class> <name>Error</name> <environment>Core</environment> <super>Core.Exception</super> <private>false</private> none <imports></imports> <category>Kernel-Exception Handling</category> <attributes> <package>Kernel-Exception Handling</package> </attributes> </class> <class> <name>KeyGenerator</name> <environment>Security</environment> <super>Core.EventManager</super> <private>false</private> none keySize random primalityTest random <imports></imports> <category>Security-Ciphers</category> <attributes> <package>CiphersBase</package> </attributes> </class>